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();