@@ -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,16 @@ 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+ newResolvedMethod = getUniverse ().getBigbang ().fallbackResolveConcreteMethod (this , (AnalysisMethod ) method );
1142+ }
11321143 }
11331144
11341145 } catch (UnsupportedFeatureException e ) {
@@ -1140,9 +1151,32 @@ public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJ
11401151 if (newResolvedMethod == null ) {
11411152 newResolvedMethod = NULL_METHOD ;
11421153 }
1143- Object oldResolvedMethod = resolvedMethods .putIfAbsent (method , newResolvedMethod );
1144- resolvedMethod = oldResolvedMethod != null ? oldResolvedMethod : newResolvedMethod ;
1154+
1155+ if (computingFallback ) {
1156+ /*
1157+ * If computingFallback is set, it is this thread's responsibility to install the
1158+ * result and override the placeholder put in the map.
1159+ */
1160+ var finalResolvedMethod = newResolvedMethod ;
1161+ resolvedMethods .compute (method , (k , v ) -> {
1162+ assert v == COMPUTING_FALLBACK_RESOLUTION : v ;
1163+ return finalResolvedMethod ;
1164+ });
1165+ resolvedMethod = newResolvedMethod ;
1166+ } else {
1167+ Object oldResolvedMethod = resolvedMethods .putIfAbsent (method , newResolvedMethod );
1168+ resolvedMethod = oldResolvedMethod != null ? oldResolvedMethod : newResolvedMethod ;
1169+ }
1170+ }
1171+
1172+ /*
1173+ * Wait for fallback resolution computation to complete on another thread (if needed).
1174+ */
1175+ while (resolvedMethod == COMPUTING_FALLBACK_RESOLUTION ) {
1176+ Thread .onSpinWait ();
1177+ resolvedMethod = resolvedMethods .get (method );
11451178 }
1179+ assert resolvedMethod != null ;
11461180 return resolvedMethod == NULL_METHOD ? null : (AnalysisMethod ) resolvedMethod ;
11471181 }
11481182
0 commit comments