Skip to content

Commit 3560a55

Browse files
committed
[GR-68593] Include unresolved types in the method descriptor
PullRequest: graal/21924
2 parents d2a998b + 4e0e146 commit 3560a55

File tree

4 files changed

+73
-34
lines changed

4 files changed

+73
-34
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import java.util.function.Supplier;
5252
import java.util.stream.IntStream;
5353

54+
import com.oracle.svm.hosted.substitute.SubstitutionMethod;
5455
import org.graalvm.collections.EconomicMap;
5556
import org.graalvm.nativeimage.AnnotationAccess;
5657
import org.graalvm.nativeimage.ImageSingletons;
@@ -453,16 +454,32 @@ protected boolean delegateLoadType(PersistedAnalysisType.Reader typeData) {
453454
return false;
454455
}
455456

457+
/**
458+
* The {@link SubstitutionMethod} contains less information than the original
459+
* {@link ResolvedJavaMethod} and trying to access it can result in an exception.
460+
*/
461+
private static ResolvedJavaMethod getOriginalWrapped(AnalysisMethod method) {
462+
ResolvedJavaMethod wrapped = method.getWrapped();
463+
if (wrapped instanceof SubstitutionMethod subst) {
464+
return subst.getAnnotated();
465+
}
466+
return wrapped;
467+
}
468+
456469
/**
457470
* Load all lambda types of the given capturing class. Each method of the capturing class is
458471
* parsed (see {@link LambdaParser#createMethodGraph(ResolvedJavaMethod, OptionValues)}). The
459472
* lambda types can then be found in the constant nodes of the graphs.
460473
*/
461474
private void loadLambdaTypes(Class<?> capturingClass) {
462475
capturingClasses.computeIfAbsent(capturingClass, key -> {
476+
/*
477+
* Getting the original wrapped method is important to avoid getting exceptions that
478+
* would be ignored otherwise.
479+
*/
463480
LambdaParser.allExecutablesDeclaredInClass(universe.getBigbang().getMetaAccess().lookupJavaType(capturingClass))
464481
.filter(m -> m.getCode() != null)
465-
.forEach(m -> loadLambdaTypes(((AnalysisMethod) m).getWrapped(), universe.getBigbang()));
482+
.forEach(m -> loadLambdaTypes(getOriginalWrapped((AnalysisMethod) m), universe.getBigbang()));
466483
return true;
467484
});
468485
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerSnapshotUtil.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,13 @@ public String getMethodDescriptor(AnalysisMethod method) {
299299
if (originalMethod != null) {
300300
return addModuleName(originalMethod.toString(), moduleName);
301301
}
302-
return addModuleName(getQualifiedName(method), moduleName);
302+
/*
303+
* The wrapped qualified method is needed here as the AnalysisMethod replaces unresolved
304+
* parameter or return types with java.lang.Object, potentially causing method descriptor
305+
* duplication. The wrapped method signature preserves the original type information,
306+
* preventing this issue.
307+
*/
308+
return addModuleName(getWrappedQualifiedName(method), moduleName);
303309
}
304310

305311
/*
@@ -326,6 +332,10 @@ private static String getQualifiedName(AnalysisMethod method) {
326332
return method.getSignature().getReturnType().toJavaName(true) + " " + method.getQualifiedName();
327333
}
328334

335+
private static String getWrappedQualifiedName(AnalysisMethod method) {
336+
return method.wrapped.format("%R %H.%n(%P)");
337+
}
338+
329339
public static void forcePersistConstant(ImageHeapConstant imageHeapConstant) {
330340
AnalysisUniverse universe = imageHeapConstant.getType().getUniverse();
331341
universe.getHeapScanner().markReachable(imageHeapConstant, ObjectScanner.OtherReason.PERSISTED);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,13 @@ public class SVMImageLayerWriter extends ImageLayerWriter {
204204
private final MessageBuilder snapshotFileBuilder = new MessageBuilder();
205205
private final SharedLayerSnapshot.Builder snapshotBuilder = this.snapshotFileBuilder.initRoot(SharedLayerSnapshot.factory);
206206
private Map<ImageHeapConstant, ConstantParent> constantsMap;
207-
private final Map<String, MethodGraphsInfo> methodsMap = new ConcurrentHashMap<>();
207+
private final Map<AnalysisMethod, MethodGraphsInfo> methodsMap = new ConcurrentHashMap<>();
208+
/**
209+
* This map is only used for validation, to ensure that all method descriptors are unique. A
210+
* duplicate method descriptor would cause methods to be incorrectly matched across layers,
211+
* which is really hard to debug and can have unexpected consequences.
212+
*/
213+
private final Map<String, AnalysisMethod> methodDescriptors = new HashMap<>();
208214
private final Map<AnalysisMethod, Set<AnalysisMethod>> polymorphicSignatureCallers = new ConcurrentHashMap<>();
209215
private final GraphsOutput graphsOutput;
210216
private final boolean useSharedLayerGraphs;
@@ -231,13 +237,13 @@ private record MethodGraphsInfo(String analysisGraphLocation, boolean analysisGr
231237

232238
static final MethodGraphsInfo NO_GRAPHS = new MethodGraphsInfo(null, false, null);
233239

234-
MethodGraphsInfo withAnalysisGraph(String location, boolean isIntrinsic) {
235-
assert analysisGraphLocation == null && !analysisGraphIsIntrinsic;
240+
MethodGraphsInfo withAnalysisGraph(AnalysisMethod method, String location, boolean isIntrinsic) {
241+
assert analysisGraphLocation == null && !analysisGraphIsIntrinsic : "Only one analysis graph can be persisted for a given method: " + method;
236242
return new MethodGraphsInfo(location, isIntrinsic, strengthenedGraphLocation);
237243
}
238244

239-
MethodGraphsInfo withStrengthenedGraph(String location) {
240-
assert strengthenedGraphLocation == null;
245+
MethodGraphsInfo withStrengthenedGraph(AnalysisMethod method, String location) {
246+
assert strengthenedGraphLocation == null : "Only one strengthened graph can be persisted for a given method: " + method;
241247
return new MethodGraphsInfo(analysisGraphLocation, analysisGraphIsIntrinsic, location);
242248
}
243249
}
@@ -379,6 +385,7 @@ public void persistAnalysisInfo() {
379385
AnalysisMethod[] methodsToPersist = aUniverse.getMethods().stream().filter(AnalysisMethod::isTrackedAcrossLayers).sorted(Comparator.comparingInt(AnalysisMethod::getId))
380386
.toArray(AnalysisMethod[]::new);
381387
initSortedArray(snapshotBuilder::initMethods, methodsToPersist, this::persistMethod);
388+
methodDescriptors.clear();
382389

383390
AnalysisField[] fieldsToPersist = aUniverse.getFields().stream().filter(AnalysisField::isTrackedAcrossLayers).sorted(Comparator.comparingInt(AnalysisField::getId))
384391
.toArray(AnalysisField[]::new);
@@ -582,18 +589,20 @@ private static void handleNameConflict(String message) {
582589

583590
private void persistMethod(AnalysisMethod method, Supplier<PersistedAnalysisMethod.Builder> builderSupplier) {
584591
PersistedAnalysisMethod.Builder builder = builderSupplier.get();
585-
MethodGraphsInfo graphsInfo = methodsMap.putIfAbsent(imageLayerSnapshotUtil.getMethodDescriptor(method), MethodGraphsInfo.NO_GRAPHS);
592+
MethodGraphsInfo graphsInfo = methodsMap.putIfAbsent(method, MethodGraphsInfo.NO_GRAPHS);
586593
Executable executable = method.getJavaMethod();
587594

588-
if (builder.getId() != 0) {
589-
throw GraalError.shouldNotReachHere("The method descriptor should be unique, but " + imageLayerSnapshotUtil.getMethodDescriptor(method) + " got added twice.");
590-
}
591595
if (executable != null) {
592596
initStringList(builder::initArgumentClassNames, Arrays.stream(executable.getParameterTypes()).map(Class::getName));
593597
builder.setClassName(executable.getDeclaringClass().getName());
594598
}
595599

596-
builder.setDescriptor(imageLayerSnapshotUtil.getMethodDescriptor(method));
600+
String methodDescriptor = imageLayerSnapshotUtil.getMethodDescriptor(method);
601+
if (methodDescriptors.put(methodDescriptor, method) != null) {
602+
throw GraalError.shouldNotReachHere("The method descriptor should be unique, but %s got added twice.\nThe first method is %s and the second is %s."
603+
.formatted(methodDescriptor, methodDescriptors.get(methodDescriptor), method));
604+
}
605+
builder.setDescriptor(methodDescriptor);
597606
builder.setDeclaringTypeId(method.getDeclaringClass().getId());
598607
initInts(builder::initArgumentTypeIds, method.getSignature().toParameterList(null).stream().mapToInt(AnalysisType::getId));
599608
builder.setId(method.getId());
@@ -1053,18 +1062,19 @@ private static AnalysisMethod getRelocatableConstantMethod(MethodRef methodRef)
10531062

10541063
@Override
10551064
public void persistAnalysisParsedGraph(AnalysisMethod method, AnalysisParsedGraph analysisParsedGraph) {
1056-
String name = imageLayerSnapshotUtil.getMethodDescriptor(method);
1057-
MethodGraphsInfo graphsInfo = methodsMap.get(name);
1058-
if (graphsInfo == null || graphsInfo.analysisGraphLocation == null) {
1065+
/*
1066+
* A copy of the encoded graph is needed here because the nodeStartOffsets can be
1067+
* concurrently updated otherwise, which causes the ObjectCopier to fail.
1068+
*/
1069+
String location = persistGraph(method, new EncodedGraph(analysisParsedGraph.getEncodedGraph()));
1070+
if (location != null) {
10591071
/*
1060-
* A copy of the encoded graph is needed here because the nodeStartOffsets can be
1061-
* concurrently updated otherwise, which causes the ObjectCopier to fail.
1072+
* This method should only be called once for each method. This check is performed by
1073+
* withAnalysisGraph as it will throw if the MethodGraphsInfo already has an analysis
1074+
* graph.
10621075
*/
1063-
String location = persistGraph(method, new EncodedGraph(analysisParsedGraph.getEncodedGraph()));
1064-
if (location != null) {
1065-
methodsMap.compute(name, (n, mgi) -> (mgi != null ? mgi : MethodGraphsInfo.NO_GRAPHS)
1066-
.withAnalysisGraph(location, analysisParsedGraph.isIntrinsic()));
1067-
}
1076+
methodsMap.compute(method, (n, mgi) -> (mgi != null ? mgi : MethodGraphsInfo.NO_GRAPHS)
1077+
.withAnalysisGraph(method, location, analysisParsedGraph.isIntrinsic()));
10681078
}
10691079
}
10701080

@@ -1073,14 +1083,14 @@ public void persistMethodStrengthenedGraph(AnalysisMethod method) {
10731083
return;
10741084
}
10751085

1076-
String name = imageLayerSnapshotUtil.getMethodDescriptor(method);
1077-
MethodGraphsInfo graphsInfo = methodsMap.get(name);
1078-
1079-
if (graphsInfo == null || graphsInfo.strengthenedGraphLocation == null) {
1080-
EncodedGraph analyzedGraph = method.getAnalyzedGraph();
1081-
String location = persistGraph(method, analyzedGraph);
1082-
methodsMap.compute(name, (n, mgi) -> (mgi != null ? mgi : MethodGraphsInfo.NO_GRAPHS).withStrengthenedGraph(location));
1083-
}
1086+
EncodedGraph analyzedGraph = method.getAnalyzedGraph();
1087+
String location = persistGraph(method, analyzedGraph);
1088+
/*
1089+
* This method should only be called once for each method. This check is performed by
1090+
* withStrengthenedGraph as it will throw if the MethodGraphsInfo already has a strengthened
1091+
* graph.
1092+
*/
1093+
methodsMap.compute(method, (n, mgi) -> (mgi != null ? mgi : MethodGraphsInfo.NO_GRAPHS).withStrengthenedGraph(method, location));
10841094
}
10851095

10861096
private String persistGraph(AnalysisMethod method, EncodedGraph analyzedGraph) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,12 @@ public Object transform(Object receiver, Object originalValue) {
259259
for (var entry : cache.entrySet()) {
260260
SoftReference<Object> value = entry.getValue();
261261
Object object = value.get();
262-
MethodHandle constructor = ReflectionUtil.invokeMethod(constructorGetter, object);
263-
MethodHandle concatenator = ReflectionUtil.invokeMethod(concatenatorGetter, object);
264-
if (constructor != null && concatenator != null && heapScanner.isObjectReachable(constructor) && heapScanner.isObjectReachable(concatenator)) {
265-
result.put(entry.getKey(), value);
262+
if (object != null) {
263+
MethodHandle constructor = ReflectionUtil.invokeMethod(constructorGetter, object);
264+
MethodHandle concatenator = ReflectionUtil.invokeMethod(concatenatorGetter, object);
265+
if (constructor != null && concatenator != null && heapScanner.isObjectReachable(constructor) && heapScanner.isObjectReachable(concatenator)) {
266+
result.put(entry.getKey(), value);
267+
}
266268
}
267269
}
268270

0 commit comments

Comments
 (0)