Skip to content

Commit b07be4b

Browse files
committed
[GR-70083] Refactor open world field handling.
PullRequest: graal/22716
2 parents 2d3c1c8 + 25a8d34 commit b07be4b

File tree

15 files changed

+204
-93
lines changed

15 files changed

+204
-93
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ public AbstractAnalysisEngine(OptionValues options, AnalysisUniverse universe, H
135135
this.snippetReflectionProvider = snippetReflectionProvider;
136136
this.constantReflectionProvider = constantReflectionProvider;
137137
this.wordTypes = wordTypes;
138-
classInclusionPolicy.setBigBang(this);
138+
if (classInclusionPolicy != null) {
139+
classInclusionPolicy.setBigBang(this);
140+
}
139141
this.classInclusionPolicy = classInclusionPolicy;
140142
}
141143

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,30 @@ public boolean isClosed(AnalysisType type) {
180180
return type.isArray() || type.isLeaf() || hostVM.isCoreType(type);
181181
}
182182

183+
/**
184+
* Determine if the field is by default closed in an open-world analysis. A field is considered
185+
* closed if it cannot be written *directly* from the open world. This applies for example to
186+
* core VM fields whose writes should only come from code included in the base image.
187+
* <p>
188+
* Note that flows of closed fields can still contain values coming from the open world. For
189+
* example the field could be the target of a field-store that is reached directly from a
190+
* parameter of an entry point method. Similarly, the field could be unsafe accessed.
191+
*/
192+
public boolean isClosed(ResolvedJavaField field) {
193+
if (hostVM.isClosedTypeWorld()) {
194+
/* In a closed type world all fields are closed. */
195+
return true;
196+
}
197+
if (hostVM.isAlwaysClosedField(field)) {
198+
return true;
199+
}
200+
if (hostVM.isCoreType(field.getDeclaringClass())) {
201+
/* All the other svm.core fields are by default closed. */
202+
return true;
203+
}
204+
return false;
205+
}
206+
183207
@Override
184208
protected CompletionExecutor.Timing getTiming() {
185209
return timing;

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public void checkType(ResolvedJavaType type, AnalysisUniverse universe) {
182182
public void onTypeInstantiated(BigBang bb, AnalysisType type) {
183183
}
184184

185-
public boolean isCoreType(@SuppressWarnings("unused") AnalysisType type) {
185+
public boolean isCoreType(@SuppressWarnings("unused") ResolvedJavaType type) {
186186
return false;
187187
}
188188

@@ -429,6 +429,11 @@ public boolean isFieldIncludedInSharedLayer(ResolvedJavaField field) {
429429
return true;
430430
}
431431

432+
/** Returns true for fields that should be always closed, even in an open-world analysis. */
433+
public boolean isAlwaysClosedField(@SuppressWarnings("unused") ResolvedJavaField field) {
434+
return true;
435+
}
436+
432437
public boolean isClosedTypeWorld() {
433438
return true;
434439
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,26 @@ public TypeFlow<AnalysisField> copy(PointsToAnalysis bb, MethodFlowsGraph method
6666

6767
@Override
6868
public boolean canSaturate(PointsToAnalysis bb) {
69-
/* Fields declared with a closed type don't saturate, they track all input types. */
69+
/*
70+
* Fields with a closed declared type don't saturate; when their input saturates they are
71+
* injected their declared type, and they track all its instantiated subtypes.
72+
*/
7073
return !bb.isClosed(declaredType);
7174
}
7275

7376
@Override
7477
protected void onInputSaturated(PointsToAnalysis bb, TypeFlow<?> input) {
78+
/* The field input, which is usually a field-store, is saturated. */
7579
if (bb.isClosed(declaredType)) {
7680
/*
77-
* When a field store is saturated conservatively assume that the field state can
78-
* contain any subtype of its declared type or any primitive value for primitive fields.
81+
* If the field's declared type is closed, conservatively assume that the field state
82+
* can contain any subtype of its declared type or any primitive value for primitive
83+
* fields. Even if a store is not visible to the analysis, i.e., it is from the
84+
* open-world, the complete hierarchy of the field's declared type is visible.
7985
*/
80-
declaredType.getTypeFlow(bb, true).addUse(bb, this);
86+
source.injectDeclaredType();
8187
} else {
82-
/* Propagate saturation stamp through the field flow. */
88+
/* Otherwise, propagate saturation stamp through the field flow. */
8389
super.onInputSaturated(bb, input);
8490
}
8591
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,17 @@ public void onObservedUpdate(PointsToAnalysis bb) {
146146

147147
@Override
148148
public void onObservedSaturated(PointsToAnalysis bb, TypeFlow<?> observed) {
149-
/*
150-
* Nothing needs to change for open world analysis: we want to link all field flows when
151-
* the receiver saturates.
152-
*/
153-
if (!isSaturated()) {
149+
if (bb.isClosed(field.getDeclaringClass())) {
154150
/*
155-
* When the receiver flow saturates start observing the flow of the field declaring
156-
* type, unless the load is already saturated.
151+
* If the field declaring class is closed and the receiver flow saturates start
152+
* observing the flow of the declaring class, unless the load is already saturated.
157153
*/
158-
replaceObservedWith(bb, field.getDeclaringClass());
154+
if (!isSaturated()) {
155+
replaceObservedWith(bb, field.getDeclaringClass());
156+
}
157+
} else {
158+
/* If the field declaring class is open simply propagate the saturation. */
159+
onSaturated(bb);
159160
}
160161
}
161162

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,17 +2030,28 @@ protected void processLoadField(ValueNode node, PointsToAnalysisField field, Val
20302030
TypeFlowBuilder<?> loadFieldBuilder;
20312031
if (field.isStatic()) {
20322032
loadFieldBuilder = TypeFlowBuilder.create(bb, method, state.getPredicate(), node, LoadStaticFieldTypeFlow.class, () -> {
2033+
processOpenWorldField(field);
20332034
FieldTypeFlow fieldFlow = field.getStaticFieldFlow();
2034-
LoadStaticFieldTypeFlow loadFieldFLow = new LoadStaticFieldTypeFlow(AbstractAnalysisEngine.sourcePosition(node), field, fieldFlow);
2035-
flowsGraph.addNodeFlow(node, loadFieldFLow);
2036-
return loadFieldFLow;
2035+
LoadStaticFieldTypeFlow loadFieldFlow = new LoadStaticFieldTypeFlow(AbstractAnalysisEngine.sourcePosition(node), field, fieldFlow);
2036+
flowsGraph.addNodeFlow(node, loadFieldFlow);
2037+
return loadFieldFlow;
20372038
});
20382039
} else {
20392040
TypeFlowBuilder<?> objectBuilder = state.lookup(object);
20402041
loadFieldBuilder = TypeFlowBuilder.create(bb, method, state.getPredicate(), node, LoadInstanceFieldTypeFlow.class, () -> {
2041-
LoadInstanceFieldTypeFlow loadFieldFLow = new LoadInstanceFieldTypeFlow(AbstractAnalysisEngine.sourcePosition(node), field, objectBuilder.get());
2042-
flowsGraph.addNodeFlow(node, loadFieldFLow);
2043-
return loadFieldFLow;
2042+
LoadInstanceFieldTypeFlow loadFieldFlow = new LoadInstanceFieldTypeFlow(AbstractAnalysisEngine.sourcePosition(node), field, objectBuilder.get());
2043+
processOpenWorldField(field);
2044+
if (!bb.isClosed(loadFieldFlow.field().getDeclaringClass())) {
2045+
/*
2046+
* If the field declaring type is open then the receiver object state of a
2047+
* load may be empty/incomplete. Saturate the load, assuming there are
2048+
* unseen writes in the open world.
2049+
*/
2050+
loadFieldFlow.enableFlow(bb);
2051+
loadFieldFlow.onSaturated(bb);
2052+
}
2053+
flowsGraph.addNodeFlow(node, loadFieldFlow);
2054+
return loadFieldFlow;
20442055
});
20452056
loadFieldBuilder.addObserverDependency(objectBuilder);
20462057
}
@@ -2049,9 +2060,28 @@ protected void processLoadField(ValueNode node, PointsToAnalysisField field, Val
20492060
}
20502061
}
20512062

2063+
private void processOpenWorldField(PointsToAnalysisField field) {
2064+
if (!bb.isClosed(field)) {
2065+
/*
2066+
* Open fields can be written from the open world and may contain state not seen by the
2067+
* analysis. This is equivalent with assuming any of their input is saturated.
2068+
*/
2069+
field.getInitialFlow().onInputSaturated(bb, null);
2070+
}
2071+
}
2072+
20522073
protected void processStoreField(ValueNode node, PointsToAnalysisField field, ValueNode object, ValueNode newValue, JavaKind newValueKind, TypeFlowsOfNodes state) {
20532074
field.registerAsWritten(AbstractAnalysisEngine.sourcePosition(node));
20542075

2076+
if (!bb.isClosed(field)) {
2077+
/*
2078+
* Open fields can be written from the open world and may contain state not seen by the
2079+
* analysis. We don't track their writes, instead we eagerly saturate their loads (See
2080+
* processLoadField).
2081+
*/
2082+
return;
2083+
}
2084+
20552085
if (bb.isSupportedJavaKind(newValueKind)) {
20562086
TypeFlowBuilder<?> valueBuilder = state.lookupOrAny(newValue, newValueKind);
20572087

@@ -2121,7 +2151,6 @@ protected void processStoreIndexed(ValueNode node, ValueNode array, ValueNode ne
21212151
protected void processUnsafeLoad(ValueNode node, ValueNode object, TypeFlowsOfNodes state) {
21222152
/* All unsafe accessed primitive fields are always saturated. */
21232153
if (node.getStackKind() == JavaKind.Object) {
2124-
TypeFlowBuilder<?> objectBuilder = state.lookup(object);
21252154

21262155
TypeFlowBuilder<?> loadBuilder;
21272156
if (bb.analysisPolicy().useConservativeUnsafeAccess()) {
@@ -2137,6 +2166,7 @@ protected void processUnsafeLoad(ValueNode node, ValueNode object, TypeFlowsOfNo
21372166
return preSaturated;
21382167
});
21392168
} else {
2169+
TypeFlowBuilder<?> objectBuilder = state.lookup(object);
21402170
/*
21412171
* Use the Object type as a conservative approximation for both the receiver object
21422172
* type and the loaded values type.
@@ -2146,9 +2176,9 @@ protected void processUnsafeLoad(ValueNode node, ValueNode object, TypeFlowsOfNo
21462176
flowsGraph.addMiscEntryFlow(loadTypeFlow);
21472177
return loadTypeFlow;
21482178
});
2179+
loadBuilder.addObserverDependency(objectBuilder);
21492180
}
21502181

2151-
loadBuilder.addObserverDependency(objectBuilder);
21522182
state.add(node, loadBuilder);
21532183
}
21542184
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,7 @@ public void onObservedUpdate(PointsToAnalysis bb) {
177177

178178
@Override
179179
public void onObservedSaturated(PointsToAnalysis bb, TypeFlow<?> observed) {
180-
/*
181-
* Nothing needs to change for open world analysis: we want to link all field flows when
182-
* the receiver saturates.
183-
*/
180+
assert bb.isClosed(field);
184181
/*
185182
* When receiver flow saturates swap in the saturated store type flow. When the store
186183
* itself saturates it propagates the saturation state to the uses/observers and unlinks

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/TypeFlowSimplifier.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,27 @@ private void handleParameter(ParameterNode node, SimplifierTool tool) {
182182
}
183183

184184
private void handleLoadField(LoadFieldNode node, SimplifierTool tool) {
185-
/*
186-
* First step: it is beneficial to strengthen the stamp of the LoadFieldNode because then
187-
* there is no artificial anchor after which the more precise type is available. However,
188-
* the memory load will be a floating node later, so we can only update the stamp directly
189-
* to the stamp that is correct for the whole method and all inlined methods.
190-
*/
191185
PointsToAnalysisField field = (PointsToAnalysisField) node.field();
186+
if (!analysis.isClosed(field)) {
187+
/*
188+
* We cannot trust the field's state if the field is open as it may be written from the
189+
* open world.
190+
*/
191+
return;
192+
}
192193
Object fieldNewStampOrConstant = strengthenStampFromTypeFlow(node, field.getSinkFlow(), node, tool);
193194
if (fieldNewStampOrConstant instanceof JavaConstant) {
194195
ConstantNode replacement = ConstantNode.forConstant((JavaConstant) fieldNewStampOrConstant, analysis.getMetaAccess(), graph);
195196
graph.replaceFixedWithFloating(node, replacement);
196197
tool.addToWorkList(replacement);
197198
} else {
199+
/*
200+
* First step: it is beneficial to strengthen the stamp of the LoadFieldNode because
201+
* then there is no artificial anchor after which the more precise type is available.
202+
* However, the memory load will be a floating node later, so we can only update the
203+
* stamp directly to the stamp that is correct for the whole method and all inlined
204+
* methods.
205+
*/
198206
super.updateStampInPlace(node, (Stamp) fieldNewStampOrConstant, tool);
199207

200208
/*

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jvmstat/Target_jdk_internal_perf_PerfCounter.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,30 @@
2929
import com.oracle.svm.core.annotate.Delete;
3030
import com.oracle.svm.core.annotate.Substitute;
3131
import com.oracle.svm.core.annotate.TargetClass;
32+
import com.oracle.svm.core.util.VMError;
3233

3334
/**
34-
* PerfCounter objects that end up in the image heap reference an invalid buffer (see
35-
* PerfCounter.lb). Accessing this buffer would result in a segfault, so we substitute all methods
36-
* that may access this buffer.
35+
* {@code PerfCounter} objects created at build time and written in the image heap reference an
36+
* invalid buffer via the {@code PerfCounter.lb} field. Accessing this buffer would result in a
37+
* segfault, so we substitute all methods that may access this buffer. Moreover, we delete the
38+
* buffer field since it should never be reachable and make the factory methods that would
39+
* initialize it throw an {@link VMError#unsupportedFeature(String)} at run time.
3740
*/
3841
@TargetClass(className = "jdk.internal.perf.PerfCounter")
3942
final class Target_jdk_internal_perf_PerfCounter {
4043
@Delete //
4144
private LongBuffer lb;
4245

46+
@Substitute //
47+
public static Target_jdk_internal_perf_PerfCounter newPerfCounter(@SuppressWarnings("unused") String name) {
48+
throw VMError.unsupportedFeature("Creating a new jdk.internal.perf.PerfCounter at run time is currently not supported.");
49+
}
50+
51+
@Substitute //
52+
public static Target_jdk_internal_perf_PerfCounter newConstantPerfCounter(@SuppressWarnings("unused") String name) {
53+
throw VMError.unsupportedFeature("Creating a new jdk.internal.perf.PerfCounter at run time is currently not supported.");
54+
}
55+
4356
@Substitute
4457
@SuppressWarnings("static-method")
4558
public long get() {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ public void beforeAnalysis(BeforeAnalysisAccess a) {
200200

201201
access.registerFieldValueTransformer(moduleLayerFeatureUtils.moduleReferenceLocationField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE);
202202
access.registerFieldValueTransformer(moduleLayerFeatureUtils.moduleReferenceImplLocationField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE);
203+
access.registerFieldValueTransformer(moduleLayerFeatureUtils.jarModuleReaderJfField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE);
204+
access.registerFieldValueTransformer(moduleLayerFeatureUtils.jarModuleReaderUriField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE);
205+
access.registerFieldValueTransformer(moduleLayerFeatureUtils.moduleReference1ValFileStringField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE);
206+
access.registerFieldValueTransformer(moduleLayerFeatureUtils.moduleReference1ValUriField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE);
207+
access.registerFieldValueTransformer(moduleLayerFeatureUtils.loadedModuleClassCodeSourceURLField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE);
208+
access.registerFieldValueTransformer(moduleLayerFeatureUtils.loadedModuleClassURIField, ModuleLayerFeatureUtils.ResetModuleReferenceLocation.INSTANCE);
203209
}
204210

205211
/**
@@ -785,6 +791,12 @@ private final class ModuleLayerFeatureUtils {
785791
private final Field moduleLayerModulesField;
786792
private final Field moduleReferenceLocationField;
787793
private final Field moduleReferenceImplLocationField;
794+
private final Field jarModuleReaderJfField;
795+
private final Field jarModuleReaderUriField;
796+
private final Field moduleReference1ValFileStringField;
797+
private final Field moduleReference1ValUriField;
798+
private final Field loadedModuleClassCodeSourceURLField;
799+
private final Field loadedModuleClassURIField;
788800
private final Set<String> nativeAccessEnabled;
789801

790802
ModuleLayerFeatureUtils(ImageClassLoader cl) {
@@ -855,6 +867,16 @@ private final class ModuleLayerFeatureUtils {
855867
moduleLayerModulesField = ReflectionUtil.lookupField(ModuleLayer.class, "modules");
856868
moduleReferenceLocationField = ReflectionUtil.lookupField(ModuleReference.class, "location");
857869
moduleReferenceImplLocationField = ReflectionUtil.lookupField(ModuleReferenceImpl.class, "location");
870+
Class<?> jarModuleReaderClass = ReflectionUtil.lookupClass("jdk.internal.module.ModuleReferences$JarModuleReader");
871+
jarModuleReaderJfField = ReflectionUtil.lookupField(jarModuleReaderClass, "jf");
872+
jarModuleReaderUriField = ReflectionUtil.lookupField(jarModuleReaderClass, "uri");
873+
/* Supplier<ModuleReader> in ModuleReferences.newJarModule() captures dirs. */
874+
Class<?> moduleReference1Class = ReflectionUtil.lookupClass("jdk.internal.module.ModuleReferences$1");
875+
moduleReference1ValFileStringField = ReflectionUtil.lookupField(moduleReference1Class, "val$fileString");
876+
moduleReference1ValUriField = ReflectionUtil.lookupField(moduleReference1Class, "val$uri");
877+
Class<?> loadedModuleClass = ReflectionUtil.lookupClass("jdk.internal.loader.BuiltinClassLoader$LoadedModule");
878+
loadedModuleClassCodeSourceURLField = ReflectionUtil.lookupField(loadedModuleClass, "codeSourceURL");
879+
loadedModuleClassURIField = ReflectionUtil.lookupField(loadedModuleClass, "uri");
858880
} catch (ReflectiveOperationException | NoSuchElementException ex) {
859881
throw VMError.shouldNotReachHere("Failed to retrieve fields of the Module/ModuleLayer class.", ex);
860882
}

0 commit comments

Comments
 (0)