@@ -161,6 +161,7 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav
161161 * implement a method.
162162 */
163163 private static final Object NULL_METHOD = new Object ();
164+ private static final Object COMPUTING_FALLBACK_RESOLUTION = new Object ();
164165
165166 private final AnalysisType componentType ;
166167 private final AnalysisType elementalType ;
@@ -1117,6 +1118,7 @@ public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJ
11171118 if (resolvedMethod == null ) {
11181119 ResolvedJavaMethod originalMethod = OriginalMethodProvider .getOriginalMethod (method );
11191120 Object newResolvedMethod = null ;
1121+ boolean computingFallback = false ;
11201122 if (originalMethod != null ) {
11211123 /*
11221124 * We do not want any access checks to be performed, so we use the method's
@@ -1128,7 +1130,27 @@ public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJ
11281130 var concreteMethod = originalMethod instanceof BaseLayerMethod ? originalMethod : wrapped .resolveConcreteMethod (originalMethod , originalCallerType );
11291131 newResolvedMethod = universe .lookup (concreteMethod );
11301132 if (newResolvedMethod == null ) {
1131- newResolvedMethod = getUniverse ().getBigbang ().fallbackResolveConcreteMethod (this , (AnalysisMethod ) method );
1133+ /*
1134+ * Note we cannot directly use computeIfAbsent; calling
1135+ * fallbackResolveConcreteMethod will potentially cause other entries to be
1136+ * added to resolvedMethods, resulting in illegal recursive updates.
1137+ */
1138+ Object oldResolvedMethod = resolvedMethods .putIfAbsent (method , COMPUTING_FALLBACK_RESOLUTION );
1139+ if (oldResolvedMethod == null ) {
1140+ computingFallback = true ;
1141+ try {
1142+ newResolvedMethod = getUniverse ().getBigbang ().fallbackResolveConcreteMethod (this , (AnalysisMethod ) method );
1143+ } catch (Throwable t ) {
1144+ /* Finalize result if an error occurs. */
1145+ resolvedMethods .compute (method , (k , v ) -> {
1146+ assert v == COMPUTING_FALLBACK_RESOLUTION : v ;
1147+ return NULL_METHOD ;
1148+ });
1149+ computingFallback = false ;
1150+
1151+ throw t ;
1152+ }
1153+ }
11321154 }
11331155
11341156 } catch (UnsupportedFeatureException e ) {
@@ -1140,9 +1162,32 @@ public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJ
11401162 if (newResolvedMethod == null ) {
11411163 newResolvedMethod = NULL_METHOD ;
11421164 }
1143- Object oldResolvedMethod = resolvedMethods .putIfAbsent (method , newResolvedMethod );
1144- resolvedMethod = oldResolvedMethod != null ? oldResolvedMethod : newResolvedMethod ;
1165+
1166+ if (computingFallback ) {
1167+ /*
1168+ * If computingFallback is set, it is this thread's responsibility to install the
1169+ * result and override the placeholder put in the map.
1170+ */
1171+ var finalResolvedMethod = newResolvedMethod ;
1172+ resolvedMethods .compute (method , (k , v ) -> {
1173+ assert v == COMPUTING_FALLBACK_RESOLUTION : v ;
1174+ return finalResolvedMethod ;
1175+ });
1176+ resolvedMethod = newResolvedMethod ;
1177+ } else {
1178+ Object oldResolvedMethod = resolvedMethods .putIfAbsent (method , newResolvedMethod );
1179+ resolvedMethod = oldResolvedMethod != null ? oldResolvedMethod : newResolvedMethod ;
1180+ }
1181+ }
1182+
1183+ /*
1184+ * Wait for fallback resolution computation to complete on another thread (if needed).
1185+ */
1186+ while (resolvedMethod == COMPUTING_FALLBACK_RESOLUTION ) {
1187+ Thread .onSpinWait ();
1188+ resolvedMethod = resolvedMethods .get (method );
11451189 }
1190+ assert resolvedMethod != null ;
11461191 return resolvedMethod == NULL_METHOD ? null : (AnalysisMethod ) resolvedMethod ;
11471192 }
11481193
0 commit comments