Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,26 @@ public TypeFlow<AnalysisField> 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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);

Expand Down Expand Up @@ -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()) {
Expand All @@ -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.
Expand All @@ -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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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<String> nativeAccessEnabled;

ModuleLayerFeatureUtils(ImageClassLoader cl) {
Expand Down Expand Up @@ -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<ModuleReader> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
Loading