diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java index 74ea7f0c3201..3a169a648e96 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java @@ -135,7 +135,9 @@ public AbstractAnalysisEngine(OptionValues options, AnalysisUniverse universe, H this.snippetReflectionProvider = snippetReflectionProvider; this.constantReflectionProvider = constantReflectionProvider; this.wordTypes = wordTypes; - classInclusionPolicy.setBigBang(this); + if (classInclusionPolicy != null) { + classInclusionPolicy.setBigBang(this); + } this.classInclusionPolicy = classInclusionPolicy; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java index 2d9105dd0717..4004df5235b5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java @@ -180,6 +180,30 @@ public boolean isClosed(AnalysisType type) { return type.isArray() || type.isLeaf() || hostVM.isCoreType(type); } + /** + * Determine if the field is by default closed in an open-world analysis. A field is considered + * closed if it cannot be written *directly* from the open world. This applies for example to + * core VM fields whose writes should only come from code included in the base image. + *

+ * Note that flows of closed fields can still contain values coming from the open world. For + * example the field could be the target of a field-store that is reached directly from a + * parameter of an entry point method. Similarly, the field could be unsafe accessed. + */ + public boolean isClosed(ResolvedJavaField field) { + if (hostVM.isClosedTypeWorld()) { + /* In a closed type world all fields are closed. */ + return true; + } + if (hostVM.isAlwaysClosedField(field)) { + return true; + } + if (hostVM.isCoreType(field.getDeclaringClass())) { + /* All the other svm.core fields are by default closed. */ + return true; + } + return false; + } + @Override protected CompletionExecutor.Timing getTiming() { return timing; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java index 46e527bb106d..52adbef0f881 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java @@ -182,7 +182,7 @@ public void checkType(ResolvedJavaType type, AnalysisUniverse universe) { public void onTypeInstantiated(BigBang bb, AnalysisType type) { } - public boolean isCoreType(@SuppressWarnings("unused") AnalysisType type) { + public boolean isCoreType(@SuppressWarnings("unused") ResolvedJavaType type) { return false; } @@ -429,6 +429,11 @@ public boolean isFieldIncludedInSharedLayer(ResolvedJavaField field) { return true; } + /** Returns true for fields that should be always closed, even in an open-world analysis. */ + public boolean isAlwaysClosedField(@SuppressWarnings("unused") ResolvedJavaField field) { + return true; + } + public boolean isClosedTypeWorld() { return true; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java index f8c9c26072dc..6fb4e69adffe 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java @@ -66,20 +66,26 @@ public TypeFlow copy(PointsToAnalysis bb, MethodFlowsGraph method @Override public boolean canSaturate(PointsToAnalysis bb) { - /* Fields declared with a closed type don't saturate, they track all input types. */ + /* + * Fields with a closed declared type don't saturate; when their input saturates they are + * injected their declared type, and they track all its instantiated subtypes. + */ return !bb.isClosed(declaredType); } @Override protected void onInputSaturated(PointsToAnalysis bb, TypeFlow input) { + /* The field input, which is usually a field-store, is saturated. */ if (bb.isClosed(declaredType)) { /* - * When a field store is saturated conservatively assume that the field state can - * contain any subtype of its declared type or any primitive value for primitive fields. + * If the field's declared type is closed, conservatively assume that the field state + * can contain any subtype of its declared type or any primitive value for primitive + * fields. Even if a store is not visible to the analysis, i.e., it is from the + * open-world, the complete hierarchy of the field's declared type is visible. */ - declaredType.getTypeFlow(bb, true).addUse(bb, this); + source.injectDeclaredType(); } else { - /* Propagate saturation stamp through the field flow. */ + /* Otherwise, propagate saturation stamp through the field flow. */ super.onInputSaturated(bb, input); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java index ae269e644c51..3ad9a5ea942e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java @@ -146,16 +146,17 @@ public void onObservedUpdate(PointsToAnalysis bb) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { - /* - * Nothing needs to change for open world analysis: we want to link all field flows when - * the receiver saturates. - */ - if (!isSaturated()) { + if (bb.isClosed(field.getDeclaringClass())) { /* - * When the receiver flow saturates start observing the flow of the field declaring - * type, unless the load is already saturated. + * If the field declaring class is closed and the receiver flow saturates start + * observing the flow of the declaring class, unless the load is already saturated. */ - replaceObservedWith(bb, field.getDeclaringClass()); + if (!isSaturated()) { + replaceObservedWith(bb, field.getDeclaringClass()); + } + } else { + /* If the field declaring class is open simply propagate the saturation. */ + onSaturated(bb); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java index 7a32717faf11..74080e871eb4 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java @@ -2030,17 +2030,28 @@ protected void processLoadField(ValueNode node, PointsToAnalysisField field, Val TypeFlowBuilder loadFieldBuilder; if (field.isStatic()) { loadFieldBuilder = TypeFlowBuilder.create(bb, method, state.getPredicate(), node, LoadStaticFieldTypeFlow.class, () -> { + processOpenWorldField(field); FieldTypeFlow fieldFlow = field.getStaticFieldFlow(); - LoadStaticFieldTypeFlow loadFieldFLow = new LoadStaticFieldTypeFlow(AbstractAnalysisEngine.sourcePosition(node), field, fieldFlow); - flowsGraph.addNodeFlow(node, loadFieldFLow); - return loadFieldFLow; + LoadStaticFieldTypeFlow loadFieldFlow = new LoadStaticFieldTypeFlow(AbstractAnalysisEngine.sourcePosition(node), field, fieldFlow); + flowsGraph.addNodeFlow(node, loadFieldFlow); + return loadFieldFlow; }); } else { TypeFlowBuilder objectBuilder = state.lookup(object); loadFieldBuilder = TypeFlowBuilder.create(bb, method, state.getPredicate(), node, LoadInstanceFieldTypeFlow.class, () -> { - LoadInstanceFieldTypeFlow loadFieldFLow = new LoadInstanceFieldTypeFlow(AbstractAnalysisEngine.sourcePosition(node), field, objectBuilder.get()); - flowsGraph.addNodeFlow(node, loadFieldFLow); - return loadFieldFLow; + LoadInstanceFieldTypeFlow loadFieldFlow = new LoadInstanceFieldTypeFlow(AbstractAnalysisEngine.sourcePosition(node), field, objectBuilder.get()); + processOpenWorldField(field); + if (!bb.isClosed(loadFieldFlow.field().getDeclaringClass())) { + /* + * If the field declaring type is open then the receiver object state of a + * load may be empty/incomplete. Saturate the load, assuming there are + * unseen writes in the open world. + */ + loadFieldFlow.enableFlow(bb); + loadFieldFlow.onSaturated(bb); + } + flowsGraph.addNodeFlow(node, loadFieldFlow); + return loadFieldFlow; }); loadFieldBuilder.addObserverDependency(objectBuilder); } @@ -2049,9 +2060,28 @@ protected void processLoadField(ValueNode node, PointsToAnalysisField field, Val } } + private void processOpenWorldField(PointsToAnalysisField field) { + if (!bb.isClosed(field)) { + /* + * Open fields can be written from the open world and may contain state not seen by the + * analysis. This is equivalent with assuming any of their input is saturated. + */ + field.getInitialFlow().onInputSaturated(bb, null); + } + } + protected void processStoreField(ValueNode node, PointsToAnalysisField field, ValueNode object, ValueNode newValue, JavaKind newValueKind, TypeFlowsOfNodes state) { field.registerAsWritten(AbstractAnalysisEngine.sourcePosition(node)); + if (!bb.isClosed(field)) { + /* + * Open fields can be written from the open world and may contain state not seen by the + * analysis. We don't track their writes, instead we eagerly saturate their loads (See + * processLoadField). + */ + return; + } + if (bb.isSupportedJavaKind(newValueKind)) { TypeFlowBuilder valueBuilder = state.lookupOrAny(newValue, newValueKind); @@ -2121,7 +2151,6 @@ protected void processStoreIndexed(ValueNode node, ValueNode array, ValueNode ne protected void processUnsafeLoad(ValueNode node, ValueNode object, TypeFlowsOfNodes state) { /* All unsafe accessed primitive fields are always saturated. */ if (node.getStackKind() == JavaKind.Object) { - TypeFlowBuilder objectBuilder = state.lookup(object); TypeFlowBuilder loadBuilder; if (bb.analysisPolicy().useConservativeUnsafeAccess()) { @@ -2137,6 +2166,7 @@ protected void processUnsafeLoad(ValueNode node, ValueNode object, TypeFlowsOfNo return preSaturated; }); } else { + TypeFlowBuilder objectBuilder = state.lookup(object); /* * Use the Object type as a conservative approximation for both the receiver object * type and the loaded values type. @@ -2146,9 +2176,9 @@ protected void processUnsafeLoad(ValueNode node, ValueNode object, TypeFlowsOfNo flowsGraph.addMiscEntryFlow(loadTypeFlow); return loadTypeFlow; }); + loadBuilder.addObserverDependency(objectBuilder); } - loadBuilder.addObserverDependency(objectBuilder); state.add(node, loadBuilder); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java index e07d14c9f6e4..da61376bd293 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java @@ -177,10 +177,7 @@ public void onObservedUpdate(PointsToAnalysis bb) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { - /* - * Nothing needs to change for open world analysis: we want to link all field flows when - * the receiver saturates. - */ + assert bb.isClosed(field); /* * When receiver flow saturates swap in the saturated store type flow. When the store * itself saturates it propagates the saturation state to the uses/observers and unlinks diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/TypeFlowSimplifier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/TypeFlowSimplifier.java index 3f1a68d7224c..eeada5de8bb5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/TypeFlowSimplifier.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/TypeFlowSimplifier.java @@ -182,19 +182,27 @@ private void handleParameter(ParameterNode node, SimplifierTool tool) { } private void handleLoadField(LoadFieldNode node, SimplifierTool tool) { - /* - * First step: it is beneficial to strengthen the stamp of the LoadFieldNode because then - * there is no artificial anchor after which the more precise type is available. However, - * the memory load will be a floating node later, so we can only update the stamp directly - * to the stamp that is correct for the whole method and all inlined methods. - */ PointsToAnalysisField field = (PointsToAnalysisField) node.field(); + if (!analysis.isClosed(field)) { + /* + * We cannot trust the field's state if the field is open as it may be written from the + * open world. + */ + return; + } Object fieldNewStampOrConstant = strengthenStampFromTypeFlow(node, field.getSinkFlow(), node, tool); if (fieldNewStampOrConstant instanceof JavaConstant) { ConstantNode replacement = ConstantNode.forConstant((JavaConstant) fieldNewStampOrConstant, analysis.getMetaAccess(), graph); graph.replaceFixedWithFloating(node, replacement); tool.addToWorkList(replacement); } else { + /* + * First step: it is beneficial to strengthen the stamp of the LoadFieldNode because + * then there is no artificial anchor after which the more precise type is available. + * However, the memory load will be a floating node later, so we can only update the + * stamp directly to the stamp that is correct for the whole method and all inlined + * methods. + */ super.updateStampInPlace(node, (Stamp) fieldNewStampOrConstant, tool); /* diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/Target_jdk_internal_perf_PerfCounter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/Target_jdk_internal_perf_PerfCounter.java index e4a5dbca516f..fcf50d931a72 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/Target_jdk_internal_perf_PerfCounter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/Target_jdk_internal_perf_PerfCounter.java @@ -29,17 +29,30 @@ import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.util.VMError; /** - * PerfCounter objects that end up in the image heap reference an invalid buffer (see - * PerfCounter.lb). Accessing this buffer would result in a segfault, so we substitute all methods - * that may access this buffer. + * {@code PerfCounter} objects created at build time and written in the image heap reference an + * invalid buffer via the {@code PerfCounter.lb} field. Accessing this buffer would result in a + * segfault, so we substitute all methods that may access this buffer. Moreover, we delete the + * buffer field since it should never be reachable and make the factory methods that would + * initialize it throw an {@link VMError#unsupportedFeature(String)} at run time. */ @TargetClass(className = "jdk.internal.perf.PerfCounter") final class Target_jdk_internal_perf_PerfCounter { @Delete // private LongBuffer lb; + @Substitute // + public static Target_jdk_internal_perf_PerfCounter newPerfCounter(@SuppressWarnings("unused") String name) { + throw VMError.unsupportedFeature("Creating a new jdk.internal.perf.PerfCounter at run time is currently not supported."); + } + + @Substitute // + public static Target_jdk_internal_perf_PerfCounter newConstantPerfCounter(@SuppressWarnings("unused") String name) { + throw VMError.unsupportedFeature("Creating a new jdk.internal.perf.PerfCounter at run time is currently not supported."); + } + @Substitute @SuppressWarnings("static-method") public long get() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java index c690d4d7c5d9..0f84d0b320f0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -200,6 +200,12 @@ public void beforeAnalysis(BeforeAnalysisAccess a) { access.registerFieldValueTransformer(moduleLayerFeatureUtils.moduleReferenceLocationField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE); access.registerFieldValueTransformer(moduleLayerFeatureUtils.moduleReferenceImplLocationField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE); + access.registerFieldValueTransformer(moduleLayerFeatureUtils.jarModuleReaderJfField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE); + access.registerFieldValueTransformer(moduleLayerFeatureUtils.jarModuleReaderUriField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE); + access.registerFieldValueTransformer(moduleLayerFeatureUtils.moduleReference1ValFileStringField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE); + access.registerFieldValueTransformer(moduleLayerFeatureUtils.moduleReference1ValUriField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE); + access.registerFieldValueTransformer(moduleLayerFeatureUtils.loadedModuleClassCodeSourceURLField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE); + access.registerFieldValueTransformer(moduleLayerFeatureUtils.loadedModuleClassURIField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE); } /** @@ -785,6 +791,12 @@ private final class ModuleLayerFeatureUtils { private final Field moduleLayerModulesField; private final Field moduleReferenceLocationField; private final Field moduleReferenceImplLocationField; + private final Field jarModuleReaderJfField; + private final Field jarModuleReaderUriField; + private final Field moduleReference1ValFileStringField; + private final Field moduleReference1ValUriField; + private final Field loadedModuleClassCodeSourceURLField; + private final Field loadedModuleClassURIField; private final Set nativeAccessEnabled; ModuleLayerFeatureUtils(ImageClassLoader cl) { @@ -855,6 +867,16 @@ private final class ModuleLayerFeatureUtils { moduleLayerModulesField = ReflectionUtil.lookupField(ModuleLayer.class, "modules"); moduleReferenceLocationField = ReflectionUtil.lookupField(ModuleReference.class, "location"); moduleReferenceImplLocationField = ReflectionUtil.lookupField(ModuleReferenceImpl.class, "location"); + Class jarModuleReaderClass = ReflectionUtil.lookupClass("jdk.internal.module.ModuleReferences$JarModuleReader"); + jarModuleReaderJfField = ReflectionUtil.lookupField(jarModuleReaderClass, "jf"); + jarModuleReaderUriField = ReflectionUtil.lookupField(jarModuleReaderClass, "uri"); + /* Supplier in ModuleReferences.newJarModule() captures dirs. */ + Class moduleReference1Class = ReflectionUtil.lookupClass("jdk.internal.module.ModuleReferences$1"); + moduleReference1ValFileStringField = ReflectionUtil.lookupField(moduleReference1Class, "val$fileString"); + moduleReference1ValUriField = ReflectionUtil.lookupField(moduleReference1Class, "val$uri"); + Class loadedModuleClass = ReflectionUtil.lookupClass("jdk.internal.loader.BuiltinClassLoader$LoadedModule"); + loadedModuleClassCodeSourceURLField = ReflectionUtil.lookupField(loadedModuleClass, "codeSourceURL"); + loadedModuleClassURIField = ReflectionUtil.lookupField(loadedModuleClass, "uri"); } catch (ReflectiveOperationException | NoSuchElementException ex) { throw VMError.shouldNotReachHere("Failed to retrieve fields of the Module/ModuleLayer class.", ex); } 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 c8c55de21b88..a24c134a6430 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 @@ -1395,9 +1395,7 @@ private static Inflation createBigBang(DebugContext debug, OptionValues options, ConstantReflectionProvider constantReflectionProvider = aProviders.getConstantReflection(); WordTypes wordTypes = aProviders.getWordTypes(); String reason = "included by " + SubstrateOptionsParser.commandArgument(LayerCreate, ""); - ClassInclusionPolicy classInclusionPolicy = ImageLayerBuildingSupport.buildingSharedLayer() - ? new ClassInclusionPolicy.SharedLayerImageInclusionPolicy(reason) - : new ClassInclusionPolicy.DefaultAllInclusionPolicy(reason); + ClassInclusionPolicy classInclusionPolicy = ImageLayerBuildingSupport.buildingSharedLayer() ? new ClassInclusionPolicy.SharedLayerImageInclusionPolicy(reason) : null; if (PointstoOptions.UseExperimentalReachabilityAnalysis.getValue(options)) { ReachabilityMethodProcessingHandler reachabilityMethodProcessingHandler; if (PointstoOptions.UseReachabilityMethodSummaries.getValue(options)) { 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 0a43d0f8710a..a8b6c9d4ffed 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 @@ -248,7 +248,11 @@ public enum UsageKind { private final boolean buildingExtensionLayer = ImageLayerBuildingSupport.buildingExtensionLayer(); // All elements below are from the host VM universe, not the analysis universe - private final Set sharedLayerExcludedFields; + /** + * Contains fields that should be kept as closed in an open-world analysis. In general these are + * fields that should not be written to because, e.g., they need to be folded in the base image. + */ + private final Set closedWorldFields; /** * Some modules contain native methods that should never be in the image, as they are either * hosted only, or currently unsupported in layered images. @@ -297,13 +301,10 @@ public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializatio parsingSupport = null; } layerId = buildingImageLayer ? DynamicImageLayerInfo.getCurrentLayerNumber() : 0; - if (buildingSharedLayer) { - sharedLayerExcludedFields = initializeSharedLayerExcludedFields(); - sharedLayerForbiddenModules = initializeSharedLayerForbiddenModules(); - } else { - sharedLayerExcludedFields = null; - sharedLayerForbiddenModules = null; - } + + /* In a closed-world analysis all fields are closed, so the set is null. */ + closedWorldFields = isClosedTypeWorld ? null : getAlwaysClosedFields(); + sharedLayerForbiddenModules = buildingSharedLayer ? initializeSharedLayerForbiddenModules() : null; layeredStaticFieldSupport = buildingImageLayer ? LayeredStaticFieldSupport.singleton() : null; optionKeyType = lookupOriginalType(OptionKey.class); @@ -322,8 +323,8 @@ public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializatio * time then they cannot be an {@link AnalysisType}. */ @Override - public boolean isCoreType(AnalysisType type) { - return loader.getBuilderModules().contains(type.getJavaClass().getModule()); + public boolean isCoreType(ResolvedJavaType type) { + return loader.getBuilderModules().contains(OriginalClassProvider.getJavaClass(type).getModule()); } @Override @@ -983,43 +984,46 @@ private ResolvedJavaField lookupOriginalDeclaredField(Class declaringClass, S return originalMetaAccess.lookupJavaField(ReflectionUtil.lookupField(declaringClass, fieldName)); } - private Set initializeSharedLayerExcludedFields() { - Set excludedFields = new HashSet<>(); + /** + * @return fields that should stay closed even in an open-world analysis. + */ + private Set getAlwaysClosedFields() { + Set closedFields = new HashSet<>(); /* * These fields need to be folded as they are used in snippets, and they must be accessed * without producing reads with side effects. */ - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "layoutEncoding")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "numClassTypes")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "numIterableInterfaceTypes")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "openTypeWorldTypeCheckSlots")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "openTypeWorldInterfaceHashParam")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "openTypeWorldInterfaceHashTable")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "interfaceID")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "typeIDDepth")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "typeID")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "monitorOffset")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "hubType")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "companion")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHubCompanion.class, "arrayHub")); - excludedFields.add(lookupOriginalDeclaredField(DynamicHubCompanion.class, "additionalFlags")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "layoutEncoding")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "numClassTypes")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "numIterableInterfaceTypes")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "openTypeWorldTypeCheckSlots")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "openTypeWorldInterfaceHashParam")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "openTypeWorldInterfaceHashTable")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "interfaceID")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "typeIDDepth")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "typeID")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "monitorOffset")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "hubType")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "companion")); + closedFields.add(lookupOriginalDeclaredField(DynamicHubCompanion.class, "arrayHub")); + closedFields.add(lookupOriginalDeclaredField(DynamicHubCompanion.class, "additionalFlags")); /* Needs to be immutable for correct lowering of SubstrateIdentityHashCodeNode. */ - excludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "identityHashOffset")); + closedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "identityHashOffset")); /* * Including this field makes ThreadLocalAllocation.getTlabDescriptorSize reachable through * ThreadLocalAllocation.regularTLAB which is accessed with * FastThreadLocalBytes.getSizeSupplier */ - excludedFields.add(lookupOriginalDeclaredField(VMThreadLocalInfo.class, "sizeSupplier")); + closedFields.add(lookupOriginalDeclaredField(VMThreadLocalInfo.class, "sizeSupplier")); /* This field cannot be written to (see documentation) */ - excludedFields.add(lookupOriginalDeclaredField(Counter.Group.class, "enabled")); + closedFields.add(lookupOriginalDeclaredField(Counter.Group.class, "enabled")); /* This field can contain a reference to a Thread, which is not allowed in the heap */ - excludedFields.add(lookupOriginalDeclaredField(NativeLibraries.class, "nativeLibraryLockMap")); + closedFields.add(lookupOriginalDeclaredField(NativeLibraries.class, "nativeLibraryLockMap")); - return excludedFields; + return closedFields; } protected Set initializeSharedLayerForbiddenModules() { @@ -1253,19 +1257,21 @@ public boolean isSupportedOriginalField(BigBang bb, ResolvedJavaField field) { } /** - * Checks the exclusion list to determine if field should be included in the shared layer. + * Determine if a field should be force-included in the shared layer. */ @Override public boolean isFieldIncludedInSharedLayer(ResolvedJavaField field) { - if (sharedLayerExcludedFields.contains(OriginalFieldProvider.getOriginalField(field))) { + if (isAlwaysClosedField(field)) { return false; } + // GR-71702 will prevent batch registering svm.core fields as roots + return !field.getDeclaringClass().toJavaName().startsWith("jdk.graal.compiler"); + } - /* Fields from the Graal compiler should not be in the shared layer unconditionally. */ - if (field.getDeclaringClass().toJavaName().startsWith("jdk.graal.compiler")) { - return false; - } - return true; + @Override + public boolean isAlwaysClosedField(ResolvedJavaField field) { + /* Note that DynamicHub may not be seen as core. */ + return closedWorldFields.contains(OriginalFieldProvider.getOriginalField(field)); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImagePointsToAnalysis.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImagePointsToAnalysis.java index e055bc506fc8..f2a2b5c267cd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImagePointsToAnalysis.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImagePointsToAnalysis.java @@ -45,7 +45,6 @@ import com.oracle.svm.hosted.HostedConfiguration; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.CustomTypeFieldHandler; -import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.code.IncompatibleClassChangeFallbackMethod; import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; @@ -145,8 +144,9 @@ public void onTypeReachable(AnalysisType type) { } if (ImageLayerBuildingSupport.buildingSharedLayer()) { /* - * Register open-world fields as roots to prevent premature optimizations, i.e., - * like constant-folding their values in the shared layer. + * To prevent premature optimizations of fields accesses in open world analysis, + * i.e., like constant-folding their values in the base image, register all fields + * of reachable types as roots, except some fields that should always be folded. */ tryRegisterFieldsInBaseImage(type.getInstanceFields(true)); tryRegisterFieldsInBaseImage(type.getStaticFields()); @@ -155,7 +155,7 @@ public void onTypeReachable(AnalysisType type) { * Register run time executed class initializers as roots in the base layer. */ AnalysisMethod classInitializer = type.getClassInitializer(); - if (classInitializer != null && !ClassInitializationSupport.singleton().maybeInitializeAtBuildTime(type) && classInitializer.getCode() != null) { + if (classInitializer != null && !hostVM.isInitialized(type) && classInitializer.getCode() != null) { classInclusionPolicy.includeMethod(classInitializer); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java index 442c5860e04f..6e61c03096e5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java @@ -301,6 +301,17 @@ public Object transform(Object receiver, Object originalValue) { } catch (ReflectiveOperationException e) { VMError.shouldNotReachHere("Can not invoke createFormsForm method to register base types from the java.lang.invoke.LambdaForm$BasicType class."); } + + /* + * Allocating PerfCounter objects at run-time is not supported. For more details see the + * Target_jdk_internal_perf_PerfCounter substitutions. Here we ensure that the @Stable field + * java.lang.invoke.LambdaForm.LF_FAILED is initialized and is allowed to be folded before + * analysis to eliminate any code paths that would try initializing it. + */ + Method failedCompilationCounterMethod = ReflectionUtil.lookupMethod(lambdaFormClass, "failedCompilationCounter"); + ReflectionUtil.invokeMethod(failedCompilationCounterMethod, null); + access.allowStableFieldFoldingBeforeAnalysis(access.findField(lambdaFormClass, "LF_FAILED")); + // The following call sites produce side effects by generating BoundMethodHandle // species, which are subsequently referenced by java.lang.invoke.LambdaForm$Holder. MethodHandles.constant(long.class, 0L); diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaFeature.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaFeature.java index d73118ef4861..78d17a4cee5e 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaFeature.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaFeature.java @@ -28,7 +28,6 @@ import static com.oracle.graal.pointsto.ObjectScanner.ScanReason; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; @@ -94,7 +93,6 @@ private static boolean assertionsEnabled() { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - methodHandleSetup(); FeatureImpl.BeforeAnalysisAccessImpl accessImpl = (FeatureImpl.BeforeAnalysisAccessImpl) access; try { AnalysisType declaringClass = accessImpl.getMetaAccess().lookupJavaType(InterpreterStubSection.class); @@ -106,16 +104,6 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { } } - private static void methodHandleSetup() { - /* - * Get java.lang.invoke.LambdaForm.LF_FAILED initialized since we don't support perf counter - * creation at run-time. See Target_jdk_internal_perf_PerfCounter. - */ - Class lfClass = ReflectionUtil.lookupClass("java.lang.invoke.LambdaForm"); - Method failedCompilationCounterMethod = ReflectionUtil.lookupMethod(lfClass, "failedCompilationCounter"); - ReflectionUtil.invokeMethod(failedCompilationCounterMethod, null); - } - @Override public void afterAnalysis(AfterAnalysisAccess access) { AnalysisUniverse aUniverse = ((FeatureImpl.AfterAnalysisAccessImpl) access).getUniverse();