From e18bf16c776cf58d2ff99d22540d40e2e89c72f9 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Fri, 15 Nov 2024 15:21:54 +0100 Subject: [PATCH] Improve loading of base layer elements --- .../pointsto/standalone/PointsToAnalyzer.java | 6 +- .../graal/pointsto/heap/ImageLayerLoader.java | 153 ++++++++++++------ .../pointsto/heap/ImageLayerSnapshotUtil.java | 15 ++ .../graal/pointsto/heap/ImageLayerWriter.java | 59 +++++-- .../graal/pointsto/meta/BaseLayerField.java | 14 +- .../graal/pointsto/meta/BaseLayerMethod.java | 2 +- .../graal/pointsto/meta/BaseLayerType.java | 16 +- .../annotation/AnnotationArrayValue.java | 9 +- .../annotation/AnnotationMemberValue.java | 18 ++- .../hosted/annotation/AnnotationMetadata.java | 2 +- .../hosted/annotation/AnnotationValue.java | 8 +- .../code/CEntryPointCallStubMethod.java | 4 + .../oracle/svm/hosted/code/FactoryMethod.java | 4 + .../IsStaticFinalFieldInitializedNode.java | 11 +- .../MarkStaticFinalFieldInitializedNode.java | 11 +- .../StaticFinalFieldFoldingFeature.java | 41 ++++- .../svm/hosted/heap/SVMImageLayerLoader.java | 40 ++++- .../heap/SVMImageLayerLoaderHelper.java | 87 +++++++++- .../svm/hosted/heap/SVMImageLayerWriter.java | 74 ++++++--- .../heap/SVMImageLayerWriterHelper.java | 43 +++++ .../svm/hosted/jni/JNIAccessFeature.java | 14 +- .../jni/JNIJavaCallVariantWrapperMethod.java | 11 +- .../ReflectionExpandSignatureMethod.java | 10 +- .../svm/hosted/reflect/ReflectionFeature.java | 2 +- 24 files changed, 508 insertions(+), 146 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java index 53f48d3a8a0c..18de8ba7cebc 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/PointsToAnalyzer.java @@ -49,6 +49,7 @@ import com.oracle.graal.pointsto.heap.HostedValuesProvider; import com.oracle.graal.pointsto.heap.ImageHeap; import com.oracle.graal.pointsto.heap.ImageLayerLoader; +import com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil; import com.oracle.graal.pointsto.heap.ImageLayerWriter; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; @@ -162,14 +163,17 @@ private PointsToAnalyzer(String mainEntryClass, OptionValues options) { aUniverse.setBigBang(bigbang); ImageHeap heap = new ImageHeap(); HostedValuesProvider hostedValuesProvider = new HostedValuesProvider(aMetaAccess, aUniverse); + ImageLayerSnapshotUtil imageLayerSnapshotUtil = new ImageLayerSnapshotUtil(); ImageLayerLoader imageLayerLoader = new ImageLayerLoader(); + imageLayerLoader.setImageLayerSnapshotUtil(imageLayerSnapshotUtil); imageLayerLoader.setUniverse(aUniverse); aUniverse.setImageLayerLoader(imageLayerLoader); StandaloneImageHeapScanner heapScanner = new StandaloneImageHeapScanner(bigbang, heap, aMetaAccess, snippetReflection, aConstantReflection, new AnalysisObjectScanningObserver(bigbang), analysisClassLoader, hostedValuesProvider); aUniverse.setHeapScanner(heapScanner); imageLayerLoader.executeHeapScannerTasks(); - ImageLayerWriter imageLayerWriter = new ImageLayerWriter(); + ImageLayerWriter imageLayerWriter = new ImageLayerWriter(true); + imageLayerWriter.setImageLayerSnapshotUtil(imageLayerSnapshotUtil); imageLayerWriter.setImageHeap(heap); HeapSnapshotVerifier heapVerifier = new StandaloneHeapSnapshotVerifier(bigbang, heap, heapScanner); aUniverse.setHeapVerifier(heapVerifier); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java index 26f0f67a7b17..ab256b1e13d2 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java @@ -53,6 +53,8 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IDENTITY_HASH_CODE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_HEAP_SIZE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANCE_FIELDS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANCE_FIELDS_WITH_SUPER_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANCE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTERFACES_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTRINSIC_TAG; @@ -69,6 +71,7 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INVOKED; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_LINKED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_REACHABLE; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_STATIC_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_SYNTHETIC_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_UNSAFE_ALLOCATED; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_VAR_ARGS_TAG; @@ -88,10 +91,9 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PARENT_CONSTANT_ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PARENT_CONSTANT_INDEX_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PERSISTED; -import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.POSITION_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PRIMITIVE_ARRAY_TAG; -import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RETURN_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RELOCATED_CONSTANT_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RETURN_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SIMULATED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SOURCE_FILE_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_OBJECT_FIELDS_TAG; @@ -112,6 +114,7 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -148,6 +151,7 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MethodHandleAccessProvider.IntrinsicMethod; import jdk.vm.ci.meta.PrimitiveConstant; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; /** @@ -312,6 +316,7 @@ public class ImageLayerLoader { private final Map baseLayerTypes = new ConcurrentHashMap<>(); private final Map typeToHubIdentityHashCode = new ConcurrentHashMap<>(); private final Map baseLayerMethods = new ConcurrentHashMap<>(); + private final Map baseLayerFields = new ConcurrentHashMap<>(); /** Map from the type id to its identifier in the jsonMap. */ protected final Map typeIdToIdentifier = new HashMap<>(); @@ -481,6 +486,12 @@ private void loadType(EconomicMap typeData) { String name = get(typeData, CLASS_JAVA_NAME_TAG); Class clazz = lookupBaseLayerTypeInHostVM(name); + Integer superClassTid = get(typeData, SUPER_CLASS_TAG); + ResolvedJavaType superClass = getResolvedJavaType(superClassTid); + + List interfacesIds = get(typeData, INTERFACES_TAG); + ResolvedJavaType[] interfaces = interfacesIds.stream().map(this::getResolvedJavaType).toList().toArray(new ResolvedJavaType[0]); + if (clazz != null) { /* * When looking up the class by name, the host VM will create the corresponding @@ -497,39 +508,58 @@ private void loadType(EconomicMap typeData) { * If the type cannot be looked up by name, an incomplete AnalysisType, which uses a * BaseLayerType in its wrapped field, has to be created */ - baseLayerTypes.computeIfAbsent(tid, (typeId) -> { - String className = get(typeData, CLASS_NAME_TAG); - int modifiers = get(typeData, MODIFIERS_TAG); - boolean isInterface = get(typeData, IS_INTERFACE_TAG); - boolean isEnum = get(typeData, IS_ENUM_TAG); - boolean isInitialized = get(typeData, IS_INITIALIZED_TAG); - boolean initializedAtBuildTime = get(typeData, IS_INITIALIZED_AT_BUILD_TIME_TAG); - boolean isLinked = get(typeData, IS_LINKED_TAG); - String sourceFileName = get(typeData, SOURCE_FILE_NAME_TAG); + BaseLayerType baseLayerType = getBaseLayerType(typeData, tid, superClass, interfaces); - Integer enclosingTid = get(typeData, ENCLOSING_TYPE_TAG); - ResolvedJavaType enclosingType = getResolvedJavaType(enclosingTid); + List instanceFieldIds = get(typeData, INSTANCE_FIELDS_TAG); + ResolvedJavaField[] instanceFields = instanceFieldIds.stream().map(this::getBaseLayerField).toList().toArray(new ResolvedJavaField[0]); + baseLayerType.setInstanceFields(instanceFields); - Integer componentTid = get(typeData, COMPONENT_TYPE_TAG); - ResolvedJavaType componentType = getResolvedJavaType(componentTid); + List instanceFieldWithSuperIds = get(typeData, INSTANCE_FIELDS_WITH_SUPER_TAG); + ResolvedJavaField[] instanceFieldsWithSuper = instanceFieldWithSuperIds.stream().map(this::getBaseLayerField).toList().toArray(new ResolvedJavaField[0]); + baseLayerType.setInstanceFieldsWithSuper(instanceFieldsWithSuper); - Integer superClassTid = get(typeData, SUPER_CLASS_TAG); - ResolvedJavaType superClass = getResolvedJavaType(superClassTid); + AnalysisType type = universe.lookup(baseLayerType); + guarantee(getBaseLayerTypeId(type) == tid, "The base layer type %s is not correctly matched to the id %d", type, tid); + } + } - List interfacesIds = get(typeData, INTERFACES_TAG); - ResolvedJavaType[] interfaces = interfacesIds.stream().map(this::getResolvedJavaType).toList().toArray(new ResolvedJavaType[0]); + private BaseLayerType getBaseLayerType(int tid) { + EconomicMap typesMap = get(jsonMap, TYPES_TAG); + EconomicMap typeData = get(typesMap, typeIdToIdentifier.get(tid)); - ResolvedJavaType objectType = universe.getOriginalMetaAccess().lookupJavaType(Object.class); + Integer superClassTid = get(typeData, SUPER_CLASS_TAG); + ResolvedJavaType superClass = getResolvedJavaType(superClassTid); - Annotation[] annotations = getAnnotations(typeData); + List interfacesIds = get(typeData, INTERFACES_TAG); + ResolvedJavaType[] interfaces = interfacesIds.stream().map(this::getResolvedJavaType).toList().toArray(new ResolvedJavaType[0]); - return new BaseLayerType(className, tid, modifiers, isInterface, isEnum, isInitialized, initializedAtBuildTime, isLinked, sourceFileName, enclosingType, componentType, superClass, - interfaces, objectType, annotations); - }); - BaseLayerType baseLayerType = baseLayerTypes.get(tid); - AnalysisType type = universe.lookup(baseLayerType); - guarantee(getBaseLayerTypeId(type) == tid, "The base layer type %s is not correctly matched to the id %d", type, tid); - } + return getBaseLayerType(typeData, tid, superClass, interfaces); + } + + private BaseLayerType getBaseLayerType(EconomicMap typeData, int tid, ResolvedJavaType superClass, ResolvedJavaType[] interfaces) { + return baseLayerTypes.computeIfAbsent(tid, typeId -> { + String className = get(typeData, CLASS_NAME_TAG); + int modifiers = get(typeData, MODIFIERS_TAG); + boolean isInterface = get(typeData, IS_INTERFACE_TAG); + boolean isEnum = get(typeData, IS_ENUM_TAG); + boolean isInitialized = get(typeData, IS_INITIALIZED_TAG); + boolean initializedAtBuildTime = get(typeData, IS_INITIALIZED_AT_BUILD_TIME_TAG); + boolean isLinked = get(typeData, IS_LINKED_TAG); + String sourceFileName = get(typeData, SOURCE_FILE_NAME_TAG); + + Integer enclosingTid = get(typeData, ENCLOSING_TYPE_TAG); + ResolvedJavaType enclosingType = getResolvedJavaType(enclosingTid); + + Integer componentTid = get(typeData, COMPONENT_TYPE_TAG); + ResolvedJavaType componentType = getResolvedJavaType(componentTid); + + ResolvedJavaType objectType = universe.getOriginalMetaAccess().lookupJavaType(Object.class); + + Annotation[] annotations = getAnnotations(typeData); + + return new BaseLayerType(className, tid, modifiers, isInterface, isEnum, isInitialized, initializedAtBuildTime, isLinked, sourceFileName, enclosingType, componentType, superClass, + interfaces, objectType, annotations); + }); } protected Annotation[] getAnnotations(@SuppressWarnings("unused") EconomicMap elementData) { @@ -658,6 +688,14 @@ private void loadMethod(EconomicMap methodData) { return; } + int tid = get(methodData, TID_TAG); + AnalysisType type = getAnalysisType(tid); + + List parameterTypeIds = get(methodData, ARGUMENT_IDS_TAG); + AnalysisType[] parameterTypes = parameterTypeIds.stream().map(this::getAnalysisType).toList().toArray(new AnalysisType[0]); + + AnalysisType returnType = getAnalysisType(get(methodData, RETURN_TYPE_TAG)); + String name = get(methodData, NAME_TAG); String className = get(methodData, CLASS_NAME_TAG); if (className != null) { @@ -678,13 +716,7 @@ private void loadMethod(EconomicMap methodData) { } } - int tid = get(methodData, TID_TAG); - List argumentIds = get(methodData, ARGUMENT_IDS_TAG); - int returnTypeId = get(methodData, RETURN_TYPE_TAG); - - AnalysisType type = getAnalysisType(tid); - List arguments = argumentIds.stream().map(this::getAnalysisType).toList(); - Class[] argumentClasses = arguments.stream().map(AnalysisType::getJavaClass).toList().toArray(new Class[0]); + Class[] argumentClasses = Arrays.stream(parameterTypes).map(AnalysisType::getJavaClass).toList().toArray(new Class[0]); Executable method = lookupMethodByReflection(name, type.getJavaClass(), argumentClasses); if (method != null) { @@ -694,8 +726,7 @@ private void loadMethod(EconomicMap methodData) { } } - AnalysisType returnType = getAnalysisType(returnTypeId); - ResolvedSignature signature = ResolvedSignature.fromList(arguments, returnType); + ResolvedSignature signature = ResolvedSignature.fromList(Arrays.stream(parameterTypes).toList(), returnType); if (name.equals(CONSTRUCTOR_NAME)) { type.findConstructor(signature); @@ -706,11 +737,11 @@ private void loadMethod(EconomicMap methodData) { } if (!methods.containsKey(mid)) { - createBaseLayerMethod(methodData, mid, name); + createBaseLayerMethod(methodData, mid, name, parameterTypes, returnType); } } - private static Executable lookupMethodByReflection(String name, Class clazz, Class[] argumentClasses) { + public static Executable lookupMethodByReflection(String name, Class clazz, Class[] argumentClasses) { try { Executable method; if (name.equals(CONSTRUCTOR_NAME)) { @@ -724,11 +755,8 @@ private static Executable lookupMethodByReflection(String name, Class clazz, } } - private void createBaseLayerMethod(EconomicMap methodData, int mid, String name) { + private void createBaseLayerMethod(EconomicMap methodData, int mid, String name, AnalysisType[] parameterTypes, AnalysisType returnType) { AnalysisType type = getAnalysisType(get(methodData, TID_TAG)); - List parameterTypeIds = get(methodData, ARGUMENT_IDS_TAG); - AnalysisType[] parameterTypes = parameterTypeIds.stream().map(this::getAnalysisType).toList().toArray(new AnalysisType[0]); - AnalysisType returnType = getAnalysisType(get(methodData, RETURN_TYPE_TAG)); ResolvedSignature signature = ResolvedSignature.fromArray(parameterTypes, returnType); boolean canBeStaticallyBound = get(methodData, CAN_BE_STATICALLY_BOUND_TAG); boolean isConstructor = get(methodData, IS_CONSTRUCTOR_TAG); @@ -884,6 +912,7 @@ private EconomicMap getMethodData(AnalysisMethod analysisMethod) private void loadField(FieldIdentifier fieldIdentifier, EconomicMap fieldData) { AnalysisType declaringClass = getAnalysisType(Integer.parseInt(fieldIdentifier.tid)); String className = get(fieldData, CLASS_NAME_TAG); + int id = get(fieldData, ID_TAG); Class clazz = className != null ? lookupBaseLayerTypeInHostVM(className) : declaringClass.getJavaClass(); if (clazz == null) { @@ -897,17 +926,45 @@ private void loadField(FieldIdentifier fieldIdentifier, EconomicMap fieldsMap = get(jsonMap, FIELDS_TAG); + EconomicMap fieldData = get(get(fieldsMap, fieldIdentifier.tid), fieldIdentifier.name); + + BaseLayerType declaringClass = getBaseLayerType(Integer.parseInt(fieldIdentifier.tid)); + ResolvedJavaType type = getResolvedJavaType(get(fieldData, FIELD_TYPE_TAG)); + + return getBaseLayerField(fieldIdentifier, fieldData, id, declaringClass, type); + } + + private BaseLayerField getBaseLayerField(FieldIdentifier fieldIdentifier, EconomicMap fieldData, int id, ResolvedJavaType declaringClass, ResolvedJavaType type) { + return baseLayerFields.computeIfAbsent(id, + fid -> new BaseLayerField(id, fieldIdentifier.name, declaringClass, type, get(fieldData, IS_INTERNAL_TAG), get(fieldData, IS_SYNTHETIC_TAG), get(fieldData, MODIFIERS_TAG), + getAnnotations(fieldData))); + } + public AnalysisField getAnalysisField(int fid) { if (!fields.containsKey(fid)) { FieldIdentifier fieldIdentifier = fieldIdToIdentifier.get(fid); @@ -968,7 +1025,7 @@ public void initializeBaseLayerField(AnalysisField analysisField) { registerFlag(isFolded, true, () -> analysisField.registerAsFolded(PERSISTED)); } - private EconomicMap getFieldData(AnalysisField analysisField) { + protected EconomicMap getFieldData(AnalysisField analysisField) { int tid = analysisField.getDeclaringClass().getId(); EconomicMap typeFieldsMap = getElementData(FIELDS_TAG, Integer.toString(tid)); if (typeFieldsMap == null) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java index 32d05bfd421e..a8ef074e6de2 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java @@ -71,6 +71,7 @@ public class ImageLayerSnapshotUtil { public static final String METHODS_TAG = "methods"; public static final String FIELDS_TAG = "fields"; public static final String IS_INTERNAL_TAG = "is internal"; + public static final String IS_STATIC_TAG = "is static"; public static final String FIELD_TYPE_TAG = "field type"; public static final String CLASS_JAVA_NAME_TAG = "class java name"; public static final String CAN_BE_STATICALLY_BOUND_TAG = "can be statically bound"; @@ -85,6 +86,7 @@ public class ImageLayerSnapshotUtil { public static final String IS_IMPLEMENTATION_INVOKED = "is implementation invoked"; public static final String IS_INTRINSIC_METHOD = "is intrinsic method"; public static final String ANNOTATIONS_TAG = "annotations"; + public static final String ANNOTATION_VALUES_TAG = "annotation values"; public static final String IS_INSTANTIATED = "is instantiated"; public static final String IS_UNSAFE_ALLOCATED = "is unsafe allocated"; public static final String IS_REACHABLE = "is reachable"; @@ -104,8 +106,11 @@ public class ImageLayerSnapshotUtil { public static final String GENERATED_SERIALIZATION_TAG = "generated serialization"; public static final String LAMBDA_TYPE_TAG = "lambda type"; public static final String CAPTURING_CLASS_TAG = "capturing class"; + public static final String PROXY_TYPE_TAG = "proxy type"; public static final String RAW_DECLARING_CLASS_TAG = "raw declaring class"; public static final String RAW_TARGET_CONSTRUCTOR_CLASS_TAG = "raw target constructor class"; + public static final String INSTANCE_FIELDS_TAG = "instance fields"; + public static final String INSTANCE_FIELDS_WITH_SUPER_TAG = "instance fields with super"; public static final String CONSTANTS_TAG = "constants"; public static final String CONSTANTS_TO_RELINK_TAG = "constants to relink"; public static final String TID_TAG = "tid"; @@ -118,8 +123,17 @@ public class ImageLayerSnapshotUtil { public static final String METHOD_TYPE_PARAMETERS_TAG = "method type parameters"; public static final String METHOD_TYPE_RETURN_TAG = "method type return"; public static final String FACTORY_TAG = "factory"; + public static final String C_ENTRY_POINT_CALL_STUB_METHOD_TAG = "CEntryPointCallStubMethod"; + public static final String REFLECTION_EXPAND_SIGNATURE_METHOD_TAG = "reflection expand signature method"; + public static final String JNI_JAVA_CALL_VARIANT_WRAPPER_METHOD_TAG = "jni java call variant wrapper method"; public static final String OUTLINED_SB_TAG = "outlinedSB"; + public static final String ORIGINAL_METHOD_ID_TAG = "original method id"; + public static final String NOT_AS_PUBLISHED_TAG = "not as published"; public static final String TARGET_CONSTRUCTOR_TAG = "target constructor"; + public static final String INSTANTIATED_TYPE_TAG = "instantiated type"; + public static final String WRAPPED_MEMBER_CLASS_TAG = "wrapped member class"; + public static final String WRAPPED_MEMBER_NAME_TAG = "wrapped member name"; + public static final String WRAPPED_MEMBER_ARGUMENTS_TAG = "wrapped member arguments"; public static final String THROW_ALLOCATED_OBJECT_TAG = "throw allocated object"; public static final String IDENTITY_HASH_CODE_TAG = "identityHashCode"; public static final String PARENT_CONSTANT_ID_TAG = "parent constant id"; @@ -146,6 +160,7 @@ public class ImageLayerSnapshotUtil { public static final String ARRAY_TAG = "array"; public static final String PRIMITIVE_ARRAY_TAG = "primitive array"; public static final String RELOCATED_CONSTANT_TAG = "relocation constant"; + public static final String FIELD_CHECK_TAG = "field check"; public static final String FIELD_ACCESSED_TAG = "accessed"; public static final String FIELD_READ_TAG = "read"; public static final String FIELD_WRITTEN_TAG = "written"; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java index c24ccef9b83e..b23089b4361c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java @@ -51,6 +51,8 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IDENTITY_HASH_CODE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_HEAP_SIZE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANCE_FIELDS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANCE_FIELDS_WITH_SUPER_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANCE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTERFACES_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTRINSIC_TAG; @@ -66,6 +68,7 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INVOKED; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_LINKED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_REACHABLE; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_STATIC_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_SYNTHETIC_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_UNSAFE_ALLOCATED; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_VAR_ARGS_TAG; @@ -84,8 +87,8 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PARENT_CONSTANT_INDEX_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.POSITION_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PRIMITIVE_ARRAY_TAG; -import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RETURN_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RELOCATED_CONSTANT_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RETURN_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SIMULATED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SOURCE_FILE_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STRENGTHENED_GRAPH_TAG; @@ -98,6 +101,8 @@ import java.io.IOException; import java.io.PrintWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.nio.ByteBuffer; @@ -208,7 +213,6 @@ public ImageLayerWriter() { this(true); } - @SuppressWarnings({"this-escape", "unused"}) public ImageLayerWriter(boolean useSharedLayerGraphs) { this.useSharedLayerGraphs = useSharedLayerGraphs; this.useSharedLayerStrengthenedGraphs = false; @@ -278,12 +282,6 @@ public void persistAnalysisInfo() { jsonMap.put(NEXT_METHOD_ID_TAG, aUniverse.getNextMethodId()); jsonMap.put(NEXT_FIELD_ID_TAG, aUniverse.getNextFieldId()); - /* - * $$TypeSwitch classes should not be instantiated as they are only used as a container for - * a static method, so no constant of those types should be created. This filter can be - * removed after a mechanism for determining which types have to be persisted is added, or - * if a stable name is implemented for them. - */ for (AnalysisType type : aUniverse.getTypes().stream().filter(AnalysisType::isTrackedAcrossLayers).toList()) { checkTypeStability(type); persistType(type); @@ -312,6 +310,16 @@ public void persistAnalysisInfo() { jsonMap.put(CONSTANTS_TO_RELINK_TAG, constantsToRelink); } + private void persistAnnotations(AnnotatedElement annotatedElement, EconomicMap typeMap) { + Class[] annotationTypes = AnnotationAccess.getAnnotationTypes(annotatedElement); + persistAnnotations(annotatedElement, typeMap, annotationTypes); + } + + @SuppressWarnings("unused") + protected void persistAnnotations(AnnotatedElement annotatedElement, EconomicMap typeMap, Class[] annotationTypes) { + typeMap.put(ANNOTATIONS_TAG, Arrays.stream(annotationTypes).map(Class::getName).toList()); + } + /** * A hook used to persist more general information about the base layer not accessible in * pointsto. @@ -338,6 +346,15 @@ protected void persistType(AnalysisType type) { */ persistType(superclass); } + + for (AnalysisType interfaceType : type.getInterfaces()) { + /* + * Some persisted types are not reachable. In this case, the interfaces have to be + * persisted manually as well. + */ + persistType(interfaceType); + } + EconomicMap typeMap = EconomicMap.create(); persistType(type, typeMap); @@ -364,8 +381,17 @@ protected void persistType(AnalysisType type, EconomicMap typeMa typeMap.put(IS_INITIALIZED_TAG, type.isInitialized()); typeMap.put(IS_LINKED_TAG, type.isLinked()); typeMap.put(SOURCE_FILE_NAME_TAG, type.getSourceFileName()); - if (type.getEnclosingType() != null) { - typeMap.put(ENCLOSING_TYPE_TAG, type.getEnclosingType().getId()); + try { + AnalysisType enclosingType = type.getEnclosingType(); + if (enclosingType != null) { + typeMap.put(ENCLOSING_TYPE_TAG, enclosingType.getId()); + } + } catch (AnalysisError.TypeNotFoundError e) { + /* + * GR-59571: The enclosing type is not automatically created when the inner type is + * created. If the enclosing type is missing, it is ignored for now. This try/catch + * block could be removed after the trackAcrossLayers is fully implemented. + */ } if (type.isArray()) { typeMap.put(COMPONENT_TYPE_TAG, type.getComponentType().getId()); @@ -374,7 +400,9 @@ protected void persistType(AnalysisType type, EconomicMap typeMa typeMap.put(SUPER_CLASS_TAG, type.getSuperclass().getId()); } typeMap.put(INTERFACES_TAG, Arrays.stream(type.getInterfaces()).map(AnalysisType::getId).toList()); - typeMap.put(ANNOTATIONS_TAG, Arrays.stream(AnnotationAccess.getAnnotationTypes(type)).map(Class::getName).toList()); + typeMap.put(INSTANCE_FIELDS_TAG, Arrays.stream(type.getInstanceFields(false)).map(field -> ((AnalysisField) field).getId()).toList()); + typeMap.put(INSTANCE_FIELDS_WITH_SUPER_TAG, Arrays.stream(type.getInstanceFields(true)).map(field -> ((AnalysisField) field).getId()).toList()); + persistAnnotations(type, typeMap); typeMap.put(IS_INSTANTIATED, type.isInstantiated()); typeMap.put(IS_UNSAFE_ALLOCATED, type.isUnsafeAllocated()); @@ -410,6 +438,9 @@ protected void persistMethod(AnalysisMethod method, EconomicMap methodMap.put(ARGUMENTS_TAG, Arrays.stream(executable.getParameterTypes()).map(Class::getName).toList()); methodMap.put(CLASS_NAME_TAG, executable.getDeclaringClass().getName()); } + + persistType(method.getSignature().getReturnType()); + methodMap.put(TID_TAG, method.getDeclaringClass().getId()); methodMap.put(ARGUMENT_IDS_TAG, method.getSignature().toParameterList(null).stream().map(AnalysisType::getId).toList()); methodMap.put(ID_TAG, method.getId()); @@ -429,7 +460,7 @@ protected void persistMethod(AnalysisMethod method, EconomicMap if (intrinsicMethod != null) { methodMap.put(METHOD_HANDLE_INTRINSIC_TAG, intrinsicMethod.name()); } - methodMap.put(ANNOTATIONS_TAG, Arrays.stream(AnnotationAccess.getAnnotationTypes(method)).map(Class::getName).toList()); + persistAnnotations(method, methodMap); methodMap.put(IS_VIRTUAL_ROOT_METHOD, method.isVirtualRootMethod()); methodMap.put(IS_DIRECT_ROOT_METHOD, method.isDirectRootMethod()); @@ -524,11 +555,13 @@ protected void persistField(AnalysisField field) { if (originalField != null && !originalField.getDeclaringClass().equals(field.getDeclaringClass().getJavaClass())) { fieldMap.put(CLASS_NAME_TAG, originalField.getDeclaringClass().getName()); } + fieldMap.put(IS_STATIC_TAG, field.isStatic()); fieldMap.put(IS_INTERNAL_TAG, field.isInternal()); + fieldMap.put(IS_SYNTHETIC_TAG, field.isSynthetic()); fieldMap.put(FIELD_TYPE_TAG, field.getType().getId()); fieldMap.put(MODIFIERS_TAG, field.getModifiers()); fieldMap.put(POSITION_TAG, field.getPosition()); - fieldMap.put(ANNOTATIONS_TAG, Arrays.stream(AnnotationAccess.getAnnotationTypes(field)).map(Class::getName).toList()); + persistAnnotations(field, fieldMap); String tid = String.valueOf(field.getDeclaringClass().getId()); fieldsMap.computeIfAbsent(tid, key -> new ConcurrentHashMap<>()).put(field.getName(), fieldMap); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerField.java index f7cbc7d3d1b2..3921b3a4dea6 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerField.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerField.java @@ -40,18 +40,20 @@ public class BaseLayerField extends BaseLayerElement implements ResolvedJavaField { private final int id; private final String name; - private final AnalysisType declaringClass; - private final AnalysisType type; + private final ResolvedJavaType declaringClass; + private final ResolvedJavaType type; private final boolean isInternal; + private final boolean isSynthetic; private final int modifiers; - public BaseLayerField(int id, String name, AnalysisType declaringClass, AnalysisType type, boolean isInternal, int modifiers, Annotation[] annotations) { + public BaseLayerField(int id, String name, ResolvedJavaType declaringClass, ResolvedJavaType type, boolean isInternal, boolean isSynthetic, int modifiers, Annotation[] annotations) { super(annotations); this.id = id; this.name = name; this.declaringClass = declaringClass; this.type = type; this.isInternal = isInternal; + this.isSynthetic = isSynthetic; this.modifiers = modifiers; } @@ -76,7 +78,7 @@ public boolean isInternal() { @Override public boolean isSynthetic() { - throw GraalError.unimplemented("This field is incomplete and should not be used."); + return isSynthetic; } @Override @@ -86,12 +88,12 @@ public String getName() { @Override public JavaType getType() { - return type.getWrapped(); + return type; } @Override public ResolvedJavaType getDeclaringClass() { - return declaringClass.getWrapped(); + return declaringClass; } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerMethod.java index b1d5e78fe675..444ee03cba85 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerMethod.java @@ -166,7 +166,7 @@ public ExceptionHandler[] getExceptionHandlers() { @Override public StackTraceElement asStackTraceElement(int bci) { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return new StackTraceElement(declaringClass.toClassName(), name, declaringClass.getSourceFileName(), -1); } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerType.java index 0e8bc2bcb6d1..e11da2faf88c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerType.java @@ -64,6 +64,8 @@ public class BaseLayerType extends BaseLayerElement implements ResolvedJavaType, private final ResolvedJavaType superClass; private final ResolvedJavaType[] interfaces; private final ResolvedJavaType objectType; + private ResolvedJavaField[] instanceFields; + private ResolvedJavaField[] instanceFieldsWithSuper; public BaseLayerType(String name, int baseLayerId, int modifiers, boolean isInterface, boolean isEnum, boolean isInitialized, boolean initializedAtBuildTime, boolean isLinked, String sourceFileName, ResolvedJavaType enclosingType, ResolvedJavaType componentType, ResolvedJavaType superClass, ResolvedJavaType[] interfaces, ResolvedJavaType objectType, @@ -85,6 +87,14 @@ public BaseLayerType(String name, int baseLayerId, int modifiers, boolean isInte this.objectType = objectType; } + public void setInstanceFields(ResolvedJavaField[] instanceFields) { + this.instanceFields = instanceFields; + } + + public void setInstanceFieldsWithSuper(ResolvedJavaField[] instanceFieldsWithSuper) { + this.instanceFieldsWithSuper = instanceFieldsWithSuper; + } + @Override public boolean isArray() { return name.charAt(0) == '['; @@ -244,11 +254,7 @@ public ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) { @Override public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) { - /* - * For now, the base layer types have no fields. If they are needed, a BaseLayerField could - * be created and put in an AnalysisField in a similar way to this BaseLayerType. - */ - return new ResolvedJavaField[0]; + return includeSuperclasses ? instanceFieldsWithSuper : instanceFields; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationArrayValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationArrayValue.java index 709463aad274..4d602de98dbd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationArrayValue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationArrayValue.java @@ -51,10 +51,11 @@ static AnnotationArrayValue extract(ByteBuffer buf, ConstantPool cp, Class co return skip ? null : new AnnotationArrayValue(elements); } - AnnotationArrayValue(Class elementType, Object[] values) { - this.elements = new AnnotationMemberValue[values.length]; - for (int i = 0; i < values.length; ++i) { - this.elements[i] = AnnotationMemberValue.from(elementType, values[i]); + AnnotationArrayValue(Class elementType, Object values) { + int length = Array.getLength(values); + this.elements = new AnnotationMemberValue[length]; + for (int i = 0; i < length; ++i) { + this.elements[i] = AnnotationMemberValue.from(elementType, Array.get(values, i)); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMemberValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMemberValue.java index 0b0318af7801..4c44faf1cd2f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMemberValue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMemberValue.java @@ -25,11 +25,14 @@ package com.oracle.svm.hosted.annotation; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.Collections; import java.util.List; import jdk.internal.reflect.ConstantPool; +import sun.reflect.annotation.AnnotationType; public abstract class AnnotationMemberValue { static AnnotationMemberValue extract(ByteBuffer buf, ConstantPool cp, Class container, boolean skip) { @@ -60,17 +63,28 @@ static AnnotationMemberValue from(Class type, Object value) { } else if (type == String.class) { return new AnnotationStringValue((String) value); } else if (type.isArray()) { - return new AnnotationArrayValue(type.getComponentType(), (Object[]) value); + return new AnnotationArrayValue(type.getComponentType(), value); } else { return new AnnotationPrimitiveValue(type, value); } } + public static AnnotationMemberValue getMemberValue(Annotation annotation, String memberName, Method memberAccessor, AnnotationType annotationType) { + AnnotationMemberValue memberValue; + try { + memberAccessor.setAccessible(true); + memberValue = AnnotationMemberValue.from(annotationType.memberTypes().get(memberName), memberAccessor.invoke(annotation)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new AnnotationMetadata.AnnotationExtractionError(annotation, e); + } + return memberValue; + } + public List> getTypes() { return Collections.emptyList(); } public abstract char getTag(); - abstract Object get(Class memberType); + public abstract Object get(Class memberType); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMetadata.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMetadata.java index da1be4a27418..ee63c890c718 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMetadata.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMetadata.java @@ -40,7 +40,7 @@ public class AnnotationMetadata { @SuppressWarnings("serial") - static final class AnnotationExtractionError extends Error { + public static final class AnnotationExtractionError extends Error { AnnotationExtractionError(Object targetElement, Throwable cause) { super("Failed to process '%s': %s".formatted(targetElement, cause), cause); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationValue.java index 73956be6d558..b16f632777c2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationValue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationValue.java @@ -26,7 +26,6 @@ import java.lang.annotation.Annotation; import java.lang.annotation.AnnotationFormatError; -import java.lang.reflect.InvocationTargetException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -88,12 +87,7 @@ static AnnotationValue forAnnotationFormatException() { this.members = new LinkedHashMap<>(); AnnotationType annotationType = AnnotationType.getInstance(type); annotationType.members().forEach((memberName, memberAccessor) -> { - AnnotationMemberValue memberValue; - try { - memberValue = AnnotationMemberValue.from(annotationType.memberTypes().get(memberName), memberAccessor.invoke(annotation)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new AnnotationMetadata.AnnotationExtractionError(annotation, e); - } + AnnotationMemberValue memberValue = AnnotationMemberValue.getMemberValue(annotation, memberName, memberAccessor, annotationType); Object memberDefault = annotationType.memberDefaults().get(memberName); if (!memberValue.equals(memberDefault)) { members.put(memberName, memberValue); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java index efa51ff354c2..9cdfa4b61c40 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java @@ -622,4 +622,8 @@ private void generateEpilogue(HostedGraphKit kit) { "Epilogue method must be annotated with @%s: %s", Uninterruptible.class.getSimpleName(), epilogueMethods[0]); generatePrologueOrEpilogueInvoke(kit, epilogueMethods[0]); } + + public boolean isNotPublished() { + return entryPointData.getPublishAs().equals(CEntryPoint.Publish.NotPublished); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java index 1c2baa2b91c7..f0ca9deee284 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java @@ -114,4 +114,8 @@ public ResolvedJavaMethod getTargetConstructor() { public boolean throwAllocatedObject() { return throwAllocatedObject; } + + public ResolvedJavaType getInstantiatedType() { + return instantiatedType; + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/IsStaticFinalFieldInitializedNode.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/IsStaticFinalFieldInitializedNode.java index 345f0b83f360..2eb7156beb89 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/IsStaticFinalFieldInitializedNode.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/IsStaticFinalFieldInitializedNode.java @@ -24,6 +24,10 @@ */ package com.oracle.svm.hosted.fieldfolding; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.svm.hosted.code.AnalysisToHostedGraphTransplanter; +import com.oracle.svm.hosted.meta.HostedField; + import jdk.graal.compiler.core.common.type.StampFactory; import jdk.graal.compiler.graph.NodeClass; import jdk.graal.compiler.nodeinfo.NodeCycles; @@ -35,11 +39,6 @@ import jdk.graal.compiler.nodes.java.LoadIndexedNode; import jdk.graal.compiler.nodes.spi.Simplifiable; import jdk.graal.compiler.nodes.spi.SimplifierTool; - -import com.oracle.graal.pointsto.meta.AnalysisField; -import com.oracle.svm.hosted.code.AnalysisToHostedGraphTransplanter; -import com.oracle.svm.hosted.meta.HostedField; - import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; @@ -89,7 +88,7 @@ public void simplify(SimplifierTool tool) { } else { StaticFinalFieldFoldingFeature feature = StaticFinalFieldFoldingFeature.singleton(); - Integer fieldCheckIndex = feature.fieldCheckIndexMap.get(StaticFinalFieldFoldingFeature.toAnalysisField(field)); + Integer fieldCheckIndex = feature.getFieldCheckIndex(field); assert fieldCheckIndex != null : "Field must be optimizable: " + field; ConstantNode fieldInitializationStatusNode = ConstantNode.forConstant(tool.getSnippetReflection().forObject(feature.fieldInitializationStatus), tool.getMetaAccess(), graph()); ConstantNode fieldCheckIndexNode = ConstantNode.forInt(fieldCheckIndex, graph()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/MarkStaticFinalFieldInitializedNode.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/MarkStaticFinalFieldInitializedNode.java index d352a1d6e199..8acc9e925818 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/MarkStaticFinalFieldInitializedNode.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/MarkStaticFinalFieldInitializedNode.java @@ -24,6 +24,10 @@ */ package com.oracle.svm.hosted.fieldfolding; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.svm.hosted.code.AnalysisToHostedGraphTransplanter; +import com.oracle.svm.hosted.meta.HostedField; + import jdk.graal.compiler.core.common.type.StampFactory; import jdk.graal.compiler.graph.NodeClass; import jdk.graal.compiler.nodeinfo.NodeCycles; @@ -34,11 +38,6 @@ import jdk.graal.compiler.nodes.java.StoreIndexedNode; import jdk.graal.compiler.nodes.spi.Simplifiable; import jdk.graal.compiler.nodes.spi.SimplifierTool; - -import com.oracle.graal.pointsto.meta.AnalysisField; -import com.oracle.svm.hosted.code.AnalysisToHostedGraphTransplanter; -import com.oracle.svm.hosted.meta.HostedField; - import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; @@ -77,7 +76,7 @@ public void simplify(SimplifierTool tool) { assert field instanceof HostedField; StaticFinalFieldFoldingFeature feature = StaticFinalFieldFoldingFeature.singleton(); - Integer fieldCheckIndex = feature.fieldCheckIndexMap.get(StaticFinalFieldFoldingFeature.toAnalysisField(field)); + Integer fieldCheckIndex = feature.getFieldCheckIndex(field); if (fieldCheckIndex != null) { ConstantNode fieldInitializationStatusNode = ConstantNode.forConstant(tool.getSnippetReflection().forObject(feature.fieldInitializationStatus), tool.getMetaAccess(), graph()); ConstantNode fieldCheckIndexNode = ConstantNode.forInt(fieldCheckIndex, graph()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingFeature.java index 97c42656b450..8e4dda2c3e52 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingFeature.java @@ -33,12 +33,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import jdk.graal.compiler.graph.Node; -import jdk.graal.compiler.nodes.StructuredGraph; -import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; -import jdk.graal.compiler.nodes.java.StoreFieldNode; -import jdk.graal.compiler.options.Option; -import jdk.graal.compiler.phases.util.Providers; import org.graalvm.nativeimage.ImageSingletons; import com.oracle.graal.pointsto.BigBang; @@ -54,6 +48,12 @@ import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.meta.HostedField; +import jdk.graal.compiler.graph.Node; +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; +import jdk.graal.compiler.nodes.java.StoreFieldNode; +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.phases.util.Providers; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaField; @@ -99,7 +99,7 @@ * */ @AutomaticallyRegisteredFeature -final class StaticFinalFieldFoldingFeature implements InternalFeature { +public final class StaticFinalFieldFoldingFeature implements InternalFeature { public static class Options { @Option(help = "Optimize static final fields that get a constant assigned in the class initializer.")// @@ -109,6 +109,14 @@ public static class Options { BigBang bb; final Map foldedFieldValues = new ConcurrentHashMap<>(); Map fieldCheckIndexMap; + /** + * Stores the field check index from the base layer. + * + * GR-59855: This is currently only used to determine that a field was folded in a previous + * layer already. The value from the base layer should also be reused in extension layers to + * ensure valid field folding across layers. + */ + Map baseLayerFieldCheckIndexMap = new HashMap<>(); boolean[] fieldInitializationStatus; public static StaticFinalFieldFoldingFeature singleton() { @@ -161,6 +169,7 @@ public void afterAnalysis(AfterAnalysisAccess access) { fieldCheckIndexMap = new HashMap<>(); int fieldCheckIndex = 0; for (AnalysisField field : foldedFields) { + assert !baseLayerFieldCheckIndexMap.containsKey(field.getId()) : "The field %s was already assigned an index in the base layer".formatted(field); fieldCheckIndexMap.put(field, fieldCheckIndex); fieldCheckIndex++; } @@ -198,7 +207,7 @@ void onAnalysisMethodParsed(AnalysisMethod method, StructuredGraph graph) { if (n instanceof StoreFieldNode) { StoreFieldNode node = (StoreFieldNode) n; AnalysisField field = (AnalysisField) node.field(); - if (field.isStatic() && field.isFinal()) { + if (field.isStatic() && field.isFinal() && !field.isInBaseLayer()) { if (isClassInitializer && field.getDeclaringClass().equals(method.getDeclaringClass())) { analyzeStoreInClassInitializer(node, field, optimizableFields, ineligibleFields); } else { @@ -277,4 +286,20 @@ static AnalysisField toAnalysisField(ResolvedJavaField field) { return (AnalysisField) field; } } + + public Integer getFieldCheckIndex(ResolvedJavaField field) { + return getFieldCheckIndex(toAnalysisField(field)); + } + + public Integer getFieldCheckIndex(AnalysisField field) { + if (field.isInBaseLayer()) { + return baseLayerFieldCheckIndexMap.get(field.getId()); + } else { + return fieldCheckIndexMap.get(field); + } + } + + public void putBaseLayerFieldCheckIndex(int id, Integer fieldCheckIndex) { + baseLayerFieldCheckIndexMap.put(id, fieldCheckIndex); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java index 59e35467cc7a..b10124d54c34 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java @@ -25,8 +25,10 @@ package com.oracle.svm.hosted.heap; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ANNOTATIONS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ANNOTATION_VALUES_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.C_ENTRY_POINT_LITERAL_CODE_POINTER; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_CHECK_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_KEYS; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_OBJECTS; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INFO_CLASS_INITIALIZER_TAG; @@ -60,6 +62,7 @@ import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapInstance; import com.oracle.graal.pointsto.heap.ImageLayerLoader; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.AnalysisError; @@ -77,6 +80,7 @@ import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.c.CGlobalDataFeature; +import com.oracle.svm.hosted.fieldfolding.StaticFinalFieldFoldingFeature; import com.oracle.svm.hosted.imagelayer.HostedDynamicLayerInfo; import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.meta.RelocatableConstant; @@ -88,7 +92,6 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import sun.reflect.annotation.AnnotationParser; -import sun.reflect.annotation.AnnotationType; public class SVMImageLayerLoader extends ImageLayerLoader { @@ -166,11 +169,26 @@ public Class lookupClass(boolean optional, String className) { @Override protected Annotation[] getAnnotations(EconomicMap elementData) { List annotationNames = get(elementData, ANNOTATIONS_TAG); + List> annotationValuesList = get(elementData, ANNOTATION_VALUES_TAG); + Annotation[] annotations = new Annotation[annotationNames.size()]; + + for (int i = 0; i < annotationNames.size(); ++i) { + Class annotationType = cast(lookupBaseLayerTypeInHostVM(annotationNames.get(i))); + EconomicMap annotationValues = annotationValuesList.get(i); + Map annotationValuesMap = new HashMap<>(); + var cursor = annotationValues.getEntries(); + while (cursor.advance()) { + Object value = cursor.getValue(); + if (value instanceof EconomicMap) { + EconomicMap enumData = cast(value); + value = getEnumValue(enumData); + } + annotationValuesMap.put(cursor.getKey(), value); + } + annotations[i] = AnnotationParser.annotationForMap(annotationType, annotationValuesMap); + } - return annotationNames.stream().map(s -> { - Class annotationType = cast(lookupBaseLayerTypeInHostVM(s)); - return AnnotationParser.annotationForMap(annotationType, AnnotationType.getInstance(annotationType).memberDefaults()); - }).toList().toArray(new Annotation[0]); + return annotations; } @Override @@ -196,6 +214,18 @@ protected void afterGraphDecodeHook(EncodedGraph encodedGraph) { } } + @Override + public void initializeBaseLayerField(AnalysisField analysisField) { + EconomicMap fieldData = getFieldData(analysisField); + + Integer fieldCheckIndex = get(fieldData, FIELD_CHECK_TAG); + if (fieldCheckIndex != null) { + StaticFinalFieldFoldingFeature.singleton().putBaseLayerFieldCheckIndex(analysisField.getId(), fieldCheckIndex); + } + + super.initializeBaseLayerField(analysisField); + } + @Override protected void prepareConstantRelinking(EconomicMap constantData, int identityHashCode, int id) { Integer tid = get(constantData, CLASS_ID_TAG); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java index b49165fc61ae..c94a24ee9fad 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java @@ -26,35 +26,60 @@ import static com.oracle.graal.pointsto.heap.ImageLayerLoader.get; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CAPTURING_CLASS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.C_ENTRY_POINT_CALL_STUB_METHOD_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FACTORY_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.GENERATED_SERIALIZATION_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANTIATED_TYPE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTERFACES_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.JNI_JAVA_CALL_VARIANT_WRAPPER_METHOD_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LAMBDA_TYPE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NOT_AS_PUBLISHED_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ORIGINAL_METHOD_ID_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PROXY_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_DECLARING_CLASS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_TARGET_CONSTRUCTOR_CLASS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.REFLECTION_EXPAND_SIGNATURE_METHOD_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TARGET_CONSTRUCTOR_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.THROW_ALLOCATED_OBJECT_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_MEMBER_ARGUMENTS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_MEMBER_CLASS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_MEMBER_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_METHOD_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_TYPE_TAG; import static com.oracle.svm.hosted.lambda.LambdaParser.createMethodGraph; import static com.oracle.svm.hosted.lambda.LambdaParser.getLambdaClassFromConstantNode; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Proxy; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.graalvm.collections.EconomicMap; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.c.function.CEntryPoint; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.heap.ImageLayerLoader; import com.oracle.graal.pointsto.heap.ImageLayerLoaderHelper; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.BaseLayerMethod; import com.oracle.svm.core.reflect.serialize.SerializationSupport; +import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.code.CEntryPointCallStubSupport; +import com.oracle.svm.hosted.code.CEntryPointData; import com.oracle.svm.hosted.code.FactoryMethodSupport; +import com.oracle.svm.hosted.jni.JNIAccessFeature; import com.oracle.svm.hosted.lambda.LambdaParser; +import com.oracle.svm.hosted.reflect.ReflectionFeature; import com.oracle.svm.hosted.reflect.serialize.SerializationFeature; import com.oracle.svm.util.ReflectionUtil; import jdk.graal.compiler.graph.iterators.NodeIterable; +import jdk.graal.compiler.java.BytecodeParser; import jdk.graal.compiler.nodes.ConstantNode; import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.options.OptionValues; @@ -69,6 +94,7 @@ public SVMImageLayerLoaderHelper(ImageLayerLoader imageLayerLoader) { } @Override + @SuppressWarnings("deprecation") protected boolean loadType(EconomicMap typeData, int tid) { String wrappedType = get(typeData, WRAPPED_TYPE_TAG); if (wrappedType == null) { @@ -90,6 +116,12 @@ protected boolean loadType(EconomicMap typeData, int tid) { String capturingClassName = get(typeData, CAPTURING_CLASS_TAG); Class capturingClass = imageLayerLoader.lookupClass(false, capturingClassName); loadLambdaTypes(capturingClass); + } else if (wrappedType.equals(PROXY_TYPE_TAG)) { + List interfaceIds = get(typeData, INTERFACES_TAG); + Class[] interfaces = interfaceIds.stream().map(i -> imageLayerLoader.getAnalysisType(i).getJavaClass()).toArray(Class[]::new); + /* GR-59854: The deprecation warning comes from this call to Proxy.getProxyClass. */ + Class proxy = Proxy.getProxyClass(interfaces[0].getClassLoader(), interfaces); + imageLayerLoader.getMetaAccess().lookupJavaType(proxy); return true; } @@ -112,7 +144,13 @@ private void loadLambdaTypes(Class capturingClass) { } private static void loadLambdaTypes(ResolvedJavaMethod m, BigBang bigBang) { - StructuredGraph graph = createMethodGraph(m, bigBang.getOptions()); + StructuredGraph graph; + try { + graph = createMethodGraph(m, bigBang.getOptions()); + } catch (NoClassDefFoundError | BytecodeParser.BytecodeParserError e) { + /* Skip the method if it refers to a missing class */ + return; + } NodeIterable constantNodes = ConstantNode.getConstantNodes(graph); @@ -134,10 +172,53 @@ protected boolean loadMethod(EconomicMap methodData, int mid) { if (wrappedMethod.equals(FACTORY_TAG)) { int constructorId = get(methodData, TARGET_CONSTRUCTOR_TAG); boolean throwAllocatedObject = get(methodData, THROW_ALLOCATED_OBJECT_TAG); - FactoryMethodSupport.singleton().lookup(imageLayerLoader.getMetaAccess(), imageLayerLoader.getAnalysisMethod(constructorId), throwAllocatedObject); + AnalysisMethod analysisMethod = imageLayerLoader.getAnalysisMethod(constructorId); + if (analysisMethod.wrapped instanceof BaseLayerMethod) { + return false; + } + int instantiatedTypeId = get(methodData, INSTANTIATED_TYPE_TAG); + AnalysisType instantiatedType = imageLayerLoader.getAnalysisType(instantiatedTypeId); + FactoryMethodSupport.singleton().lookup(imageLayerLoader.getMetaAccess(), analysisMethod, instantiatedType, throwAllocatedObject); + return true; + } else if (wrappedMethod.equals(C_ENTRY_POINT_CALL_STUB_METHOD_TAG)) { + int originalMethodId = get(methodData, ORIGINAL_METHOD_ID_TAG); + boolean asNotPublished = get(methodData, NOT_AS_PUBLISHED_TAG); + AnalysisMethod originalMethod = imageLayerLoader.getAnalysisMethod(originalMethodId); + CEntryPointCallStubSupport.singleton().registerStubForMethod(originalMethod, () -> { + CEntryPointData data = CEntryPointData.create(originalMethod); + if (asNotPublished) { + data = data.copyWithPublishAs(CEntryPoint.Publish.NotPublished); + } + return data; + }); + return true; + } else if (wrappedMethod.equals(REFLECTION_EXPAND_SIGNATURE_METHOD_TAG)) { + Executable member = getWrappedMember(methodData); + if (member == null) { + return false; + } + ImageSingletons.lookup(ReflectionFeature.class).getOrCreateAccessor(member); + return true; + } else if (wrappedMethod.equals(JNI_JAVA_CALL_VARIANT_WRAPPER_METHOD_TAG)) { + Executable member = getWrappedMember(methodData); + if (member == null) { + return false; + } + JNIAccessFeature.singleton().addMethod(member, (FeatureImpl.DuringAnalysisAccessImpl) imageLayerLoader.getUniverse().getConcurrentAnalysisAccess()); return true; } - return super.loadMethod(methodData, mid); } + + private Executable getWrappedMember(EconomicMap methodData) { + String className = get(methodData, WRAPPED_MEMBER_CLASS_TAG); + Class declaringClass = imageLayerLoader.lookupClass(true, className); + if (declaringClass == null) { + return null; + } + String name = get(methodData, WRAPPED_MEMBER_NAME_TAG); + List parameterNames = get(methodData, WRAPPED_MEMBER_ARGUMENTS_TAG); + Class[] parameters = parameterNames.stream().map(c -> imageLayerLoader.lookupClass(false, c)).toArray(Class[]::new); + return ImageLayerLoader.lookupMethodByReflection(name, declaringClass, parameters); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java index c78351fe6f0d..dd58ed3ad8d7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java @@ -24,8 +24,13 @@ */ package com.oracle.svm.hosted.heap; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ANNOTATIONS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ANNOTATION_VALUES_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.C_ENTRY_POINT_LITERAL_CODE_POINTER; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ENUM_CLASS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ENUM_NAME_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_CHECK_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HUB_IDENTITY_HASH_CODE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_KEYS; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_OBJECTS; @@ -46,6 +51,8 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_OBJECT_FIELDS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_PRIMITIVE_FIELDS_TAG; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -54,6 +61,7 @@ import java.util.Map; import org.graalvm.collections.EconomicMap; +import org.graalvm.nativeimage.AnnotationAccess; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.RelocatedPointer; import org.graalvm.nativeimage.impl.CEntryPointLiteralCodePointer; @@ -76,7 +84,10 @@ import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.annotation.AnnotationMemberValue; +import com.oracle.svm.hosted.annotation.AnnotationMetadata; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; +import com.oracle.svm.hosted.fieldfolding.StaticFinalFieldFoldingFeature; import com.oracle.svm.hosted.image.NativeImageHeap; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; import com.oracle.svm.hosted.imagelayer.HostedDynamicLayerInfo; @@ -91,11 +102,13 @@ import com.oracle.svm.hosted.reflect.proxy.ProxyRenamingSubstitutionProcessor; import com.oracle.svm.hosted.reflect.proxy.ProxySubstitutionType; import com.oracle.svm.util.LogUtils; +import com.oracle.svm.util.ModuleSupport; import jdk.graal.compiler.debug.Assertions; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; +import sun.reflect.annotation.AnnotationType; public class SVMImageLayerWriter extends ImageLayerWriter { private NativeImageHeap nativeImageHeap; @@ -113,6 +126,37 @@ public void setHostedUniverse(HostedUniverse hUniverse) { this.hUniverse = hUniverse; } + @Override + protected void persistAnnotations(AnnotatedElement annotatedElement, EconomicMap elementMap, Class[] annotationTypes) { + elementMap.put(ANNOTATION_VALUES_TAG, Arrays.stream(annotationTypes).map(annotationClass -> { + EconomicMap members = EconomicMap.create(); + AnnotationType annotationType = AnnotationType.getInstance(annotationClass); + Annotation annotation = AnnotationAccess.getAnnotation(annotatedElement, annotationClass); + annotationType.members().forEach((memberName, memberAccessor) -> { + try { + String moduleName = memberAccessor.getDeclaringClass().getModule().getName(); + if (moduleName != null) { + ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, SVMImageLayerWriter.class, false, moduleName); + } + AnnotationMemberValue memberValue = AnnotationMemberValue.getMemberValue(annotation, memberName, memberAccessor, annotationType); + Object value = memberValue.get(annotationType.memberTypes().get(memberName)); + if (value.getClass().isEnum()) { + HashMap enumEncoding = new HashMap<>(); + enumEncoding.put(ENUM_CLASS_TAG, value.getClass().getName()); + enumEncoding.put(ENUM_NAME_TAG, value.toString()); + value = enumEncoding; + } + members.put(memberName, value); + } catch (AnnotationMetadata.AnnotationExtractionError e) { + /* We skip the incorrect annotation */ + } + }); + return members; + }).toList()); + elementMap.put(ANNOTATIONS_TAG, Arrays.stream(annotationTypes).map(Class::getName).toList()); + super.persistAnnotations(annotatedElement, elementMap, annotationTypes); + } + @Override protected void persistHook() { ImageHeapConstant staticPrimitiveFields = (ImageHeapConstant) hUniverse.getSnippetReflection().forObject(StaticFieldsSupport.getStaticPrimitiveFields()); @@ -207,9 +251,13 @@ public void persistMethod(AnalysisMethod method, EconomicMap met protected void persistField(AnalysisField field, EconomicMap fieldMap) { HostedField hostedField = hUniverse.lookup(field); int location = hostedField.getLocation(); - if (hostedField.isStatic() && location > 0) { + if (location > 0) { fieldMap.put(LOCATION_TAG, location); } + Integer fieldCheck = StaticFinalFieldFoldingFeature.singleton().getFieldCheckIndex(field); + if (fieldCheck != null) { + fieldMap.put(FIELD_CHECK_TAG, fieldCheck); + } super.persistField(field, fieldMap); } @@ -238,7 +286,9 @@ protected boolean delegateProcessing(List> data, Object constant) { if (constant instanceof RelocatableConstant relocatableConstant) { RelocatedPointer pointer = relocatableConstant.getPointer(); if (pointer instanceof MethodPointer methodPointer) { - data.add(List.of(METHOD_POINTER_TAG, getRelocatableConstantMethodId(methodPointer))); + AnalysisMethod method = getRelocatableConstantMethod(methodPointer); + persistMethod(method); + data.add(List.of(METHOD_POINTER_TAG, method.getId())); return true; } else if (pointer instanceof CEntryPointLiteralCodePointer cEntryPointLiteralCodePointer) { data.add(List.of(C_ENTRY_POINT_LITERAL_CODE_POINTER, cEntryPointLiteralCodePointer.methodName, cEntryPointLiteralCodePointer.definingClass.getName(), @@ -249,26 +299,12 @@ protected boolean delegateProcessing(List> data, Object constant) { return super.delegateProcessing(data, constant); } - private static int getRelocatableConstantMethodId(MethodPointer methodPointer) { + private static AnalysisMethod getRelocatableConstantMethod(MethodPointer methodPointer) { ResolvedJavaMethod method = methodPointer.getMethod(); if (method instanceof HostedMethod hostedMethod) { - return getMethodId(hostedMethod.wrapped); - } else { - return getMethodId((AnalysisMethod) method); - } - } - - private static int getMethodId(AnalysisMethod analysisMethod) { - if (!analysisMethod.isTrackedAcrossLayers()) { - /* - * Only tracked methods are persisted, so the method will not be loaded in the extension - * image. - * - * GR-59009 will ensure all methods referred to are tracked. - */ - return -1; + return hostedMethod.wrapped; } else { - return analysisMethod.getId(); + return (AnalysisMethod) method; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriterHelper.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriterHelper.java index 518b39f5a772..fd7f9f8cb4f5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriterHelper.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriterHelper.java @@ -25,17 +25,32 @@ package com.oracle.svm.hosted.heap; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CAPTURING_CLASS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTRUCTOR_NAME; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.C_ENTRY_POINT_CALL_STUB_METHOD_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FACTORY_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.GENERATED_SERIALIZATION_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANTIATED_TYPE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.JNI_JAVA_CALL_VARIANT_WRAPPER_METHOD_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LAMBDA_TYPE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NOT_AS_PUBLISHED_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ORIGINAL_METHOD_ID_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PROXY_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_DECLARING_CLASS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_TARGET_CONSTRUCTOR_CLASS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.REFLECTION_EXPAND_SIGNATURE_METHOD_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TARGET_CONSTRUCTOR_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.THROW_ALLOCATED_OBJECT_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_MEMBER_ARGUMENTS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_MEMBER_CLASS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_MEMBER_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_METHOD_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_TYPE_TAG; import static com.oracle.svm.hosted.heap.SVMImageLayerSnapshotUtil.GENERATED_SERIALIZATION; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.util.Arrays; + import org.graalvm.collections.EconomicMap; import com.oracle.graal.pointsto.heap.ImageLayerWriter; @@ -43,7 +58,12 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.reflect.serialize.SerializationSupport; +import com.oracle.svm.hosted.code.CEntryPointCallStubMethod; +import com.oracle.svm.hosted.code.CEntryPointCallStubSupport; import com.oracle.svm.hosted.code.FactoryMethod; +import com.oracle.svm.hosted.jni.JNIJavaCallVariantWrapperMethod; +import com.oracle.svm.hosted.reflect.ReflectionExpandSignatureMethod; +import com.oracle.svm.hosted.reflect.proxy.ProxyRenamingSubstitutionProcessor; import jdk.graal.compiler.java.LambdaUtils; @@ -62,6 +82,8 @@ protected void persistType(AnalysisType type, EconomicMap typeMa } else if (LambdaUtils.isLambdaType(type)) { typeMap.put(WRAPPED_TYPE_TAG, LAMBDA_TYPE_TAG); typeMap.put(CAPTURING_CLASS_TAG, LambdaUtils.capturingClass(type.toJavaName())); + } else if (ProxyRenamingSubstitutionProcessor.isProxyType(type)) { + typeMap.put(WRAPPED_TYPE_TAG, PROXY_TYPE_TAG); } super.persistType(type, typeMap); } @@ -75,7 +97,28 @@ protected void persistMethod(AnalysisMethod method, EconomicMap imageLayerWriter.persistMethod(targetConstructor); methodMap.put(TARGET_CONSTRUCTOR_TAG, targetConstructor.getId()); methodMap.put(THROW_ALLOCATED_OBJECT_TAG, factoryMethod.throwAllocatedObject()); + AnalysisType instantiatedType = method.getUniverse().lookup(factoryMethod.getInstantiatedType()); + methodMap.put(INSTANTIATED_TYPE_TAG, instantiatedType.getId()); + } else if (method.wrapped instanceof CEntryPointCallStubMethod cEntryPointCallStubMethod) { + methodMap.put(WRAPPED_METHOD_TAG, C_ENTRY_POINT_CALL_STUB_METHOD_TAG); + AnalysisMethod originalMethod = CEntryPointCallStubSupport.singleton().getMethodForStub(cEntryPointCallStubMethod); + methodMap.put(ORIGINAL_METHOD_ID_TAG, originalMethod.getId()); + methodMap.put(NOT_AS_PUBLISHED_TAG, cEntryPointCallStubMethod.isNotPublished()); + } else if (method.wrapped instanceof ReflectionExpandSignatureMethod reflectionExpandSignatureMethod) { + methodMap.put(WRAPPED_METHOD_TAG, REFLECTION_EXPAND_SIGNATURE_METHOD_TAG); + Executable member = reflectionExpandSignatureMethod.getMember(); + persistWrappedMember(methodMap, member); + } else if (method.wrapped instanceof JNIJavaCallVariantWrapperMethod jniJavaCallVariantWrapperMethod) { + Executable executable = jniJavaCallVariantWrapperMethod.getMember(); + methodMap.put(WRAPPED_METHOD_TAG, JNI_JAVA_CALL_VARIANT_WRAPPER_METHOD_TAG); + persistWrappedMember(methodMap, executable); } super.persistMethod(method, methodMap); } + + private static void persistWrappedMember(EconomicMap methodMap, Executable member) { + methodMap.put(WRAPPED_MEMBER_CLASS_TAG, member.getDeclaringClass().getName()); + methodMap.put(WRAPPED_MEMBER_NAME_TAG, member instanceof Constructor ? CONSTRUCTOR_NAME : member.getName()); + methodMap.put(WRAPPED_MEMBER_ARGUMENTS_TAG, Arrays.stream(member.getParameters()).map(p -> p.getType().getName()).toList()); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java index 1faf781e3323..c79b30e0ab31 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java @@ -420,7 +420,7 @@ private static void addNegativeClassLookup(String className) { JNIReflectionDictionary.singleton().addNegativeClassLookupIfAbsent(className); } - private void addMethod(Executable method, DuringAnalysisAccessImpl access) { + public void addMethod(Executable method, DuringAnalysisAccessImpl access) { if (SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { return; } @@ -454,10 +454,10 @@ private void addMethod(Executable method, DuringAnalysisAccessImpl access) { signature -> factory.create(signature, originalMetaAccess, access.getBigBang().getWordTypes())); access.registerAsRoot(universe.lookup(callWrapperMethod), true, "JNI call wrapper, registered in " + JNIAccessFeature.class); - JNIJavaCallVariantWrapperGroup variantWrappers = createJavaCallVariantWrappers(access, callWrapperMethod.getSignature(), false); + JNIJavaCallVariantWrapperGroup variantWrappers = createJavaCallVariantWrappers(access, callWrapperMethod.getSignature(), false, method); JNIJavaCallVariantWrapperGroup nonvirtualVariantWrappers = null; if (!Modifier.isStatic(method.getModifiers()) && !Modifier.isAbstract(method.getModifiers())) { - nonvirtualVariantWrappers = createJavaCallVariantWrappers(access, callWrapperMethod.getSignature(), true); + nonvirtualVariantWrappers = createJavaCallVariantWrappers(access, callWrapperMethod.getSignature(), true, method); } JNIAccessibleMethod jniMethod = new JNIAccessibleMethod(jniClass, method.getModifiers()); calledJavaMethods.add(new JNICallableJavaMethod(descriptor, jniMethod, targetMethod, callWrapperMethod, newObjectMethod, variantWrappers, nonvirtualVariantWrappers)); @@ -471,14 +471,14 @@ private static void addNegativeMethodLookup(Class declaringClass, String meth jniClass.addMethodIfAbsent(descriptor, d -> JNIAccessibleMethod.negativeMethodQuery(jniClass)); } - private JNIJavaCallVariantWrapperGroup createJavaCallVariantWrappers(DuringAnalysisAccessImpl access, ResolvedSignature wrapperSignature, boolean nonVirtual) { + private JNIJavaCallVariantWrapperGroup createJavaCallVariantWrappers(DuringAnalysisAccessImpl access, ResolvedSignature wrapperSignature, boolean nonVirtual, Executable method) { var map = nonVirtual ? nonvirtualCallVariantWrappers : callVariantWrappers; return map.computeIfAbsent(wrapperSignature, signature -> { MetaAccessProvider originalMetaAccess = access.getUniverse().getOriginalMetaAccess(); WordTypes wordTypes = access.getBigBang().getWordTypes(); - var varargs = new JNIJavaCallVariantWrapperMethod(signature, CallVariant.VARARGS, nonVirtual, originalMetaAccess, wordTypes); - var array = new JNIJavaCallVariantWrapperMethod(signature, CallVariant.ARRAY, nonVirtual, originalMetaAccess, wordTypes); - var valist = new JNIJavaCallVariantWrapperMethod(signature, CallVariant.VA_LIST, nonVirtual, originalMetaAccess, wordTypes); + var varargs = new JNIJavaCallVariantWrapperMethod(method, signature, CallVariant.VARARGS, nonVirtual, originalMetaAccess, wordTypes); + var array = new JNIJavaCallVariantWrapperMethod(method, signature, CallVariant.ARRAY, nonVirtual, originalMetaAccess, wordTypes); + var valist = new JNIJavaCallVariantWrapperMethod(method, signature, CallVariant.VA_LIST, nonVirtual, originalMetaAccess, wordTypes); Stream wrappers = Stream.of(varargs, array, valist); CEntryPointData unpublished = CEntryPointData.createCustomUnpublished(); wrappers.forEach(wrapper -> { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallVariantWrapperMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallVariantWrapperMethod.java index 5a1b983123ef..cd59f8fbecde 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallVariantWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIJavaCallVariantWrapperMethod.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.hosted.jni; +import java.lang.reflect.Executable; import java.util.ArrayList; import java.util.List; @@ -82,9 +83,10 @@ public class JNIJavaCallVariantWrapperMethod extends EntryPointCallStubMethod { private final Signature callWrapperSignature; private final CallVariant callVariant; private final boolean nonVirtual; + private final Executable member; - public JNIJavaCallVariantWrapperMethod(ResolvedSignature callWrapperSignature, CallVariant callVariant, boolean nonVirtual, MetaAccessProvider originalMetaAccess, - WordTypes wordTypes) { + public JNIJavaCallVariantWrapperMethod(Executable member, ResolvedSignature callWrapperSignature, CallVariant callVariant, boolean nonVirtual, + MetaAccessProvider originalMetaAccess, WordTypes wordTypes) { super(createName(callWrapperSignature, callVariant, nonVirtual), originalMetaAccess.lookupJavaType(JNIJavaCallVariantWrapperHolder.class), createSignature(callWrapperSignature, callVariant, nonVirtual, originalMetaAccess, wordTypes), @@ -92,6 +94,7 @@ public JNIJavaCallVariantWrapperMethod(ResolvedSignature callW this.callWrapperSignature = callWrapperSignature; this.callVariant = callVariant; this.nonVirtual = nonVirtual; + this.member = member; } private static String createName(ResolvedSignature targetSignature, CallVariant callVariant, boolean nonVirtual) { @@ -282,4 +285,8 @@ private List loadArguments(JNIGraphKit kit, ResolvedSignature[] argTypes; private final JavaKind returnKind; private final boolean callerSensitiveAdapter; + private final Executable member; - public ReflectionExpandSignatureMethod(String name, ResolvedJavaMethod prototype, boolean isStatic, Class[] argTypes, JavaKind returnKind, boolean callerSensitiveAdapter) { + public ReflectionExpandSignatureMethod(String name, ResolvedJavaMethod prototype, boolean isStatic, Class[] argTypes, JavaKind returnKind, boolean callerSensitiveAdapter, Executable member) { super(name, true, prototype.getDeclaringClass(), prototype.getSignature(), prototype.getConstantPool()); this.isStatic = isStatic; this.argTypes = argTypes; this.returnKind = returnKind; this.callerSensitiveAdapter = callerSensitiveAdapter; + this.member = member; } /** @@ -136,4 +140,8 @@ public StructuredGraph buildGraph(DebugContext ctx, AnalysisMethod method, Hoste return kit.finalizeGraph(); } + + public Executable getMember() { + return member; + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index dc2864a9c6c0..51664a3a01a4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -260,7 +260,7 @@ private MethodPointer createExpandSignatureMethod(Executable member, boolean cal return expandSignatureMethods.computeIfAbsent(new SignatureKey(member, callerSensitiveAdapter), signatureKey -> { ResolvedJavaMethod prototype = analysisAccess.getMetaAccess().lookupJavaMethod(callerSensitiveAdapter ? invokePrototypeForCallerSensitiveAdapter : invokePrototype).getWrapped(); return asMethodPointer(new ReflectionExpandSignatureMethod("invoke_" + signatureKey.uniqueShortName(), prototype, signatureKey.isStatic, signatureKey.argTypes, signatureKey.returnKind, - signatureKey.callerSensitiveAdapter)); + signatureKey.callerSensitiveAdapter, member)); }); }