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..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; @@ -74,7 +75,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; @@ -84,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; @@ -96,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; @@ -114,6 +114,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 +202,12 @@ * 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(); + 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; - /** - * 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<>(); + 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 @@ -241,10 +217,10 @@ 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<>(); + 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; @@ -322,114 +298,99 @@ private void loadLayerAnalysis0() { 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); + List constantsToRelink = get(jsonMap, CONSTANTS_TO_RELINK_TAG); + for (int id : constantsToRelink) { + EconomicMap constantData = get(constantsMap, String.valueOf(id)); + prepareConstantRelinking(constantData, 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() { - 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); - } + @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 loadTypeConstants(EconomicMap typeData, int tid) { + 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) { /* - * If the type can be looked up by name, the constants can be directly loaded and - * relinked. + * 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. */ - AnalysisType type = metaAccess.lookupJavaType(clazz); - loadAndRelinkTypeConstants(type); - } else { + metaAccess.lookupJavaType(clazz); + } + + if (!types.containsKey(tid)) { /* - * 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); - 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); + ResolvedJavaType objectType = universe.getOriginalMetaAccess().lookupJavaType(Object.class); - BaseLayerType baseLayerType = new BaseLayerType(className, tid, modifiers, isInterface, isEnum, isInitialized, isLinked, sourceFileName, enclosingType, componentType, superClass, - interfaces, objectType); + 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); + } + } - /* - * 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); - } - } - - 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); - loadTypeConstants(get(typesMap, typeToIdentifier.get(tid)), tid); - } - guarantee(universe.isTypeCreated(tid)); - type = universe.getType(tid).getWrapped(); + private ResolvedJavaType getResolvedJavaType(Integer tid) { + return tid == null ? null : getAnalysisType(tid).getWrapped(); + } + + protected AnalysisType getAnalysisType(Integer tid) { + if (!types.containsKey(tid)) { + EconomicMap typesMap = get(jsonMap, TYPES_TAG); + loadType(get(typesMap, typeToIdentifier.get(tid))); } - return type; + 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()); } /** @@ -439,22 +400,18 @@ private ResolvedJavaType processType(Integer tid) { */ 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 */ @@ -508,58 +465,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,66 +571,63 @@ public void executeHeapScannerTasks() { } } - private void createConstant(EconomicMap constantsMap, String stringId, AnalysisType type) { - int id = Integer.parseInt(stringId); - EconomicMap baseLayerConstant = get(constantsMap, stringId); + 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 = getAnalysisType(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)); - addBaseLayerObject(type, id, imageHeapInstance, objectOffset); + JavaConstant hostedObject = getHostedObject(baseLayerConstant, type); + ImageHeapInstance imageHeapInstance = new ImageHeapInstance(type, hostedObject); + Object[] fieldValues = getReferencedValues(constantsMap, imageHeapInstance, instanceData, imageLayerSnapshotUtil.getRelinkedFields(type, metaAccess)); imageHeapInstance.setFieldValues(fieldValues); - relinkConstant(imageHeapInstance, baseLayerConstant, type); - /* - * 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()); - Object[] elementsValues = getReferencedValues(imageHeapObjectArray, arrayData, Set.of()); - 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); } - for (AnalysisFuture task : missingConstantTasks.getOrDefault(id, Set.of())) { - task.ensureDone(); - } - missingConstantTasks.remove(id); + + return constants.get(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 +635,13 @@ 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(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 +691,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 +704,12 @@ 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); - } - } + 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 { @@ -860,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); + } } /** @@ -910,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)); + } } } @@ -941,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) { @@ -988,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/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.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 0477ca1ac16f..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 @@ -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 { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index a70849592a16..fb979c1e927e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -64,6 +64,7 @@ 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.BaseLayerType; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisGraphDecoder; import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisPolicy; @@ -408,7 +409,11 @@ private DynamicHub createHub(AnalysisType type) { ClassLoader hubClassLoader = javaClass.getClassLoader(); /* Class names must be interned strings according to the Java specification. */ - String className = type.toClassName().intern(); + String name = type.toClassName(); + if (name.endsWith(BaseLayerType.BASE_LAYER_SUFFIX.substring(0, BaseLayerType.BASE_LAYER_SUFFIX.length() - 1))) { + name = name.substring(0, name.length() - BaseLayerType.BASE_LAYER_SUFFIX.length() + 1); + } + String className = name.intern(); /* * There is no need to have file names and simple binary names as interned strings. So we * perform our own de-duplication. 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 7dcfd68257ff..6d140e744e7a 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 @@ -74,6 +74,16 @@ public SVMImageLayerLoader(List 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)) { @@ -112,31 +122,22 @@ protected boolean delegateProcessing(String constantType, Object constantValue, } @Override - protected void relinkConstant(ImageHeapInstance imageHeapInstance, EconomicMap 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) { + getAnalysisType(tid); AnalysisType type = universe.getType(tid); DynamicHub hub = ((SVMHost) universe.hostVM()).dynamicHub(type); - relinkConstant(hub, imageHeapInstance); + return getHostedObject(hub); } @Override @@ -185,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); } }