@@ -161,6 +161,7 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav
161
161
* implement a method.
162
162
*/
163
163
private static final Object NULL_METHOD = new Object ();
164
+ private static final Object COMPUTING_FALLBACK_RESOLUTION = new Object ();
164
165
165
166
private final AnalysisType componentType ;
166
167
private final AnalysisType elementalType ;
@@ -1117,6 +1118,7 @@ public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJ
1117
1118
if (resolvedMethod == null ) {
1118
1119
ResolvedJavaMethod originalMethod = OriginalMethodProvider .getOriginalMethod (method );
1119
1120
Object newResolvedMethod = null ;
1121
+ boolean computingFallback = false ;
1120
1122
if (originalMethod != null ) {
1121
1123
/*
1122
1124
* 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
1128
1130
var concreteMethod = originalMethod instanceof BaseLayerMethod ? originalMethod : wrapped .resolveConcreteMethod (originalMethod , originalCallerType );
1129
1131
newResolvedMethod = universe .lookup (concreteMethod );
1130
1132
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
+ }
1132
1154
}
1133
1155
1134
1156
} catch (UnsupportedFeatureException e ) {
@@ -1140,9 +1162,32 @@ public AnalysisMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJ
1140
1162
if (newResolvedMethod == null ) {
1141
1163
newResolvedMethod = NULL_METHOD ;
1142
1164
}
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 );
1145
1189
}
1190
+ assert resolvedMethod != null ;
1146
1191
return resolvedMethod == NULL_METHOD ? null : (AnalysisMethod ) resolvedMethod ;
1147
1192
}
1148
1193
0 commit comments