From 5e27fe7ad3301856f0a6476fb14797d57af998fd Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Wed, 24 Apr 2024 14:49:27 +0200 Subject: [PATCH 1/2] Remove Futures from ImageLayerLoader --- .../graal/pointsto/heap/ImageLayerLoader.java | 245 +++++------------- .../graal/pointsto/meta/AnalysisUniverse.java | 7 - .../svm/hosted/NativeImageGenerator.java | 8 +- .../src/com/oracle/svm/hosted/SVMHost.java | 7 +- .../svm/hosted/heap/SVMImageLayerLoader.java | 20 +- 5 files changed, 79 insertions(+), 208 deletions(-) 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 e39fda9b49e7..feed005b0f74 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 @@ -74,7 +74,6 @@ import java.io.InputStreamReader; import java.nio.file.Path; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -114,6 +113,7 @@ * "next field id": nextFieldId, * "static primitive fields": staticPrimitiveFields.id, * "static object fields": staticObjectFields.id, + * "image heap size": imageHeapSize, * "types": { * typeIdentifier: { * "id": id, @@ -201,37 +201,10 @@ * The "offset object" is the offset of the constant in the heap from the base layer. */ public class ImageLayerLoader { - /** - * The AnalysisUniverse.createType method can be called multiple times for same type. We need to - * ensure the constants are only created once, so we keep track of the types that were already - * processed. Some types can be created before loadLayerConstants, so some types can be - * processed before they are force loaded. - */ - private final Set processedTypeIds = ConcurrentHashMap.newKeySet(); private final Set processedFieldsIds = ConcurrentHashMap.newKeySet(); protected final Map methods = new ConcurrentHashMap<>(); private final Map constants = new ConcurrentHashMap<>(); private final List loadPaths; - /** - * Map from a missing constant id to all the constants that depend on it. A constant is missing - * if its {@link AnalysisType} was not created yet, but its parent constant was already created. - * This can happen in both {@link ImageHeapInstance} and {@link ImageHeapObjectArray}, if the - * type of one field or one of the objects was not yet created. In this case, an AnalysisFuture - * that looks up the constants and assigns it in the missing place is created and stored in this - * map. - *

- * This map could be removed if the constants were created recursively using a - * {@link BaseLayerType}. However, it is easier to use this at the moment, as the types are - * loaded before the analysis. - */ - private final ConcurrentHashMap>> missingConstantTasks = new ConcurrentHashMap<>(); - /** - * Map from a missing type id to a set of tasks that depends on it. These tasks are created when - * a constant containing a DynamicHub cannot be relinked as the DynamicHub is not created yet. - * All the tasks corresponding to the missing type are executed when the DynamicHub is created. - */ - public final ConcurrentHashMap>> missingTypeTasks = new ConcurrentHashMap<>(); - private final Map claims = new ConcurrentHashMap<>(); /** * Map from a missing method id to all the constants that depend on it. A method is missing when * a constant contains a method pointer and the corresponding {@link AnalysisMethod} was not @@ -241,7 +214,6 @@ public class ImageLayerLoader { protected final ConcurrentHashMap>> missingMethodTasks = new ConcurrentHashMap<>(); private final Map typeToIdentifier = new HashMap<>(); protected final Set> heapScannerTasks = ConcurrentHashMap.newKeySet(); - private final Map> typeToConstants = new HashMap<>(); private final ImageLayerSnapshotUtil imageLayerSnapshotUtil; private final Map> baseLayerImageHeap = new ConcurrentHashMap<>(); protected final Map relinkedConstants = new ConcurrentHashMap<>(); @@ -321,62 +293,34 @@ private void loadLayerAnalysis0() { int tid = get(typeData, ID_TAG); typeToIdentifier.put(tid, typesCursor.getKey()); } - - /* - * The dependencies link between the constants is stored in typeToConstants allowing to - * easily look it up when creating the constants. - */ - EconomicMap constantsMap = get(jsonMap, CONSTANTS_TAG); - MapCursor constantsCursor = constantsMap.getEntries(); - while (constantsCursor.advance()) { - String stringId = constantsCursor.getKey(); - int id = Integer.parseInt(stringId); - EconomicMap constantData = getValue(constantsCursor); - int tid = get(constantData, TID_TAG); - typeToConstants.computeIfAbsent(tid, k -> new HashSet<>()).add(id); - } } - /** - * Creates all the constants from the base layer. - *

- * In the future, all the constants should not be eagerly created. All the constants of a given - * type should still be created via - * {@link ImageLayerLoader#loadAndRelinkTypeConstants(AnalysisType)} on the type creation, but - * the types should not be forcibly loaded. This mixed approach is used at the moment as it is - * easier to implement for the prototype, and it is useful for testing purposes. - */ - public void loadLayerConstants() { + public void loadTypes() { EconomicMap typesMap = get(jsonMap, TYPES_TAG); MapCursor typesCursor = typesMap.getEntries(); while (typesCursor.advance()) { EconomicMap typeData = getValue(typesCursor); int tid = get(typeData, ID_TAG); - /* - * Some types like Object or String are processed on demand (see - * loadAndRelinkTypeConstants) because they are created early in the build process. - */ - if (!processedTypeIds.contains(tid)) { - loadTypeConstants(typeData, tid); - } + loadType(typeData, tid); } } - private void loadTypeConstants(EconomicMap typeData, int tid) { + private void loadType(EconomicMap typeData, int tid) { + if (universe.isTypeCreated(tid)) { + return; + } + String name = get(typeData, CLASS_JAVA_NAME_TAG); Class clazz = lookupBaseLayerTypeInHostVM(name); if (clazz != null) { + metaAccess.lookupJavaType(clazz); + } + + if (!universe.isTypeCreated(tid)) { /* - * If the type can be looked up by name, the constants can be directly loaded and - * relinked. - */ - AnalysisType type = metaAccess.lookupJavaType(clazz); - loadAndRelinkTypeConstants(type); - } else { - /* - * If the type cannot be looked up by name, the constants are created using an - * incomplete AnalysisType, which uses a BaseLayerType in its wrapped field. + * If the type cannot be looked up by name, an incomplete AnalysisType, which uses a + * BaseLayerType in its wrapped field, has to be created */ String className = get(typeData, CLASS_NAME_TAG); int modifiers = get(typeData, MODIFIERS_TAG); @@ -402,16 +346,14 @@ private void loadTypeConstants(EconomicMap typeData, int tid) { BaseLayerType baseLayerType = new BaseLayerType(className, tid, modifiers, isInterface, isEnum, isInitialized, isLinked, sourceFileName, enclosingType, componentType, superClass, interfaces, objectType); - AnalysisType type = universe.lookup(baseLayerType); + universe.lookup(baseLayerType); + } - /* - * The constant cannot be relinked if the proper AnalysisType is not created yet. The - * constants are only created and the type is tracked to relink the constants if it is - * created later. - */ - loadTypeConstants(type); - processedTypeIds.add(tid); + if (!universe.isTypeCreated(tid)) { + loadType(typeData, tid); } + + guarantee(universe.isTypeCreated(tid)); } private ResolvedJavaType processType(Integer tid) { @@ -424,7 +366,7 @@ private ResolvedJavaType processType(Integer tid) { * and the set is queried before calling loadTypeConstants in other places. */ EconomicMap typesMap = get(jsonMap, TYPES_TAG); - loadTypeConstants(get(typesMap, typeToIdentifier.get(tid)), tid); + loadType(get(typesMap, typeToIdentifier.get(tid)), tid); } guarantee(universe.isTypeCreated(tid)); type = universe.getType(tid).getWrapped(); @@ -432,6 +374,20 @@ private ResolvedJavaType processType(Integer tid) { return type; } + /** + * Creates all the constants from the base layer. + */ + public void loadLayerConstants() { + EconomicMap constantsMap = get(jsonMap, CONSTANTS_TAG); + MapCursor constantsCursor = constantsMap.getEntries(); + while (constantsCursor.advance()) { + int id = Integer.parseInt(constantsCursor.getKey()); + if (!constants.containsKey(id)) { + createConstant(constantsMap, id); + } + } + } + /** * Returns the type id of the given type in the base layer if it exists. This makes the link * between the base layer and the extension layer as the id is used to determine which constant @@ -508,58 +464,6 @@ private static Class lookupPrimitiveClass(String type) { }; } - public void loadAndRelinkTypeConstants(AnalysisType type) { - if (!(type.getWrapped() instanceof BaseLayerType)) { - /* - * Types can be created before loadLayerConstants, in which case the constants have to - * be loaded before new constants are created. - */ - if (processedTypeIds.add(type.getId())) { - loadTypeConstants(type); - } - relinkTypeConstants(type); - } - } - - /** - * Creates all the constants for the given type. - */ - private void loadTypeConstants(AnalysisType type) { - EconomicMap constantsMap = get(jsonMap, CONSTANTS_TAG); - for (int constantId : typeToConstants.getOrDefault(type.getId(), Set.of())) { - createConstant(constantsMap, String.valueOf(constantId), type); - } - } - - /** - * Re-links the constants for the given type. - */ - private void relinkTypeConstants(AnalysisType analysisType) { - int id = analysisType.getId(); - Set> tasks = missingTypeTasks.getOrDefault(id, Set.of()); - if (!tasks.isEmpty()) { - Object claim = claims.put(analysisType, Thread.currentThread().getName()); - if (claim == null || !claim.equals(Thread.currentThread().getName())) { - for (AnalysisFuture task : tasks) { - if (claim == null) { - /* - * Only the thread that got the claim can execute the tasks. Some tasks can - * look up the AnalysisType, which causes this method to be executed again. - * The thread that got the claim will skip the recursive execution of the - * tasks, but another thread would try to execute them again and cause a - * deadlock. - */ - task.ensureDone(); - } else { - task.guardedGet(); - } - } - missingTypeTasks.remove(id); - claims.remove(analysisType); - } - } - } - /** * Returns the method id of the given method in the base layer if it exists. This makes the link * between the base layer and the extension layer as the id is used to determine the method used @@ -666,22 +570,24 @@ public void executeHeapScannerTasks() { } } - private void createConstant(EconomicMap constantsMap, String stringId, AnalysisType type) { - int id = Integer.parseInt(stringId); - EconomicMap baseLayerConstant = get(constantsMap, stringId); + private void createConstant(EconomicMap constantsMap, int id) { + EconomicMap baseLayerConstant = get(constantsMap, Integer.toString(id)); if (baseLayerConstant == null) { throw GraalError.shouldNotReachHere("The constant was not reachable in the base image"); } + int tid = get(baseLayerConstant, TID_TAG); + AnalysisType type = universe.getType(tid); String objectOffset = get(baseLayerConstant, OBJECT_OFFSET_TAG); String constantType = get(baseLayerConstant, CONSTANT_TYPE_TAG); switch (constantType) { case INSTANCE_TAG -> { List> instanceData = get(baseLayerConstant, DATA_TAG); - ImageHeapInstance imageHeapInstance = new ImageHeapInstance(type, null); - Object[] fieldValues = getReferencedValues(imageHeapInstance, instanceData, imageLayerSnapshotUtil.getRelinkedFields(type, metaAccess)); + JavaConstant hostedObject = getHostedObject(baseLayerConstant, type); + ImageHeapInstance imageHeapInstance = new ImageHeapInstance(type, hostedObject); addBaseLayerObject(type, id, imageHeapInstance, objectOffset); + Object[] fieldValues = getReferencedValues(constantsMap, imageHeapInstance, instanceData, imageLayerSnapshotUtil.getRelinkedFields(type, metaAccess)); imageHeapInstance.setFieldValues(fieldValues); - relinkConstant(imageHeapInstance, baseLayerConstant, type); + relinkConstant(imageHeapInstance); /* * Packages are normally rescanned when the DynamicHub is initialized. However, * since they are not relinked, the packages from the base layer will never be @@ -694,8 +600,8 @@ private void createConstant(EconomicMap constantsMap, String str case ARRAY_TAG -> { List> arrayData = get(baseLayerConstant, DATA_TAG); ImageHeapObjectArray imageHeapObjectArray = new ImageHeapObjectArray(type, null, arrayData.size()); - Object[] elementsValues = getReferencedValues(imageHeapObjectArray, arrayData, Set.of()); addBaseLayerObject(type, id, imageHeapObjectArray, objectOffset); + Object[] elementsValues = getReferencedValues(constantsMap, imageHeapObjectArray, arrayData, Set.of()); imageHeapObjectArray.setElementValues(elementsValues); } case PRIMITIVE_ARRAY_TAG -> { @@ -706,26 +612,23 @@ private void createConstant(EconomicMap constantsMap, String str } default -> throw GraalError.shouldNotReachHere("Unknown constant type: " + constantType); } - for (AnalysisFuture task : missingConstantTasks.getOrDefault(id, Set.of())) { - task.ensureDone(); - } - missingConstantTasks.remove(id); } - protected void relinkConstant(ImageHeapInstance imageHeapInstance, EconomicMap baseLayerConstant, AnalysisType analysisType) { + protected JavaConstant getHostedObject(EconomicMap baseLayerConstant, AnalysisType analysisType) { Class clazz = analysisType.getJavaClass(); boolean simulated = get(baseLayerConstant, SIMULATED_TAG); if (!simulated) { - relinkConstant(imageHeapInstance, baseLayerConstant, clazz); + return getHostedObject(baseLayerConstant, clazz); } + return null; } @SuppressWarnings("unchecked") - protected void relinkConstant(ImageHeapInstance imageHeapInstance, EconomicMap baseLayerConstant, Class clazz) { + protected JavaConstant getHostedObject(EconomicMap baseLayerConstant, Class clazz) { if (clazz.equals(String.class)) { String value = get(baseLayerConstant, VALUE_TAG); if (value != null) { - relinkConstant(value.intern(), imageHeapInstance); + return getHostedObject(value.intern()); } } else if (Enum.class.isAssignableFrom(clazz)) { String className = get(baseLayerConstant, ENUM_CLASS_TAG); @@ -733,14 +636,20 @@ protected void relinkConstant(ImageHeapInstance imageHeapInstance, EconomicMap enumValue = Enum.valueOf(enumClass.asSubclass(Enum.class), name); - relinkConstant(enumValue, imageHeapInstance); + return getHostedObject(enumValue); + } + return null; + } + + protected void relinkConstant(ImageHeapInstance imageHeapInstance) { + JavaConstant hostedObject = imageHeapInstance.getHostedObject(); + if (hostedObject != null) { + relinkedConstants.put(hostedObject, imageHeapInstance); } } - protected void relinkConstant(Object object, ImageHeapInstance imageHeapInstance) { - JavaConstant hostedObject = hostedValuesProvider.forObject(object); - imageHeapInstance.setHostedObject(hostedObject); - relinkedConstants.put(hostedObject, imageHeapInstance); + protected JavaConstant getHostedObject(Object object) { + return hostedValuesProvider.forObject(object); } @SuppressWarnings("unchecked") @@ -790,10 +699,9 @@ private static boolean[] getBooleans(List listObject) { return primitiveBooleans; } - private Object[] getReferencedValues(ImageHeapConstant parentConstant, List> data, Set positionsToRelink) { + private Object[] getReferencedValues(EconomicMap constantsMap, ImageHeapConstant parentConstant, List> data, Set positionsToRelink) { Object[] values = new Object[data.size()]; for (int position = 0; position < data.size(); ++position) { - int finalPosition = position; List constantData = data.get(position); String constantKind = (String) constantData.get(0); Object constantValue = constantData.get(1); @@ -804,34 +712,11 @@ private Object[] getReferencedValues(ImageHeapConstant parentConstant, List= 0) { boolean relink = positionsToRelink.contains(position); - ImageHeapConstant constant = constants.get(constantId); - if (constant != null) { - setConstant(parentConstant, values, position, constant, relink); - } else { - values[position] = new AnalysisFuture(() -> { - throw AnalysisError.shouldNotReachHere("The constant should be loaded before being accessed."); - }); - - AnalysisFuture linkingConstantTask = new AnalysisFuture<>(() -> { - ImageHeapConstant createdConstant = constants.get(constantId); - guarantee(createdConstant != null); - setConstant(parentConstant, values, finalPosition, createdConstant, relink); - return createdConstant; - }); - missingConstantTasks.computeIfAbsent(constantId, unused -> ConcurrentHashMap.newKeySet()).add(linkingConstantTask); - - /* If the constant was published in the meantime just set it. */ - if (constants.containsKey(constantId)) { - linkingConstantTask.ensureDone(); - /* - * It is safe to remove the entry from the map because the tasks that - * were added too late (e.g. after missingConstantTasks.getOrDefault) - * will all be executed here since the constant has been registered in - * the constants map at this point. - */ - missingConstantTasks.remove(constantId); - } + if (!constants.containsKey(constantId)) { + createConstant(constantsMap, constantId); } + ImageHeapConstant constant = constants.get(constantId); + setConstant(parentConstant, values, position, constant, relink); } else if (constantId == NULL_POINTER_CONSTANT) { values[position] = JavaConstant.NULL_POINTER; } else { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index ad5a5d65dede..b35877cff967 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -222,13 +222,6 @@ public JavaType lookupAllowUnresolved(JavaType rawType) { if (result == null) { result = createType(type); } - if (result.isInBaseLayer()) { - /* - * The constants can only be relinked after the type is registered as the dynamic hub is - * not available otherwise. - */ - getImageLayerLoader().loadAndRelinkTypeConstants(result); - } assert typesById[result.getId()].equals(result) : result; return result; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 0477ca1ac16f..91a03ecdbd81 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -225,9 +225,9 @@ import com.oracle.svm.hosted.code.CEntryPointData; import com.oracle.svm.hosted.code.CFunctionSubstitutionProcessor; import com.oracle.svm.hosted.code.CompileQueue; -import com.oracle.svm.hosted.code.ObjectFileTransformer; import com.oracle.svm.hosted.code.HostedRuntimeConfigurationBuilder; import com.oracle.svm.hosted.code.NativeMethodSubstitutionProcessor; +import com.oracle.svm.hosted.code.ObjectFileTransformer; import com.oracle.svm.hosted.code.RestrictHeapAccessCalleesImpl; import com.oracle.svm.hosted.code.SubstrateGraphMakerFactory; import com.oracle.svm.hosted.heap.ObservableImageHeapMapProviderImpl; @@ -824,10 +824,6 @@ protected boolean runPointsToAnalysis(String imageName, OptionValues options, De bb.getHostVM().getClassInitializationSupport().setConfigurationSealed(true); } - if (ImageLayerBuildingSupport.buildingExtensionLayer()) { - HostedImageLayerBuildingSupport.singleton().getLoader().loadLayerConstants(); - } - try (ReporterClosable c = ProgressReporter.singleton().printAnalysis(bb.getUniverse(), nativeLibraries.getLibraries())) { DuringAnalysisAccessImpl config = new DuringAnalysisAccessImpl(featureHandler, loader, bb, nativeLibraries, debug); try { @@ -1048,6 +1044,8 @@ protected void setupNativeImage(OptionValues options, Map baseLayerConstant, Class clazz) { + protected JavaConstant getHostedObject(EconomicMap baseLayerConstant, Class clazz) { if (clazz.equals(Class.class)) { Integer tid = get(baseLayerConstant, CLASS_ID_TAG); /* DynamicHub corresponding to $$TypeSwitch classes are not relinked */ if (tid != null) { - if (universe.isTypeCreated(tid)) { - relinkDynamicHub(imageHeapInstance, tid); - } else { - /* - * If the DynamicHub is not created yet, we create a task that will be executed - * on the DynamicHub creation - */ - AnalysisFuture task = new AnalysisFuture<>(() -> relinkDynamicHub(imageHeapInstance, tid)); - missingTypeTasks.computeIfAbsent(tid, unused -> ConcurrentHashMap.newKeySet()).add(task); - } + return getDynamicHub(tid); } - } else { - super.relinkConstant(imageHeapInstance, baseLayerConstant, clazz); } + return super.getHostedObject(baseLayerConstant, clazz); } - private void relinkDynamicHub(ImageHeapInstance imageHeapInstance, int tid) { + private JavaConstant getDynamicHub(int tid) { AnalysisType type = universe.getType(tid); DynamicHub hub = ((SVMHost) universe.hostVM()).dynamicHub(type); - relinkConstant(hub, imageHeapInstance); + return getHostedObject(hub); } @Override From 61a8c4de6e4d999480dd88512d574bab4099aae6 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Fri, 26 Apr 2024 12:03:21 +0200 Subject: [PATCH 2/2] Load base layer types and constant on-demand --- .../graal/pointsto/heap/ImageLayerLoader.java | 278 +++++++++--------- .../pointsto/heap/ImageLayerSnapshotUtil.java | 1 + .../graal/pointsto/heap/ImageLayerWriter.java | 10 +- .../graal/pointsto/meta/AnalysisType.java | 13 +- .../graal/pointsto/meta/BaseLayerType.java | 12 +- .../svm/hosted/NativeImageGenerator.java | 2 - .../svm/hosted/heap/SVMImageLayerLoader.java | 30 ++ .../svm/hosted/heap/SVMImageLayerWriter.java | 5 +- 8 files changed, 203 insertions(+), 148 deletions(-) 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 feed005b0f74..184d944194a7 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 @@ -29,6 +29,7 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.COMPONENT_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANTS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANTS_TO_RELINK_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANT_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.DATA_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ENCLOSING_TYPE_TAG; @@ -83,7 +84,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; -import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; @@ -95,6 +95,7 @@ import com.oracle.graal.pointsto.util.AnalysisFuture; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.core.common.SuppressFBWarnings; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.util.json.JSONParser; import jdk.vm.ci.meta.JavaConstant; @@ -202,9 +203,11 @@ */ public class ImageLayerLoader { private final Set processedFieldsIds = ConcurrentHashMap.newKeySet(); + private final Map types = new ConcurrentHashMap<>(); protected final Map methods = new ConcurrentHashMap<>(); - private final Map constants = new ConcurrentHashMap<>(); + protected final Map constants = new ConcurrentHashMap<>(); private final List loadPaths; + private final Map baseLayerTypes = new ConcurrentHashMap<>(); /** * Map from a missing method id to all the constants that depend on it. A method is missing when * a constant contains a method pointer and the corresponding {@link AnalysisMethod} was not @@ -215,8 +218,9 @@ public class ImageLayerLoader { private final Map typeToIdentifier = new HashMap<>(); protected final Set> heapScannerTasks = ConcurrentHashMap.newKeySet(); private final ImageLayerSnapshotUtil imageLayerSnapshotUtil; - private final Map> baseLayerImageHeap = new ConcurrentHashMap<>(); - protected final Map relinkedConstants = new ConcurrentHashMap<>(); + protected final Map typeToConstant = new ConcurrentHashMap<>(); + protected final Map stringToConstant = new ConcurrentHashMap<>(); + protected final Map, Integer> enumToConstant = new ConcurrentHashMap<>(); protected final Map objectOffsets = new ConcurrentHashMap<>(); protected final Map fieldLocations = new ConcurrentHashMap<>(); protected AnalysisUniverse universe; @@ -293,99 +297,100 @@ private void loadLayerAnalysis0() { int tid = get(typeData, ID_TAG); typeToIdentifier.put(tid, typesCursor.getKey()); } - } - public void loadTypes() { - EconomicMap typesMap = get(jsonMap, TYPES_TAG); - MapCursor typesCursor = typesMap.getEntries(); - while (typesCursor.advance()) { - EconomicMap typeData = getValue(typesCursor); - int tid = get(typeData, ID_TAG); - loadType(typeData, tid); + EconomicMap constantsMap = get(jsonMap, CONSTANTS_TAG); + List constantsToRelink = get(jsonMap, CONSTANTS_TO_RELINK_TAG); + for (int id : constantsToRelink) { + EconomicMap constantData = get(constantsMap, String.valueOf(id)); + prepareConstantRelinking(constantData, id); } } - private void loadType(EconomicMap typeData, int tid) { - if (universe.isTypeCreated(tid)) { - return; + @SuppressWarnings("unchecked") + protected void prepareConstantRelinking(EconomicMap constantData, int id) { + String value = get(constantData, VALUE_TAG); + if (value != null) { + stringToConstant.put(value, id); } + String className = get(constantData, ENUM_CLASS_TAG); + if (className != null) { + Class enumClass = ReflectionUtil.lookupClass(false, className); + String name = get(constantData, ENUM_NAME_TAG); + /* asSubclass produces an "unchecked" warning */ + Enum enumValue = Enum.valueOf(enumClass.asSubclass(Enum.class), name); + enumToConstant.put(enumValue, id); + } + } + + private void loadType(EconomicMap typeData) { + int tid = get(typeData, ID_TAG); + String name = get(typeData, CLASS_JAVA_NAME_TAG); Class clazz = lookupBaseLayerTypeInHostVM(name); if (clazz != null) { + /* + * When looking up the class by name, the host VM will create the corresponding + * AnalysisType. During this process, the method lookupHostedTypeInBaseLayer will be + * called to see if the type already exists in the base layer. If it is the case, the id + * from the base layer will be reused and the ImageLayerLoader#types map will be + * populated. + */ metaAccess.lookupJavaType(clazz); } - if (!universe.isTypeCreated(tid)) { + if (!types.containsKey(tid)) { /* * If the type cannot be looked up by name, an incomplete AnalysisType, which uses a * BaseLayerType in its wrapped field, has to be created */ - 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 isLinked = get(typeData, IS_LINKED_TAG); - String sourceFileName = get(typeData, SOURCE_FILE_NAME_TAG); + 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 isLinked = get(typeData, IS_LINKED_TAG); + String sourceFileName = get(typeData, SOURCE_FILE_NAME_TAG); - Integer enclosingTid = get(typeData, ENCLOSING_TYPE_TAG); - ResolvedJavaType enclosingType = processType(enclosingTid); + Integer enclosingTid = get(typeData, ENCLOSING_TYPE_TAG); + ResolvedJavaType enclosingType = getResolvedJavaType(enclosingTid); - Integer componentTid = get(typeData, COMPONENT_TYPE_TAG); - ResolvedJavaType componentType = processType(componentTid); + Integer componentTid = get(typeData, COMPONENT_TYPE_TAG); + ResolvedJavaType componentType = getResolvedJavaType(componentTid); - Integer superClassTid = get(typeData, SUPER_CLASS_TAG); - ResolvedJavaType superClass = processType(superClassTid); + Integer superClassTid = get(typeData, SUPER_CLASS_TAG); + ResolvedJavaType superClass = getResolvedJavaType(superClassTid); - List interfacesIds = get(typeData, INTERFACES_TAG); - ResolvedJavaType[] interfaces = interfacesIds.stream().map(this::processType).toList().toArray(new ResolvedJavaType[0]); + List interfacesIds = get(typeData, INTERFACES_TAG); + ResolvedJavaType[] interfaces = interfacesIds.stream().map(this::getResolvedJavaType).toList().toArray(new ResolvedJavaType[0]); - ResolvedJavaType objectType = universe.getOriginalMetaAccess().lookupJavaType(Object.class); - - BaseLayerType baseLayerType = new BaseLayerType(className, tid, modifiers, isInterface, isEnum, isInitialized, isLinked, sourceFileName, enclosingType, componentType, superClass, - interfaces, objectType); - universe.lookup(baseLayerType); - } + ResolvedJavaType objectType = universe.getOriginalMetaAccess().lookupJavaType(Object.class); - if (!universe.isTypeCreated(tid)) { - loadType(typeData, tid); + return new BaseLayerType(className, tid, modifiers, isInterface, isEnum, isInitialized, isLinked, sourceFileName, enclosingType, componentType, superClass, interfaces, objectType); + }); + 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); } - - guarantee(universe.isTypeCreated(tid)); } - private ResolvedJavaType processType(Integer tid) { - ResolvedJavaType type = null; - if (tid != null) { - if (!universe.isTypeCreated(tid)) { - /* - * The type is loaded if it was not created yet. Calling loadTypeConstants does not - * cause issue about duplicated constants as it adds the type id in processedTypeIds - * and the set is queried before calling loadTypeConstants in other places. - */ - EconomicMap typesMap = get(jsonMap, TYPES_TAG); - loadType(get(typesMap, typeToIdentifier.get(tid)), tid); - } - guarantee(universe.isTypeCreated(tid)); - type = universe.getType(tid).getWrapped(); - } - return type; + private ResolvedJavaType getResolvedJavaType(Integer tid) { + return tid == null ? null : getAnalysisType(tid).getWrapped(); } - /** - * Creates all the constants from the base layer. - */ - public void loadLayerConstants() { - EconomicMap constantsMap = get(jsonMap, CONSTANTS_TAG); - MapCursor constantsCursor = constantsMap.getEntries(); - while (constantsCursor.advance()) { - int id = Integer.parseInt(constantsCursor.getKey()); - if (!constants.containsKey(id)) { - createConstant(constantsMap, id); - } + protected AnalysisType getAnalysisType(Integer tid) { + if (!types.containsKey(tid)) { + EconomicMap typesMap = get(jsonMap, TYPES_TAG); + loadType(get(typesMap, typeToIdentifier.get(tid))); } + guarantee(types.containsKey(tid), "Type with id %d was not correctly loaded.", tid); + /* + * The type needs to be looked up because it ensures the type is completely created, as the + * types Map is populated before the type is created. + */ + return universe.lookup(types.get(tid).getWrapped()); } /** @@ -395,22 +400,18 @@ public void loadLayerConstants() { */ public int lookupHostedTypeInBaseLayer(AnalysisType type) { int id = getBaseLayerTypeId(type); - if (id == -1 || universe.isTypeCreated(id)) { + if (id == -1 || types.putIfAbsent(id, type) != null) { /* A complete type is treated as a different type than its incomplete version */ return -1; } return id; } - public int getBaseLayerTypeId(AnalysisType type) { - String typeIdentifier = imageLayerSnapshotUtil.getTypeIdentifier(type); - if (type.getWrapped() instanceof BaseLayerType) { - /* - * If the type is from the base layer, we remove the BASE_LAYER_SUFFIX from the - * typeIdentifier as the name would not be found in the map otherwise. - */ - typeIdentifier = typeIdentifier.substring(0, typeIdentifier.length() - BaseLayerType.BASE_LAYER_SUFFIX.length() + 1); + private int getBaseLayerTypeId(AnalysisType type) { + if (type.getWrapped() instanceof BaseLayerType baseLayerType) { + return baseLayerType.getBaseLayerId(); } + String typeIdentifier = imageLayerSnapshotUtil.getTypeIdentifier(type); EconomicMap typeData = getElementData(TYPES_TAG, typeIdentifier); if (typeData == null) { /* The type was not reachable in the base image */ @@ -570,13 +571,18 @@ public void executeHeapScannerTasks() { } } - private void createConstant(EconomicMap constantsMap, int id) { + protected ImageHeapConstant getOrCreateConstant(EconomicMap constantsMap, int id) { + if (constants.containsKey(id)) { + return constants.get(id); + } EconomicMap baseLayerConstant = get(constantsMap, Integer.toString(id)); if (baseLayerConstant == null) { throw GraalError.shouldNotReachHere("The constant was not reachable in the base image"); } + int tid = get(baseLayerConstant, TID_TAG); - AnalysisType type = universe.getType(tid); + AnalysisType type = getAnalysisType(tid); + String objectOffset = get(baseLayerConstant, OBJECT_OFFSET_TAG); String constantType = get(baseLayerConstant, CONSTANT_TYPE_TAG); switch (constantType) { @@ -584,34 +590,27 @@ private void createConstant(EconomicMap constantsMap, int id) { List> instanceData = get(baseLayerConstant, DATA_TAG); JavaConstant hostedObject = getHostedObject(baseLayerConstant, type); ImageHeapInstance imageHeapInstance = new ImageHeapInstance(type, hostedObject); - addBaseLayerObject(type, id, imageHeapInstance, objectOffset); Object[] fieldValues = getReferencedValues(constantsMap, imageHeapInstance, instanceData, imageLayerSnapshotUtil.getRelinkedFields(type, metaAccess)); imageHeapInstance.setFieldValues(fieldValues); - relinkConstant(imageHeapInstance); - /* - * Packages are normally rescanned when the DynamicHub is initialized. However, - * since they are not relinked, the packages from the base layer will never be - * marked as reachable without doing so manually. - */ - if (imageHeapInstance.getType().getJavaClass().equals(Package.class)) { - universe.getHeapScanner().doScan(imageHeapInstance); - } + addBaseLayerObject(id, imageHeapInstance, objectOffset); } case ARRAY_TAG -> { List> arrayData = get(baseLayerConstant, DATA_TAG); ImageHeapObjectArray imageHeapObjectArray = new ImageHeapObjectArray(type, null, arrayData.size()); - addBaseLayerObject(type, id, imageHeapObjectArray, objectOffset); Object[] elementsValues = getReferencedValues(constantsMap, imageHeapObjectArray, arrayData, Set.of()); imageHeapObjectArray.setElementValues(elementsValues); + addBaseLayerObject(id, imageHeapObjectArray, objectOffset); } case PRIMITIVE_ARRAY_TAG -> { List primitiveData = get(baseLayerConstant, DATA_TAG); Object array = getArray(type.getComponentType().getJavaKind(), primitiveData); ImageHeapPrimitiveArray imageHeapPrimitiveArray = new ImageHeapPrimitiveArray(type, null, array, primitiveData.size()); - addBaseLayerObject(type, id, imageHeapPrimitiveArray, objectOffset); + addBaseLayerObject(id, imageHeapPrimitiveArray, objectOffset); } default -> throw GraalError.shouldNotReachHere("Unknown constant type: " + constantType); } + + return constants.get(id); } protected JavaConstant getHostedObject(EconomicMap baseLayerConstant, AnalysisType analysisType) { @@ -641,13 +640,6 @@ protected JavaConstant getHostedObject(EconomicMap baseLayerCons return null; } - protected void relinkConstant(ImageHeapInstance imageHeapInstance) { - JavaConstant hostedObject = imageHeapInstance.getHostedObject(); - if (hostedObject != null) { - relinkedConstants.put(hostedObject, imageHeapInstance); - } - } - protected JavaConstant getHostedObject(Object object) { return hostedValuesProvider.forObject(object); } @@ -712,11 +704,12 @@ private Object[] getReferencedValues(EconomicMap constantsMap, I int constantId = (int) constantValue; if (constantId >= 0) { boolean relink = positionsToRelink.contains(position); - if (!constants.containsKey(constantId)) { - createConstant(constantsMap, constantId); - } - ImageHeapConstant constant = constants.get(constantId); - setConstant(parentConstant, values, position, constant, relink); + int finalPosition = position; + values[position] = new AnalysisFuture<>(() -> { + ImageHeapConstant constant = getOrCreateConstant(constantsMap, constantId); + setReferencedConstant(parentConstant, values, finalPosition, constant, relink); + return constant; + }); } else if (constantId == NULL_POINTER_CONSTANT) { values[position] = JavaConstant.NULL_POINTER; } else { @@ -745,20 +738,16 @@ protected boolean delegateProcessing(String constantType, Object constantValue, return false; } - private void setConstant(ImageHeapConstant parentConstant, Object[] values, int i, ImageHeapConstant constant, boolean relink) { + private void setReferencedConstant(ImageHeapConstant parentConstant, Object[] values, int i, ImageHeapConstant constant, boolean relink) { /* - * Install future that knows how to relink the constant. At this point we can assume that - * the parent is already linked if it will ever be linked. + * At this point we can assume that the parent is already linked if it will ever be linked. */ - values[i] = new AnalysisFuture<>(() -> { - values[i] = constant; - ensureHubInitialized(constant); - ensureHubInitialized(parentConstant); - if (relink) { - universe.getHeapScanner().linkBaseLayerValue(parentConstant, i, constant); - } - return constant; - }); + values[i] = constant; + ensureHubInitialized(constant); + ensureHubInitialized(parentConstant); + if (relink) { + universe.getHeapScanner().linkBaseLayerValue(parentConstant, i, constant); + } } /** @@ -795,12 +784,21 @@ private static double getDouble(Object value) { return Double.longBitsToDouble((long) value); } - private void addBaseLayerObject(AnalysisType type, int id, ImageHeapConstant heapObj, String objectOffset) { + private void addBaseLayerObject(int id, ImageHeapConstant heapObj, String objectOffset) { heapObj.markInBaseLayer(); - constants.put(id, heapObj); - baseLayerImageHeap.computeIfAbsent(type, t -> ConcurrentHashMap.newKeySet()).add(heapObj); - if (objectOffset != null) { - objectOffsets.put(heapObj.constantData.id, Long.parseLong(objectOffset)); + ImageHeapConstant constant = constants.putIfAbsent(id, heapObj); + if (constant == null) { + /* + * Packages are normally rescanned when the DynamicHub is initialized. However, since + * they are not relinked, the packages from the base layer will never be marked as + * reachable without doing so manually. + */ + if (heapObj.getType().getJavaClass().equals(Package.class)) { + universe.getHeapScanner().doScan(heapObj); + } + if (objectOffset != null) { + objectOffsets.put(heapObj.constantData.id, Long.parseLong(objectOffset)); + } } } @@ -826,20 +824,38 @@ protected static T cast(Object object) { } public boolean hasValueForConstant(JavaConstant javaConstant) { - /* - * Look up the type of the constant to ensure all the base layer constants of this type are - * created as it could be missed otherwise. - */ - try { - universe.getBigbang().getMetaAccess().lookupJavaType(javaConstant); - } catch (UnsupportedFeatureException e) { - /* Ignore unsupported type errors. */ + Object object = hostedValuesProvider.asObject(Object.class, javaConstant); + return hasValueForObject(object); + } + + @SuppressFBWarnings(value = "ES", justification = "Reference equality check needed to detect intern status") + protected boolean hasValueForObject(Object object) { + if (object instanceof String string) { + return stringToConstant.containsKey(string) && string.intern() == string; + } else if (object instanceof Enum) { + return enumToConstant.containsKey(object); } - return relinkedConstants.containsKey(javaConstant); + return false; } public ImageHeapConstant getValueForConstant(JavaConstant javaConstant) { - return relinkedConstants.get(javaConstant); + Object object = hostedValuesProvider.asObject(Object.class, javaConstant); + return getValueForObject(object); + } + + protected ImageHeapConstant getValueForObject(Object object) { + if (object instanceof String string) { + int id = stringToConstant.get(string); + return getOrCreateConstant(id); + } else if (object instanceof Enum) { + int id = enumToConstant.get(object); + return getOrCreateConstant(id); + } + throw AnalysisError.shouldNotReachHere("The constant was not in the persisted heap."); + } + + protected ImageHeapConstant getOrCreateConstant(int id) { + return getOrCreateConstant(get(jsonMap, CONSTANTS_TAG), id); } public void setMetaAccess(AnalysisMetaAccess metaAccess) { @@ -873,7 +889,7 @@ public ImageHeapConstant getBaseLayerStaticObjectFields() { private ImageHeapConstant getTaggedImageHeapConstant(String tag) { int id = get(jsonMap, tag); - return constants.get(id); + return getOrCreateConstant(id); } public long getImageHeapSize() { 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 311b3354cce6..e2202f478b04 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 @@ -58,6 +58,7 @@ public class ImageLayerSnapshotUtil { public static final String SUPER_CLASS_TAG = "super class"; public static final String INTERFACES_TAG = "interfaces"; 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"; public static final String IDENTITY_HASH_CODE_TAG = "identityHashCode"; public static final String ID_TAG = "id"; 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 733db7287ed9..be98c8411cc7 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 @@ -29,6 +29,7 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.COMPONENT_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANTS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANTS_TO_RELINK_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANT_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.DATA_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ENCLOSING_TYPE_TAG; @@ -102,6 +103,7 @@ public class ImageLayerWriter { private String[] imageInternedStrings; protected EconomicMap jsonMap = EconomicMap.create(); + protected List constantsToRelink; FileInfo fileInfo; private record FileInfo(Path layerSnapshotPath, String fileName, String suffix) { @@ -113,6 +115,7 @@ public ImageLayerWriter() { public ImageLayerWriter(ImageLayerSnapshotUtil imageLayerSnapshotUtil) { this.imageLayerSnapshotUtil = imageLayerSnapshotUtil; + this.constantsToRelink = new ArrayList<>(); } public void setImageInternedStrings(String[] imageInternedStrings) { @@ -190,6 +193,7 @@ public void persistAnalysisInfo(Universe hostedUniverse, AnalysisUniverse analys } } jsonMap.put(CONSTANTS_TAG, constantsMap); + jsonMap.put(CONSTANTS_TO_RELINK_TAG, constantsToRelink); } /** @@ -319,12 +323,12 @@ public void persistConstantRelinkingInfo(EconomicMap constantMap boolean simulated = hostedObject == null; constantMap.put(SIMULATED_TAG, simulated); if (!simulated) { - persistConstantRelinkingInfo(constantMap, bb, clazz, hostedObject); + persistConstantRelinkingInfo(constantMap, bb, clazz, hostedObject, imageHeapConstant.constantData.id); } } @SuppressFBWarnings(value = "ES", justification = "Reference equality check needed to detect intern status") - public void persistConstantRelinkingInfo(EconomicMap constantMap, BigBang bb, Class clazz, JavaConstant hostedObject) { + public void persistConstantRelinkingInfo(EconomicMap constantMap, BigBang bb, Class clazz, JavaConstant hostedObject, int id) { if (clazz.equals(String.class)) { String value = bb.getSnippetReflectionProvider().asObject(String.class, hostedObject); int stringIndex = Arrays.binarySearch(imageInternedStrings, value); @@ -334,11 +338,13 @@ public void persistConstantRelinkingInfo(EconomicMap constantMap */ if (stringIndex >= 0 && imageInternedStrings[stringIndex] == value) { constantMap.put(VALUE_TAG, value); + constantsToRelink.add(id); } } else if (Enum.class.isAssignableFrom(clazz)) { Enum value = bb.getSnippetReflectionProvider().asObject(Enum.class, hostedObject); constantMap.put(ENUM_CLASS_TAG, value.getDeclaringClass().getName()); constantMap.put(ENUM_NAME_TAG, value.name()); + constantsToRelink.add(id); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index 117c83b260c3..e345c0097128 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -274,10 +274,7 @@ public AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKi } /* Set id after accessing super types, so that all these types get a lower id number. */ - if (wrapped instanceof BaseLayerType baseLayerType) { - this.id = baseLayerType.getId(); - this.isInBaseLayer = true; - } else if (universe.hostVM().useBaseLayer()) { + if (universe.hostVM().useBaseLayer()) { int tid = universe.getImageLayerLoader().lookupHostedTypeInBaseLayer(this); if (tid != -1) { /* @@ -288,7 +285,13 @@ public AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKi this.isInBaseLayer = true; } else { this.id = universe.computeNextTypeId(); - this.isInBaseLayer = false; + /* + * If both the BaseLayerType and the complete type are created at the same time, + * there can be a race for the base layer id. It is possible that the complete type + * gets the base layer id even though the BaseLayerType is created. In this case, + * the AnalysisType should still be marked as isInBaseLayer. + */ + this.isInBaseLayer = wrapped instanceof BaseLayerType; } } else { this.id = universe.computeNextTypeId(); 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 4885d09a7d2f..ecc7251f98c7 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 @@ -51,7 +51,7 @@ public class BaseLayerType implements ResolvedJavaType, OriginalClassProvider { */ public static final String BASE_LAYER_SUFFIX = "_BaseLayer;"; private final String name; - private final int id; + private final int baseLayerId; private final int modifiers; private final boolean isInterface; private final boolean isEnum; @@ -64,10 +64,10 @@ public class BaseLayerType implements ResolvedJavaType, OriginalClassProvider { private final ResolvedJavaType[] interfaces; private final ResolvedJavaType objectType; - public BaseLayerType(String name, int id, int modifiers, boolean isInterface, boolean isEnum, boolean isInitialized, boolean isLinked, String sourceFileName, ResolvedJavaType enclosingType, - ResolvedJavaType componentType, ResolvedJavaType superClass, ResolvedJavaType[] interfaces, ResolvedJavaType objectType) { + public BaseLayerType(String name, int baseLayerId, int modifiers, boolean isInterface, boolean isEnum, boolean isInitialized, boolean isLinked, String sourceFileName, + ResolvedJavaType enclosingType, ResolvedJavaType componentType, ResolvedJavaType superClass, ResolvedJavaType[] interfaces, ResolvedJavaType objectType) { this.name = name.substring(0, name.length() - 1) + BASE_LAYER_SUFFIX; - this.id = id; + this.baseLayerId = baseLayerId; this.modifiers = modifiers; this.isInterface = isInterface; this.isEnum = isEnum; @@ -328,7 +328,7 @@ public ResolvedJavaType unwrapTowardsOriginalType() { return objectType; } - public int getId() { - return id; + public int getBaseLayerId() { + return baseLayerId; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 91a03ecdbd81..e2938db1e891 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -1044,8 +1044,6 @@ protected void setupNativeImage(OptionValues options, Map loaderPaths) { dynamicHubArrayHubField = ReflectionUtil.lookupField(DynamicHub.class, "arrayHub"); } + @Override + protected void prepareConstantRelinking(EconomicMap constantData, int id) { + Integer tid = get(constantData, CLASS_ID_TAG); + if (tid != null) { + typeToConstant.put(tid, id); + } else { + super.prepareConstantRelinking(constantData, id); + } + } + @Override protected boolean delegateProcessing(String constantType, Object constantValue, Object[] values, int i) { if (constantType.equals(METHOD_POINTER_TAG)) { @@ -124,6 +134,7 @@ protected JavaConstant getHostedObject(EconomicMap baseLayerCons } private JavaConstant getDynamicHub(int tid) { + getAnalysisType(tid); AnalysisType type = universe.getType(tid); DynamicHub hub = ((SVMHost) universe.hostVM()).dynamicHub(type); return getHostedObject(hub); @@ -175,6 +186,25 @@ public void rescanHub(AnalysisType type, Object hubObject) { } } + @Override + protected boolean hasValueForObject(Object object) { + if (object instanceof DynamicHub dynamicHub) { + AnalysisType type = ((SVMHost) universe.hostVM()).lookupType(dynamicHub); + return typeToConstant.containsKey(type.getId()); + } + return super.hasValueForObject(object); + } + + @Override + protected ImageHeapConstant getValueForObject(Object object) { + if (object instanceof DynamicHub dynamicHub) { + AnalysisType type = ((SVMHost) universe.hostVM()).lookupType(dynamicHub); + int id = typeToConstant.get(type.getId()); + return getOrCreateConstant(id); + } + return super.getValueForObject(object); + } + public Map>> loadImageSingletons(Object forbiddenObject) { loadJsonMap(); return loadImageSingletons0(forbiddenObject); 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 45fd833124cb..65421348963b 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 @@ -158,7 +158,7 @@ protected void persistConstant(AnalysisUniverse analysisUniverse, ImageHeapConst } @Override - public void persistConstantRelinkingInfo(EconomicMap constantMap, BigBang bb, Class clazz, JavaConstant hostedObject) { + public void persistConstantRelinkingInfo(EconomicMap constantMap, BigBang bb, Class clazz, JavaConstant hostedObject, int id) { ResolvedJavaType type = bb.getConstantReflectionProvider().asJavaType(hostedObject); if (type instanceof AnalysisType analysisType) { /* @@ -170,9 +170,10 @@ public void persistConstantRelinkingInfo(EconomicMap constantMap */ if (!isTypeSwitch(analysisType)) { constantMap.put(CLASS_ID_TAG, analysisType.getId()); + constantsToRelink.add(id); } } else { - super.persistConstantRelinkingInfo(constantMap, bb, clazz, hostedObject); + super.persistConstantRelinkingInfo(constantMap, bb, clazz, hostedObject, id); } }