From bd5b69c58001016d3b7a0d1bb5e6d04c60f30ed0 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Thu, 2 Dec 2021 23:00:00 -0800 Subject: [PATCH 01/23] Refactor HostVM.platformSupported(). The universe parameter is not necessary. --- .../com/oracle/graal/pointsto/api/HostVM.java | 2 +- .../graal/pointsto/meta/AnalysisUniverse.java | 8 ++++---- .../svm/hosted/HostedConfiguration.java | 4 ++-- .../svm/hosted/NativeImageGenerator.java | 2 +- .../src/com/oracle/svm/hosted/SVMHost.java | 19 +++++++++++-------- .../analysis/DynamicHubInitializer.java | 15 +++++++-------- .../analysis/NativeImagePointsToAnalysis.java | 2 +- .../SubstitutionReflectivityFilter.java | 6 +++--- .../svm/truffle/tck/WhiteListParser.java | 4 ++-- 9 files changed, 32 insertions(+), 30 deletions(-) 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 4b9e8f035a51..b371abb8e1fc 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 @@ -122,7 +122,7 @@ default boolean skipInterface(AnalysisUniverse universe, ResolvedJavaType interf } @SuppressWarnings("unused") - default boolean platformSupported(AnalysisUniverse universe, AnnotatedElement element) { + default boolean platformSupported(AnnotatedElement element) { return true; } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index f26cabef0053..28b067f1281b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -210,7 +210,7 @@ public JavaType lookupAllowUnresolved(JavaType rawType) { @SuppressFBWarnings(value = {"ES_COMPARING_STRINGS_WITH_EQ"}, justification = "Bug in findbugs") private AnalysisType createType(ResolvedJavaType type) { - if (!hostVM.platformSupported(this, type)) { + if (!hostVM.platformSupported(type)) { throw new UnsupportedFeatureException("type is not available in this platform: " + type.toJavaName(true)); } if (sealed && !type.isArray()) { @@ -382,7 +382,7 @@ public JavaField lookupAllowUnresolved(JavaField rawField) { } private AnalysisField createField(ResolvedJavaField field) { - if (!hostVM.platformSupported(this, field)) { + if (!hostVM.platformSupported(field)) { throw new UnsupportedFeatureException("field is not available in this platform: " + field.format("%H.%n")); } if (sealed) { @@ -425,7 +425,7 @@ public JavaMethod lookupAllowUnresolved(JavaMethod rawMethod) { } private AnalysisMethod createMethod(ResolvedJavaMethod method) { - if (!hostVM.platformSupported(this, method)) { + if (!hostVM.platformSupported(method)) { throw new UnsupportedFeatureException("Method " + method.format("%H.%n(%p)" + " is not available in this platform.")); } if (sealed) { @@ -439,7 +439,7 @@ private AnalysisMethod createMethod(ResolvedJavaMethod method) { public AnalysisMethod[] lookup(JavaMethod[] inputs) { List result = new ArrayList<>(inputs.length); for (JavaMethod method : inputs) { - if (hostVM.platformSupported(this, (ResolvedJavaMethod) method)) { + if (hostVM.platformSupported((ResolvedJavaMethod) method)) { AnalysisMethod aMethod = lookup(method); if (aMethod != null) { result.add(aMethod); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java index 4e7930ba7365..9ecdd04255b7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java @@ -133,8 +133,8 @@ public static ObjectLayout createObjectLayout(JavaKind referenceKind) { } public SVMHost createHostVM(OptionValues options, ClassLoader classLoader, ClassInitializationSupport classInitializationSupport, - UnsafeAutomaticSubstitutionProcessor automaticSubstitutions, Platform platform) { - return new SVMHost(options, classLoader, classInitializationSupport, automaticSubstitutions, platform); + UnsafeAutomaticSubstitutionProcessor automaticSubstitutions, Platform platform, SnippetReflectionProvider originalSnippetReflection) { + return new SVMHost(options, classLoader, classInitializationSupport, automaticSubstitutions, platform, originalSnippetReflection); } public CompileQueue createCompileQueue(DebugContext debug, FeatureHandler featureHandler, HostedUniverse hostedUniverse, 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 2fa307cb3bbd..c5ee518fac24 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 @@ -874,7 +874,7 @@ public static AnalysisUniverse createAnalysisUniverse(OptionValues options, Targ SubstitutionProcessor aSubstitutions = createAnalysisSubstitutionProcessor(originalMetaAccess, originalSnippetReflection, cEnumProcessor, automaticSubstitutions, annotationSubstitutions, additionalSubstitutions); - SVMHost hostVM = HostedConfiguration.instance().createHostVM(options, loader.getClassLoader(), classInitializationSupport, automaticSubstitutions, loader.platform); + SVMHost hostVM = HostedConfiguration.instance().createHostVM(options, loader.getClassLoader(), classInitializationSupport, automaticSubstitutions, loader.platform, originalSnippetReflection); automaticSubstitutions.init(loader, originalMetaAccess); AnalysisPolicy analysisPolicy = PointstoOptions.AllocationSiteSensitiveHeap.getValue(options) ? new BytecodeSensitiveAnalysisPolicy(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 447ff9be7918..53a8e5cae1e5 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 @@ -45,7 +45,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.BiConsumer; -import com.oracle.svm.core.jdk.SealedClassSupport; +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; import org.graalvm.compiler.debug.MethodFilter; @@ -103,6 +103,7 @@ import com.oracle.svm.core.hub.ReferenceType; import com.oracle.svm.core.jdk.ClassLoaderSupport; import com.oracle.svm.core.jdk.RecordSupport; +import com.oracle.svm.core.jdk.SealedClassSupport; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.HostedStringDeduplication; @@ -133,6 +134,7 @@ public class SVMHost implements HostVM { private final HostedStringDeduplication stringTable; private final UnsafeAutomaticSubstitutionProcessor automaticSubstitutions; private final List>> classReachabilityListeners; + private final SnippetReflectionProvider originalSnippetReflection; /** * Optionally keep the Graal graphs alive during analysis. This increases the memory footprint @@ -152,10 +154,11 @@ public class SVMHost implements HostVM { private static final Method getNestHostMethod = JavaVersionUtil.JAVA_SPEC >= 11 ? ReflectionUtil.lookupMethod(Class.class, "getNestHost") : null; public SVMHost(OptionValues options, ClassLoader classLoader, ClassInitializationSupport classInitializationSupport, - UnsafeAutomaticSubstitutionProcessor automaticSubstitutions, Platform platform) { + UnsafeAutomaticSubstitutionProcessor automaticSubstitutions, Platform platform, SnippetReflectionProvider originalSnippetReflection) { this.options = options; this.classLoader = classLoader; this.classInitializationSupport = classInitializationSupport; + this.originalSnippetReflection = originalSnippetReflection; this.stringTable = HostedStringDeduplication.singleton(); this.classReachabilityListeners = new ArrayList<>(); this.forbiddenTypes = setupForbiddenTypes(options); @@ -531,7 +534,7 @@ private static ReferenceType computeReferenceType(AnalysisType type) { @Override public void checkType(ResolvedJavaType type, AnalysisUniverse universe) { - Class originalClass = OriginalClassProvider.getJavaClass(universe.getOriginalSnippetReflection(), type); + Class originalClass = OriginalClassProvider.getJavaClass(originalSnippetReflection, type); ClassLoader originalClassLoader = originalClass.getClassLoader(); if (NativeImageSystemClassLoader.singleton().isDisallowedClassLoader(originalClassLoader)) { String message = "Class " + originalClass.getName() + " was loaded by " + originalClassLoader + " and not by the current image class loader " + classLoader + ". "; @@ -732,7 +735,7 @@ public static class Options { @Override public boolean skipInterface(AnalysisUniverse universe, ResolvedJavaType interfaceType, ResolvedJavaType implementingType) { - if (!platformSupported(universe, interfaceType)) { + if (!platformSupported(interfaceType)) { String message = "The interface " + interfaceType.toJavaName(true) + " is not available in the current platform, but used by " + implementingType.toJavaName(true) + ". " + "GraalVM before version 21.2 ignored such interfaces, but this was an oversight."; @@ -750,16 +753,16 @@ public boolean skipInterface(AnalysisUniverse universe, ResolvedJavaType interfa } @Override - public boolean platformSupported(AnalysisUniverse universe, AnnotatedElement element) { + public boolean platformSupported(AnnotatedElement element) { if (element instanceof ResolvedJavaType) { - Package p = OriginalClassProvider.getJavaClass(universe.getOriginalSnippetReflection(), (ResolvedJavaType) element).getPackage(); - if (p != null && !platformSupported(universe, p)) { + Package p = OriginalClassProvider.getJavaClass(originalSnippetReflection, (ResolvedJavaType) element).getPackage(); + if (p != null && !platformSupported(p)) { return false; } } if (element instanceof Class) { Package p = ((Class) element).getPackage(); - if (p != null && !platformSupported(universe, p)) { + if (p != null && !platformSupported(p)) { return false; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java index 760d97244dc6..1c9101385ab9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java @@ -39,7 +39,6 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.hub.AnnotatedSuperInfo; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.GenericInfo; @@ -53,7 +52,6 @@ public class DynamicHubInitializer { private final SVMHost hostVM; - private final AnalysisUniverse universe; private final AnalysisMetaAccess metaAccess; private final UnsupportedFeatures unsupportedFeatures; private final ConstantReflectionProvider constantReflection; @@ -62,11 +60,9 @@ public class DynamicHubInitializer { private final Map annotatedInterfacesMap; private final Map interfacesEncodings; - public DynamicHubInitializer(AnalysisUniverse universe, AnalysisMetaAccess metaAccess, - UnsupportedFeatures unsupportedFeatures, ConstantReflectionProvider constantReflection) { - this.hostVM = (SVMHost) universe.hostVM(); - this.universe = universe; + public DynamicHubInitializer(AnalysisMetaAccess metaAccess, UnsupportedFeatures unsupportedFeatures, ConstantReflectionProvider constantReflection) { this.metaAccess = metaAccess; + this.hostVM = (SVMHost) metaAccess.getUniverse().hostVM(); this.unsupportedFeatures = unsupportedFeatures; this.constantReflection = constantReflection; @@ -76,7 +72,7 @@ public DynamicHubInitializer(AnalysisUniverse universe, AnalysisMetaAccess metaA } public void initializeMetaData(AnalysisType type) { - assert type.isReachable(); + assert type.isReachable() : "Type " + type.toJavaName(true) + " is not marked as reachable."; DynamicHub hub = hostVM.dynamicHub(type); if (hub.getGenericInfo() == null) { fillGenericInfo(type, hub); @@ -105,6 +101,9 @@ public void initializeMetaData(AnalysisType type) { /* * Support for Java annotations. + * + * The annotation encodings must be updated after each analysis iteration since only the + * annotation types marked as reachable are included. */ try { /* @@ -330,7 +329,7 @@ private void fillAnnotatedSuperInfo(AnalysisType type, DynamicHub hub) { private boolean isTypeAllowed(Type t) { if (t instanceof Class) { Optional resolved = metaAccess.optionalLookupJavaType((Class) t); - return resolved.isPresent() && hostVM.platformSupported(universe, resolved.get()); + return resolved.isPresent() && hostVM.platformSupported(resolved.get()); } return true; } 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 b69ec03d5d0f..e9c414e54b23 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 @@ -62,7 +62,7 @@ public NativeImagePointsToAnalysis(OptionValues options, AnalysisUniverse univer super(options, universe, providers, universe.hostVM(), executor, heartbeatCallback, new SubstrateUnsupportedFeatures(), SubstrateOptions.parseOnce()); this.annotationSubstitutionProcessor = annotationSubstitutionProcessor; - dynamicHubInitializer = new DynamicHubInitializer(universe, metaAccess, unsupportedFeatures, providers.getConstantReflection()); + dynamicHubInitializer = new DynamicHubInitializer(metaAccess, unsupportedFeatures, providers.getConstantReflection()); unknownFieldHandler = new PointsToUnknownFieldHandler(metaAccess); callChecker = new CallChecker(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java index f81ce25afc16..6f90109e6d9e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java @@ -46,7 +46,7 @@ public class SubstitutionReflectivityFilter { public static boolean shouldExclude(Class classObj, AnalysisMetaAccess metaAccess, AnalysisUniverse universe) { try { ResolvedJavaType analysisClass = metaAccess.lookupJavaType(classObj); - if (!universe.hostVM().platformSupported(universe, analysisClass)) { + if (!universe.hostVM().platformSupported(analysisClass)) { return true; } else if (analysisClass.isAnnotationPresent(Delete.class)) { return true; // accesses would fail at runtime @@ -60,7 +60,7 @@ public static boolean shouldExclude(Class classObj, AnalysisMetaAccess metaAc public static boolean shouldExclude(Executable method, AnalysisMetaAccess metaAccess, AnalysisUniverse universe) { try { AnalysisMethod aMethod = metaAccess.lookupJavaMethod(method); - if (!universe.hostVM().platformSupported(universe, aMethod)) { + if (!universe.hostVM().platformSupported(aMethod)) { return true; } else if (aMethod.isAnnotationPresent(Delete.class)) { return true; // accesses would fail at runtime @@ -83,7 +83,7 @@ public static boolean shouldExclude(Executable method, AnalysisMetaAccess metaAc public static boolean shouldExclude(Field field, AnalysisMetaAccess metaAccess, AnalysisUniverse universe) { try { AnalysisField aField = metaAccess.lookupJavaField(field); - if (!universe.hostVM().platformSupported(universe, aField)) { + if (!universe.hostVM().platformSupported(aField)) { return true; } if (aField.isAnnotationPresent(Delete.class)) { diff --git a/substratevm/src/com.oracle.svm.truffle.tck/src/com/oracle/svm/truffle/tck/WhiteListParser.java b/substratevm/src/com.oracle.svm.truffle.tck/src/com/oracle/svm/truffle/tck/WhiteListParser.java index 6d3c23b1f18c..ada994664bf9 100644 --- a/substratevm/src/com.oracle.svm.truffle.tck/src/com/oracle/svm/truffle/tck/WhiteListParser.java +++ b/substratevm/src/com.oracle.svm.truffle.tck/src/com/oracle/svm/truffle/tck/WhiteListParser.java @@ -192,12 +192,12 @@ private AnalysisType resolve(String type) throws UnsupportedPlatformException { private void verifySupportedOnActivePlatform(Class clz) throws UnsupportedPlatformException { AnalysisUniverse universe = bb.getUniverse(); Package pkg = clz.getPackage(); - if (pkg != null && !universe.hostVM().platformSupported(universe, pkg)) { + if (pkg != null && !universe.hostVM().platformSupported(pkg)) { throw new UnsupportedPlatformException(clz.getPackage()); } Class current = clz; do { - if (!universe.hostVM().platformSupported(universe, current)) { + if (!universe.hostVM().platformSupported(current)) { throw new UnsupportedPlatformException(current); } current = current.getEnclosingClass(); From e1017ee3d0e5d5af37b6433af6696f7f27c37a35 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Fri, 3 Dec 2021 00:38:51 -0800 Subject: [PATCH 02/23] Use ImageHeapMap in GraalSupport. --- .../oracle/svm/graal/GraalSubstitutions.java | 7 +++---- .../src/com/oracle/svm/graal/GraalSupport.java | 17 +++++++++-------- .../svm/graal/hosted/FieldsOffsetsFeature.java | 9 +++++---- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSubstitutions.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSubstitutions.java index b1c02ef1fb63..04184102eb73 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSubstitutions.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSubstitutions.java @@ -72,7 +72,6 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.config.ConfigurationValues; -import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.util.VMError; @@ -314,7 +313,7 @@ final class Target_org_graalvm_compiler_graph_NodeClass { @SuppressWarnings("unlikely-arg-type") @SuppressFBWarnings(value = {"GC_UNRELATED_TYPES"}, justification = "Class is DynamicHub") public static NodeClass get(Class clazz) { - NodeClass nodeClass = GraalSupport.get().nodeClasses.get(DynamicHub.fromClass(clazz)); + NodeClass nodeClass = GraalSupport.get().nodeClasses.get(clazz); if (nodeClass == null) { throw VMError.shouldNotReachHere("Unknown node class: " + clazz.getName() + "\n"); } @@ -338,7 +337,7 @@ final class Target_org_graalvm_compiler_lir_LIRInstructionClass { @SuppressWarnings("unlikely-arg-type") @SuppressFBWarnings(value = {"GC_UNRELATED_TYPES"}, justification = "Class is DynamicHub") public static LIRInstructionClass get(Class clazz) { - LIRInstructionClass instructionClass = GraalSupport.get().instructionClasses.get(DynamicHub.fromClass(clazz)); + LIRInstructionClass instructionClass = GraalSupport.get().instructionClasses.get(clazz); if (instructionClass == null) { throw VMError.shouldNotReachHere("Unknown instruction class: " + clazz.getName() + "\n"); } @@ -353,7 +352,7 @@ final class Target_org_graalvm_compiler_lir_CompositeValueClass { @SuppressWarnings("unlikely-arg-type") @SuppressFBWarnings(value = {"GC_UNRELATED_TYPES"}, justification = "Class is DynamicHub") public static CompositeValueClass get(Class clazz) { - CompositeValueClass compositeValueClass = GraalSupport.get().compositeValueClasses.get(DynamicHub.fromClass(clazz)); + CompositeValueClass compositeValueClass = GraalSupport.get().compositeValueClasses.get(clazz); if (compositeValueClass == null) { throw VMError.shouldNotReachHere("Unknown composite value class: " + clazz.getName() + "\n"); } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java index 94af62be2e31..81ce0f65e4b0 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java @@ -79,6 +79,7 @@ import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.graal.meta.SharedRuntimeMethod; import com.oracle.svm.core.option.RuntimeOptionValues; +import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.graal.meta.SubstrateMethod; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; @@ -100,13 +101,13 @@ public class GraalSupport { private Object[] graphObjects; private NodeClass[] graphNodeTypes; - public final Map, NodeClass> nodeClasses = new HashMap<>(); - public final Map, LIRInstructionClass> instructionClasses = new HashMap<>(); - public final Map, CompositeValueClass> compositeValueClasses = new HashMap<>(); + public final EconomicMap, NodeClass> nodeClasses = ImageHeapMap.create(); + public final EconomicMap, LIRInstructionClass> instructionClasses = ImageHeapMap.create(); + public final EconomicMap, CompositeValueClass> compositeValueClasses = ImageHeapMap.create(); public HashMap, EconomicMap, List>> matchRuleRegistry; - protected Map, BasePhase.BasePhaseStatistics> basePhaseStatistics; - protected Map, LIRPhase.LIRPhaseStatistics> lirPhaseStatistics; + protected EconomicMap, BasePhase.BasePhaseStatistics> basePhaseStatistics; + protected EconomicMap, LIRPhase.LIRPhaseStatistics> lirPhaseStatistics; protected Function runtimeBackendProvider; protected final GlobalMetrics metricValues = new GlobalMetrics(); @@ -229,8 +230,8 @@ public static void registerImmutableObjects(CompilationAccess access) { @Platforms(Platform.HOSTED_ONLY.class) public static void allocatePhaseStatisticsCache() { - GraalSupport.get().basePhaseStatistics = new HashMap<>(); - GraalSupport.get().lirPhaseStatistics = new HashMap<>(); + GraalSupport.get().basePhaseStatistics = ImageHeapMap.create(); + GraalSupport.get().lirPhaseStatistics = ImageHeapMap.create(); } /* Invoked once for every class that is reachable in the native image. */ @@ -249,7 +250,7 @@ public static void registerPhaseStatistics(DuringAnalysisAccess a, Class newl } } - private static void registerStatistics(Class phaseSubClass, Map, S> cache, S newStatistics, DuringAnalysisAccessImpl access) { + private static void registerStatistics(Class phaseSubClass, EconomicMap, S> cache, S newStatistics, DuringAnalysisAccessImpl access) { assert !cache.containsKey(phaseSubClass); cache.put(phaseSubClass, newStatistics); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java index c35490ce1d93..2b83eb70a608 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.function.Function; +import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.core.common.FieldIntrospection; import org.graalvm.compiler.core.common.Fields; import org.graalvm.compiler.graph.Edges; @@ -168,8 +169,8 @@ private static void classReachabilityListener(DuringAnalysisAccess a, Class n } } - private static > void registerClass(Class clazz, Map, R> registry, Function, R> lookup, boolean excludeAbstract, - DuringAnalysisAccessImpl access) { + private static > void registerClass(Class clazz, EconomicMap, R> registry, + Function, R> lookup, boolean excludeAbstract, DuringAnalysisAccessImpl access) { assert !registry.containsKey(clazz); if (!excludeAbstract || !Modifier.isAbstract(clazz.getModifiers())) { @@ -247,7 +248,7 @@ public void beforeCompilation(BeforeCompilationAccess a) { @Override public void afterCompilation(AfterCompilationAccess access) { - access.registerAsImmutable(GraalSupport.get().nodeClasses.values(), o -> true); - access.registerAsImmutable(GraalSupport.get().instructionClasses.values(), o -> true); + access.registerAsImmutable(GraalSupport.get().nodeClasses.getValues(), o -> true); + access.registerAsImmutable(GraalSupport.get().instructionClasses.getValues(), o -> true); } } From 7a50e22758df07027c3525de6296c1394a49b62e Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 7 Dec 2021 18:42:14 -0800 Subject: [PATCH 03/23] Handle @Unknown value when field is marked as accessed. --- .../com/oracle/graal/pointsto/BigBang.java | 30 +++++++++---- .../graal/pointsto/meta/AnalysisField.java | 30 ++++++++++--- .../graal/pointsto/meta/AnalysisMethod.java | 21 +++++---- .../graal/pointsto/meta/AnalysisType.java | 4 ++ .../graal/pointsto/meta/AnalysisUniverse.java | 4 ++ .../com/oracle/svm/hosted/FeatureImpl.java | 4 +- .../analysis/NativeImagePointsToAnalysis.java | 9 +++- .../analysis/PointsToUnknownFieldHandler.java | 7 +-- .../hosted/analysis/UnknownFieldHandler.java | 43 ++++++++++--------- .../SubstrateGraphBuilderPlugins.java | 5 +-- .../AnnotationSubstitutionProcessor.java | 2 +- .../hosted/ReflectionObjectReplacer.java | 3 +- 12 files changed, 102 insertions(+), 60 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java index 71dbffd995a0..86bdb0e09a30 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java @@ -24,22 +24,25 @@ */ package com.oracle.graal.pointsto; +import java.io.PrintWriter; +import java.util.List; +import java.util.function.Function; + +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.DebugHandlersFactory; +import org.graalvm.compiler.graph.NodeSourcePosition; +import org.graalvm.compiler.options.OptionValues; + import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.util.Timer; -import jdk.vm.ci.meta.ConstantReflectionProvider; -import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.debug.DebugHandlersFactory; -import org.graalvm.compiler.graph.NodeSourcePosition; -import org.graalvm.compiler.options.OptionValues; -import java.io.PrintWriter; -import java.util.List; -import java.util.function.Function; +import jdk.vm.ci.meta.ConstantReflectionProvider; /** * Central static analysis interface that groups together the functionality of reachability analysis @@ -104,4 +107,13 @@ public interface BigBang extends ReachabilityAnalysis, HeapScanning { default boolean isCallAllowed(PointsToAnalysis bb, AnalysisMethod caller, AnalysisMethod target, NodeSourcePosition srcPosition) { return true; } + + /** + * Callback for when a field is marked as read, written, or unsafe accessed. See + * {@link AnalysisField#isAccessed()} for field accessibility definition. + */ + @SuppressWarnings("unused") + default void onFieldAccessed(AnalysisField field) { + } + } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java index 5b843c17374c..f0d11729a2be 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java @@ -77,6 +77,7 @@ public class AnalysisField implements ResolvedJavaField, OriginalFieldProvider { private AtomicBoolean isAccessed = new AtomicBoolean(); private AtomicBoolean isRead = new AtomicBoolean(); private AtomicBoolean isWritten = new AtomicBoolean(); + private boolean isJNIAccessed; private boolean isUsedInComparison; private AtomicBoolean isUnsafeAccessed; @@ -110,8 +111,9 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField) this.wrapped = wrappedField; this.id = universe.nextFieldId.getAndIncrement(); - readBy = PointstoOptions.TrackAccessChain.getValue(universe.hostVM().options()) ? new ConcurrentHashMap<>() : null; - writtenBy = new ConcurrentHashMap<>(); + boolean trackAccessChain = PointstoOptions.TrackAccessChain.getValue(universe.hostVM().options()); + readBy = trackAccessChain ? new ConcurrentHashMap<>() : null; + writtenBy = trackAccessChain ? new ConcurrentHashMap<>() : null; declaringClass = universe.lookup(wrappedField.getDeclaringClass()); fieldType = getDeclaredType(universe, wrappedField); @@ -129,6 +131,11 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField) } } + private AnalysisUniverse getUniverse() { + /* Access the universe via the declaring class to avoid storing it here. */ + return declaringClass.getUniverse(); + } + private static AnalysisType getDeclaredType(AnalysisUniverse universe, ResolvedJavaField wrappedField) { ResolvedJavaType resolvedType; try { @@ -261,6 +268,9 @@ public void cleanupAfterAnalysis() { public boolean registerAsAccessed() { boolean firstAttempt = AtomicUtils.atomicMark(isAccessed); notifyUpdateAccessInfo(); + if (firstAttempt) { + getUniverse().onFieldAccessed(this); + } return firstAttempt; } @@ -270,6 +280,9 @@ public boolean registerAsRead(MethodTypeFlow method) { if (readBy != null && method != null) { readBy.put(method, Boolean.TRUE); } + if (firstAttempt) { + getUniverse().onFieldAccessed(this); + } return firstAttempt; } @@ -285,14 +298,17 @@ public boolean registerAsWritten(MethodTypeFlow method) { if (writtenBy != null && method != null) { writtenBy.put(method, Boolean.TRUE); } + if (firstAttempt && (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object)) { + getUniverse().onFieldAccessed(this); + } return firstAttempt; } - public void registerAsUnsafeAccessed(AnalysisUniverse universe) { - registerAsUnsafeAccessed(universe, DefaultUnsafePartition.get()); + public void registerAsUnsafeAccessed() { + registerAsUnsafeAccessed(DefaultUnsafePartition.get()); } - public void registerAsUnsafeAccessed(AnalysisUniverse universe, UnsafePartitionKind partitionKind) { + public void registerAsUnsafeAccessed(UnsafePartitionKind partitionKind) { /* * A field can potentially be registered as unsafe accessed multiple times. This is * especially true for the Graal nodes because FieldsOffsetsFeature.registerFields iterates @@ -315,7 +331,7 @@ public void registerAsUnsafeAccessed(AnalysisUniverse universe, UnsafePartitionK if (isStatic()) { /* Register the static field as unsafe accessed with the analysis universe. */ - universe.registerUnsafeAccessedStaticField(this); + getUniverse().registerUnsafeAccessedStaticField(this); } else { /* Register the instance field as unsafe accessed on the declaring type. */ AnalysisType declaringType = getDeclaringClass(); @@ -477,7 +493,7 @@ public boolean isUsedInComparison() { @Override public Field getJavaField() { - return OriginalFieldProvider.getJavaField(getDeclaringClass().universe.getOriginalSnippetReflection(), wrapped); + return OriginalFieldProvider.getJavaField(getUniverse().getOriginalSnippetReflection(), wrapped); } public void addAnalysisFieldObserver(AnalysisFieldObserver observer) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index c3863da0896f..9f7cce7c7e13 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -39,13 +39,13 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; -import com.oracle.graal.pointsto.BigBang; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.java.BytecodeParser.BytecodeParserError; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.flow.AnalysisParsedGraph; @@ -72,7 +72,6 @@ public abstract class AnalysisMethod implements WrappedJavaMethod, GraphProvider, OriginalMethodProvider { - private final AnalysisUniverse universe; public final ResolvedJavaMethod wrapped; private final int id; @@ -103,7 +102,6 @@ public abstract class AnalysisMethod implements WrappedJavaMethod, GraphProvider protected AnalysisMethod[] implementations; public AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped) { - this.universe = universe; this.wrapped = wrapped; this.id = universe.nextMethodId.getAndIncrement(); declaringClass = universe.lookup(wrapped.getDeclaringClass()); @@ -118,7 +116,7 @@ public AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped) { exceptionHandlers = new ExceptionHandler[original.length]; for (int i = 0; i < original.length; i++) { ExceptionHandler h = original[i]; - JavaType catchType = getCatchType(h); + JavaType catchType = getCatchType(universe, h); exceptionHandlers[i] = new ExceptionHandler(h.getStartBCI(), h.getEndBCI(), h.getHandlerBCI(), h.catchTypeCPI(), catchType); } @@ -148,7 +146,7 @@ public String getQualifiedName() { return qualifiedName; } - private JavaType getCatchType(ExceptionHandler handler) { + private JavaType getCatchType(AnalysisUniverse universe, ExceptionHandler handler) { JavaType catchType = handler.getCatchType(); if (catchType == null) { return null; @@ -167,6 +165,11 @@ private JavaType getCatchType(ExceptionHandler handler) { return universe.lookup(resolvedCatchType); } + private AnalysisUniverse getUniverse() { + /* Access the universe via the declaring class to avoid storing it here. */ + return declaringClass.getUniverse(); + } + public void cleanupAfterAnalysis() { if (parsedGraphCacheState.get() instanceof AnalysisParsedGraph) { parsedGraphCacheState.set(GRAPH_CACHE_CLEARED); @@ -308,7 +311,7 @@ public String getName() { @Override public WrappedSignature getSignature() { - return universe.lookup(wrapped.getSignature(), getDeclaringClass()); + return getUniverse().lookup(wrapped.getSignature(), getDeclaringClass()); } @Override @@ -396,7 +399,7 @@ public boolean canBeStaticallyBound() { } public AnalysisMethod[] getImplementations() { - assert universe.analysisDataValid; + assert getUniverse().analysisDataValid; if (implementations == null) { return new AnalysisMethod[0]; } @@ -424,7 +427,7 @@ public ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) @Override public ConstantPool getConstantPool() { - return universe.lookup(wrapped.getConstantPool(), getDeclaringClass()); + return getUniverse().lookup(wrapped.getConstantPool(), getDeclaringClass()); } @Override @@ -519,7 +522,7 @@ public boolean equals(Object obj) { @Override public Executable getJavaMethod() { - return OriginalMethodProvider.getJavaMethod(universe.getOriginalSnippetReflection(), wrapped); + return OriginalMethodProvider.getJavaMethod(getUniverse().getOriginalSnippetReflection(), wrapped); } /** diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index 00dedf487478..1cbce54ead49 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -1085,6 +1085,10 @@ public ResolvedJavaType getHostClass() { return universe.lookup(wrapped.getHostClass()); } + AnalysisUniverse getUniverse() { + return universe; + } + @Override public int compareTo(AnalysisType other) { return Integer.compare(this.id, other.id); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index 28b067f1281b..32acc0955906 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -674,6 +674,10 @@ public AnalysisType objectType() { return objectClass; } + public void onFieldAccessed(AnalysisField field) { + bb.onFieldAccessed(field); + } + public SubstitutionProcessor getSubstitutions() { return substitutions; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index e2f71e41d8ed..d5b9f3c7c68f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -348,7 +348,7 @@ public boolean registerAsUnsafeAccessed(AnalysisField aField) { if (!aField.isUnsafeAccessed()) { /* Register the field as unsafe accessed. */ aField.registerAsAccessed(); - aField.registerAsUnsafeAccessed(bb.getUniverse()); + aField.registerAsUnsafeAccessed(); /* Force the update of registered unsafe loads and stores. */ bb.forceUnsafeUpdate(aField); return true; @@ -374,7 +374,7 @@ public void registerAsUnsafeAccessed(AnalysisField aField, UnsafePartitionKind p if (!aField.isUnsafeAccessed()) { /* Register the field as unsafe accessed. */ aField.registerAsAccessed(); - aField.registerAsUnsafeAccessed(bb.getUniverse(), partitionKind); + aField.registerAsUnsafeAccessed(partitionKind); /* Force the update of registered unsafe loads and stores. */ bb.forceUnsafeUpdate(aField); } 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 e9c414e54b23..3f59f428fe4a 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 @@ -35,6 +35,7 @@ import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; import com.oracle.graal.pointsto.flow.MethodTypeFlow; import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; @@ -63,7 +64,7 @@ public NativeImagePointsToAnalysis(OptionValues options, AnalysisUniverse univer this.annotationSubstitutionProcessor = annotationSubstitutionProcessor; dynamicHubInitializer = new DynamicHubInitializer(metaAccess, unsupportedFeatures, providers.getConstantReflection()); - unknownFieldHandler = new PointsToUnknownFieldHandler(metaAccess); + unknownFieldHandler = new PointsToUnknownFieldHandler(this, metaAccess); callChecker = new CallChecker(); } @@ -79,7 +80,6 @@ public MethodTypeFlowBuilder createMethodTypeFlowBuilder(PointsToAnalysis bb, Me @Override protected void checkObjectGraph(ObjectScanner objectScanner) { - universe.getFields().forEach(field -> unknownFieldHandler.handleUnknownValueField(this, field)); universe.getTypes().stream().filter(AnalysisType::isReachable).forEach(dynamicHubInitializer::initializeMetaData); /* Scan hubs of all types that end up in the native image. */ @@ -108,6 +108,11 @@ public AnnotationSubstitutionProcessor getAnnotationSubstitutionProcessor() { return annotationSubstitutionProcessor; } + @Override + public void onFieldAccessed(AnalysisField field) { + unknownFieldHandler.handleUnknownValueField(field); + } + private void scanHub(ObjectScanner objectScanner, AnalysisType type) { SVMHost svmHost = (SVMHost) hostVM; JavaConstant hubConstant = SubstrateObjectConstant.forObject(svmHost.dynamicHub(type)); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/PointsToUnknownFieldHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/PointsToUnknownFieldHandler.java index bea2f3c102f0..22fe87c5dd6b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/PointsToUnknownFieldHandler.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/PointsToUnknownFieldHandler.java @@ -29,11 +29,12 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisType; + import jdk.vm.ci.meta.JavaKind; public class PointsToUnknownFieldHandler extends UnknownFieldHandler { - public PointsToUnknownFieldHandler(AnalysisMetaAccess metaAccess) { - super(metaAccess); + public PointsToUnknownFieldHandler(BigBang bb, AnalysisMetaAccess metaAccess) { + super(bb, metaAccess); } /** @@ -41,7 +42,7 @@ public PointsToUnknownFieldHandler(AnalysisMetaAccess metaAccess) { * code. It can have multiple declared types provided via annotation. */ @Override - protected void handleUnknownObjectField(BigBang bb, AnalysisField aField, AnalysisType... declaredTypes) { + protected void handleUnknownObjectField(AnalysisField aField, AnalysisType... declaredTypes) { NativeImagePointsToAnalysis analysis = (NativeImagePointsToAnalysis) bb; assert aField.getJavaKind() == JavaKind.Object; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/UnknownFieldHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/UnknownFieldHandler.java index 8078dc808cf1..883b44736679 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/UnknownFieldHandler.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/UnknownFieldHandler.java @@ -24,14 +24,7 @@ */ package com.oracle.svm.hosted.analysis; -import com.oracle.graal.pointsto.BigBang; -import com.oracle.graal.pointsto.meta.AnalysisField; -import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; -import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.svm.core.annotate.UnknownObjectField; -import com.oracle.svm.core.annotate.UnknownPrimitiveField; -import jdk.vm.ci.meta.JavaKind; -import org.graalvm.word.WordBase; +import static jdk.vm.ci.common.JVMCIError.shouldNotReachHere; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -40,28 +33,36 @@ import java.util.List; import java.util.Set; -import static jdk.vm.ci.common.JVMCIError.shouldNotReachHere; +import org.graalvm.word.WordBase; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.annotate.UnknownPrimitiveField; + +import jdk.vm.ci.meta.JavaKind; public abstract class UnknownFieldHandler { + protected final BigBang bb; private Set handledUnknownValueFields = new HashSet<>(); private final AnalysisMetaAccess metaAccess; - public UnknownFieldHandler(AnalysisMetaAccess metaAccess) { + public UnknownFieldHandler(BigBang bb, AnalysisMetaAccess metaAccess) { + this.bb = bb; this.metaAccess = metaAccess; } - public void handleUnknownValueField(BigBang bb, AnalysisField field) { + public void handleUnknownValueField(AnalysisField field) { if (handledUnknownValueFields.contains(field)) { return; } - if (!field.isAccessed()) { - /* - * Field is not reachable yet, so do no process it. In particular, we must not register - * types listed in the @UnknownObjectField annotation as allocated when the field is not - * yet reachable - */ - return; - } + /* + * Only process fields that are accessed. In particular, we must not register types listed + * in the @UnknownObjectField annotation as allocated when the field is not yet accessed. + */ + assert field.isAccessed(); UnknownObjectField unknownObjectField = field.getAnnotation(UnknownObjectField.class); UnknownPrimitiveField unknownPrimitiveField = field.getAnnotation(UnknownPrimitiveField.class); @@ -81,7 +82,7 @@ public void handleUnknownValueField(BigBang bb, AnalysisField field) { * Use the annotation types, instead of the declared type, in the UnknownObjectField * annotated fields initialization. */ - handleUnknownObjectField(bb, field, aAnnotationTypes.toArray(new AnalysisType[0])); + handleUnknownObjectField(field, aAnnotationTypes.toArray(new AnalysisType[0])); } else if (unknownPrimitiveField != null) { assert !Modifier.isFinal(field.getModifiers()) : "@UnknownPrimitiveField annotated field " + field.format("%H.%n") + " cannot be final"; @@ -125,7 +126,7 @@ private List extractAnnotationTypes(AnalysisField field, UnknownOb return aAnnotationTypes; } - protected abstract void handleUnknownObjectField(BigBang bb, AnalysisField aField, AnalysisType... declaredTypes); + protected abstract void handleUnknownObjectField(AnalysisField aField, AnalysisType... declaredTypes); public void cleanupAfterAnalysis() { handledUnknownValueFields = null; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index 8480356b2202..7471b49d5594 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -94,10 +94,8 @@ import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; -import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.nodes.UnsafePartitionLoadNode; import com.oracle.graal.pointsto.nodes.UnsafePartitionStoreNode; import com.oracle.svm.core.FrameAccess; @@ -528,8 +526,7 @@ private static void interceptUpdaterInvoke(MetaAccessProvider metaAccess, Snippe private static void registerAsUnsafeAccessed(MetaAccessProvider metaAccess, Field field) { AnalysisField targetField = (AnalysisField) metaAccess.lookupJavaField(field); targetField.registerAsAccessed(); - AnalysisUniverse universe = (AnalysisUniverse) ((UniverseMetaAccess) metaAccess).getUniverse(); - targetField.registerAsUnsafeAccessed(universe); + targetField.registerAsUnsafeAccessed(); } private static void registerObjectPlugins(InvocationPlugins plugins) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index 9a6fdf00cefd..5d6b7848316f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -249,7 +249,7 @@ public void processComputedValueFields(BigBang bb) { targetFieldDeclaringType.registerAsReachable(); AnalysisField targetField = bb.getMetaAccess().lookupJavaField(cvField.getTargetField()); targetField.registerAsAccessed(); - targetField.registerAsUnsafeAccessed(bb.getUniverse()); + targetField.registerAsUnsafeAccessed(); break; } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java index 703160bb97e2..0458ed3c755a 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java @@ -41,7 +41,6 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.annotate.Delete; import sun.reflect.generics.repository.AbstractRepository; @@ -115,7 +114,7 @@ private void scan(Object original) { if (!analysisField.isUnsafeAccessed()) { analysisField.registerAsAccessed(); - analysisField.registerAsUnsafeAccessed((AnalysisUniverse) metaAccess.getUniverse()); + analysisField.registerAsUnsafeAccessed(); } } } From f08812b42b8687ec0c270aad11c9ddf0e58ae962 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Fri, 3 Dec 2021 14:09:42 -0800 Subject: [PATCH 04/23] Refactor metaAccess to return the concrete universe type. --- .../pointsto/infrastructure/UniverseMetaAccess.java | 2 +- .../graal/pointsto/meta/AnalysisMetaAccess.java | 8 +++++++- .../src/com/oracle/svm/graal/hosted/GraalFeature.java | 4 ++-- .../oracle/svm/hosted/code/FactoryMethodSupport.java | 2 +- .../com/oracle/svm/hosted/meta/HostedMetaAccess.java | 11 ++++++++--- .../svm/jni/hosted/JNICallTrampolineMethod.java | 2 +- 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/UniverseMetaAccess.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/UniverseMetaAccess.java index b4c2d4c77fd8..f76d51959025 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/UniverseMetaAccess.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/UniverseMetaAccess.java @@ -52,7 +52,7 @@ public ResolvedJavaType apply(Class clazz) { return universe.lookup(wrapped.lookupJavaType(clazz)); } }; - private final Universe universe; + protected final Universe universe; private final MetaAccessProvider wrapped; public UniverseMetaAccess(Universe universe, MetaAccessProvider wrapped) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java index 1e2704ce11f4..3f3a613f31bc 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java @@ -57,7 +57,7 @@ public Optional optionalLookupJavaType(Class clazz) { if (result != null) { return Optional.of(result); } - result = ((AnalysisUniverse) getUniverse()).optionalLookup(getWrapped().lookupJavaType(clazz)); + result = getUniverse().optionalLookup(getWrapped().lookupJavaType(clazz)); return Optional.ofNullable(result); } @@ -85,4 +85,10 @@ public int getArrayIndexScale(JavaKind elementKind) { public int getArrayBaseOffset(JavaKind elementKind) { throw shouldNotReachHere(); } + + @Override + public AnalysisUniverse getUniverse() { + return (AnalysisUniverse) universe; + } + } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java index 82572f3f693a..206ab948af82 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java @@ -888,7 +888,7 @@ public void afterCompilation(AfterCompilationAccess a) { CompilationAccessImpl config = (CompilationAccessImpl) a; HostedMetaAccess hMetaAccess = config.getMetaAccess(); - HostedUniverse hUniverse = (HostedUniverse) hMetaAccess.getUniverse(); + HostedUniverse hUniverse = hMetaAccess.getUniverse(); objectReplacer.updateSubstrateDataAfterCompilation(hUniverse, config.getProviders().getConstantFieldProvider()); objectReplacer.registerImmutableObjects(config); @@ -900,7 +900,7 @@ public void afterCompilation(AfterCompilationAccess a) { public void afterHeapLayout(AfterHeapLayoutAccess a) { AfterHeapLayoutAccessImpl config = (AfterHeapLayoutAccessImpl) a; HostedMetaAccess hMetaAccess = config.getMetaAccess(); - HostedUniverse hUniverse = (HostedUniverse) hMetaAccess.getUniverse(); + HostedUniverse hUniverse = hMetaAccess.getUniverse(); objectReplacer.updateSubstrateDataAfterHeapLayout(hUniverse); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethodSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethodSupport.java index bc380c7afbe1..5c56a6b13d4e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethodSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethodSupport.java @@ -67,7 +67,7 @@ public ResolvedJavaMethod lookup(UniverseMetaAccess metaAccess, ResolvedJavaMeth hUniverse = null; aMetaAccess = (AnalysisMetaAccess) metaAccess; } - AnalysisUniverse aUniverse = (AnalysisUniverse) aMetaAccess.getUniverse(); + AnalysisUniverse aUniverse = aMetaAccess.getUniverse(); MetaAccessProvider unwrappedMetaAccess = aMetaAccess.getWrapped(); AnalysisMethod aConstructor = constructor instanceof HostedMethod ? ((HostedMethod) constructor).getWrapped() : (AnalysisMethod) constructor; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java index b4b0bb9fd7b2..51ad8431f7a6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMetaAccess.java @@ -63,7 +63,7 @@ public Optional optionalLookupJavaType(Class clazz) { if (!analysisType.isPresent()) { return Optional.empty(); } - result = ((HostedUniverse) getUniverse()).optionalLookup(analysisType.get()); + result = getUniverse().optionalLookup(analysisType.get()); return Optional.ofNullable(result); } @@ -80,7 +80,7 @@ public HostedMethod lookupJavaMethod(Executable reflectionMethod) { } public HostedMethod optionalLookupJavaMethod(Executable reflectionMethod) { - return ((HostedUniverse) getUniverse()).optionalLookup(getWrapped().lookupJavaMethod(reflectionMethod)); + return getUniverse().optionalLookup(getWrapped().lookupJavaMethod(reflectionMethod)); } @Override @@ -89,7 +89,7 @@ public HostedField lookupJavaField(Field reflectionField) { } public HostedField optionalLookupJavaField(Field reflectionField) { - return ((HostedUniverse) getUniverse()).optionalLookup(getWrapped().lookupJavaField(reflectionField)); + return getUniverse().optionalLookup(getWrapped().lookupJavaField(reflectionField)); } @Override @@ -121,4 +121,9 @@ public int getArrayBaseOffset(JavaKind elementKind) { public int getArrayIndexScale(JavaKind elementKind) { return getObjectLayout().getArrayIndexScale(elementKind); } + + @Override + public HostedUniverse getUniverse() { + return (HostedUniverse) universe; + } } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNICallTrampolineMethod.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNICallTrampolineMethod.java index afcb8b398851..2b9f45dc36c4 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNICallTrampolineMethod.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNICallTrampolineMethod.java @@ -139,7 +139,7 @@ public CompileFunction createCustomCompileFunction() { private int getFieldOffset(HostedProviders providers) { HostedMetaAccess metaAccess = (HostedMetaAccess) providers.getMetaAccess(); - HostedUniverse universe = (HostedUniverse) metaAccess.getUniverse(); + HostedUniverse universe = metaAccess.getUniverse(); AnalysisUniverse analysisUniverse = universe.getBigBang().getUniverse(); HostedField hostedField = universe.lookup(analysisUniverse.lookup(callWrapperField)); assert hostedField.hasLocation(); From 9296bacbe0cbdd52027001a9a404b5937ca3188e Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Mon, 6 Dec 2021 20:49:36 -0800 Subject: [PATCH 05/23] Add BigBang.onTypeInstantiated() API. --- .../com/oracle/graal/pointsto/BigBang.java | 6 ++++ .../graal/pointsto/PointsToAnalysis.java | 20 +++++++++++- .../graal/pointsto/meta/AnalysisType.java | 32 +++++++------------ .../graal/pointsto/meta/AnalysisUniverse.java | 8 +++-- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java index 86bdb0e09a30..a22e0184c0b9 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java @@ -38,6 +38,8 @@ import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.AnalysisType.UsageKind; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.util.Timer; @@ -116,4 +118,8 @@ default boolean isCallAllowed(PointsToAnalysis bb, AnalysisMethod caller, Analys default void onFieldAccessed(AnalysisField field) { } + @SuppressWarnings("unused") + default void onTypeInstantiated(AnalysisType type, UsageKind usageKind) { + } + } 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 bea0c4d5348b..7963f14bb380 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 @@ -45,7 +45,6 @@ import java.util.concurrent.atomic.AtomicLongArray; import java.util.function.Function; -import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; @@ -77,6 +76,7 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import com.oracle.graal.pointsto.reports.StatisticsPrinter; import com.oracle.graal.pointsto.typestate.PointsToStats; import com.oracle.graal.pointsto.typestate.TypeState; @@ -774,6 +774,24 @@ private static ForkJoinPool.ForkJoinWorkerThreadFactory debugThreadFactory(Debug return pool -> new SubstrateWorkerThread(pool, debug); } + @Override + public void onTypeInstantiated(AnalysisType type, AnalysisType.UsageKind usageKind) { + /* Register the type as instantiated with all its super types. */ + + assert type.isAllocated() || type.isInHeap(); + assert type.isArray() || (type.isInstanceClass() && !type.isAbstract()) : this; + universe.hostVM().checkForbidden(type, usageKind); + + TypeState typeState = TypeState.forExactType(this, type, true); + TypeState typeStateNonNull = TypeState.forExactType(this, type, false); + + /* Register the instantiated type with its super types. */ + type.forAllSuperTypes(t -> { + t.instantiatedTypes.addState(this, typeState); + t.instantiatedTypesNonNull.addState(this, typeStateNonNull); + }); + } + public static class ConstantObjectsProfiler { static final ConcurrentHashMap constantTypes = new ConcurrentHashMap<>(ESTIMATED_NUMBER_OF_TYPES); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index 1cbce54ead49..f69ce44cc804 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -25,7 +25,6 @@ package com.oracle.graal.pointsto.meta; import java.lang.annotation.Annotation; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -410,7 +409,7 @@ public static boolean verifyAssignableTypes(BigBang bb) { public boolean registerAsInHeap() { registerAsReachable(); if (AtomicUtils.atomicMark(isInHeap)) { - registerAsInstantiated(UsageKind.InHeap); + universe.onTypeInstantiated(this, UsageKind.InHeap); return true; } return false; @@ -422,29 +421,12 @@ public boolean registerAsInHeap() { public boolean registerAsAllocated(Node node) { registerAsReachable(); if (AtomicUtils.atomicMark(isAllocated)) { - registerAsInstantiated(UsageKind.Allocated); + universe.onTypeInstantiated(this, UsageKind.Allocated); return true; } return false; } - /** Register the type as instantiated with all its super types. */ - private void registerAsInstantiated(UsageKind usageKind) { - assert isAllocated.get() || isInHeap.get(); - assert isArray() || (isInstanceClass() && !Modifier.isAbstract(getModifiers())) : this; - universe.hostVM.checkForbidden(this, usageKind); - - PointsToAnalysis bb = ((PointsToAnalysis) universe.getBigbang()); - TypeState typeState = TypeState.forExactType(bb, this, true); - TypeState typeStateNonNull = TypeState.forExactType(bb, this, false); - - /* Register the instantiated type with its super types. */ - forAllSuperTypes(t -> { - t.instantiatedTypes.addState(bb, typeState); - t.instantiatedTypesNonNull.addState(bb, typeStateNonNull); - }); - } - /** * Register the type as assignable with all its super types. This is a blocking call to ensure * that the type is registered with all its super types before it is propagated by the analysis @@ -504,7 +486,7 @@ private void markReachable() { * reachable directly, but also through java.lang.GenericDeclaration, so it will be processed * twice. */ - private void forAllSuperTypes(Consumer superTypeConsumer) { + public void forAllSuperTypes(Consumer superTypeConsumer) { forAllSuperTypes(superTypeConsumer, true); } @@ -1059,6 +1041,14 @@ public boolean isLinked() { return wrapped.isLinked(); } + public boolean isInHeap() { + return isInHeap.get(); + } + + public boolean isAllocated() { + return isAllocated.get(); + } + @Override public void link() { wrapped.link(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index 32acc0955906..6dfa91890ae3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -52,6 +52,7 @@ import com.oracle.graal.pointsto.infrastructure.WrappedConstantPool; import com.oracle.graal.pointsto.infrastructure.WrappedJavaType; import com.oracle.graal.pointsto.infrastructure.WrappedSignature; +import com.oracle.graal.pointsto.meta.AnalysisType.UsageKind; import com.oracle.graal.pointsto.util.AnalysisError; import jdk.vm.ci.code.BytecodePosition; @@ -678,6 +679,10 @@ public void onFieldAccessed(AnalysisField field) { bb.onFieldAccessed(field); } + public void onTypeInstantiated(AnalysisType type, UsageKind usage) { + bb.onTypeInstantiated(type, usage); + } + public SubstitutionProcessor getSubstitutions() { return substitutions; } @@ -694,7 +699,4 @@ public void setBigBang(BigBang bb) { this.bb = bb; } - public BigBang getBigbang() { - return bb; - } } From 50fe5f68305d239563567f1aa691bb0f3afb4753 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 7 Dec 2021 18:02:09 -0800 Subject: [PATCH 06/23] Add CustomFieldValueProvider.valueAvailability(). It is used to guarantee that ComputedValueFields are available when they are read. --- .../core/annotate/RecomputeFieldValue.java | 42 ++++++- .../oracle/svm/core/jdk/VarHandleFeature.java | 5 + .../svm/core/meta/ReadableJavaField.java | 12 +- .../graal/hosted/FieldsOffsetsFeature.java | 5 + .../AnnotationSubstitutionProcessor.java | 2 +- .../hosted/substitute/ComputedValueField.java | 105 +++++++++++------- .../reflect/hosted/FieldOffsetComputer.java | 6 + .../svm/truffle/TruffleBaseFeature.java | 5 + 8 files changed, 137 insertions(+), 45 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java index cfc12163f2de..bec2e0489e28 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/RecomputeFieldValue.java @@ -110,11 +110,49 @@ enum Kind { Custom, } + enum ValueAvailability { + DuringAnalysis, + AfterAnalysis, + AfterCompilation + } + + interface CustomFieldValueProvider { + + /** + * When is the value for this custom computation available? By default, it is assumed that + * the value is available {@link ValueAvailability#DuringAnalysis during analysis}. + */ + default ValueAvailability valueAvailability() { + return ValueAvailability.DuringAnalysis; + } + + /** Return true if value is available during analysis, false otherwise. */ + default boolean isAvailableDuringAnalysis() { + return valueAvailability() == ValueAvailability.DuringAnalysis; + } + + /** + * Return true if value is available only after analysis, i.e., it depends on data computed + * by the analysis (such as field offsets), false otherwise. + */ + default boolean isAvailableAfterAnalysis() { + return valueAvailability() == ValueAvailability.AfterAnalysis; + } + + /** + * Return true if value is available only after compilation, i.e., it depends on data + * computed during compilation, false otherwise. + */ + default boolean isAvailableAfterCompilation() { + return valueAvailability() == ValueAvailability.AfterCompilation; + } + } + /** * Custom recomputation of field values. A class implementing this interface must have a * no-argument constructor, which is used to instantiate it before invoking {@link #compute}. */ - interface CustomFieldValueComputer { + interface CustomFieldValueComputer extends CustomFieldValueProvider { /** * Computes the new field value. This method can already be invoked during the analysis, * especially when it computes the value of an object field that needs to be visited. @@ -140,7 +178,7 @@ interface CustomFieldValueComputer { * original value, but also requires the original field to be present, e.g., it cannot be use * for {@link Inject injected fields}. */ - interface CustomFieldValueTransformer { + interface CustomFieldValueTransformer extends CustomFieldValueProvider { /** * Computes the new field value. This method can already be invoked during the analysis, * especially when it computes the value of an object field that needs to be visited. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java index d19a0b91920c..77f9fe5196df 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java @@ -221,6 +221,11 @@ class VarHandleInfo { } class VarHandleFieldOffsetComputer implements RecomputeFieldValue.CustomFieldValueComputer { + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterAnalysis; + } + @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object varHandle) { Field field = ImageSingletons.lookup(VarHandleFeature.class).findVarHandleField(varHandle); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java index e22ee8e409c1..7b5b885a2e6e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java @@ -33,7 +33,9 @@ public interface ReadableJavaField extends ResolvedJavaField { static JavaConstant readFieldValue(MetaAccessProvider metaAccess, ConstantReflectionProvider originalConstantReflection, ResolvedJavaField javaField, JavaConstant javaConstant) { if (javaField instanceof ReadableJavaField) { - return ((ReadableJavaField) javaField).readValue(metaAccess, javaConstant); + ReadableJavaField readableField = (ReadableJavaField) javaField; + assert readableField.isValueAvailable(); + return readableField.readValue(metaAccess, javaConstant); } else { return originalConstantReflection.readFieldValue(javaField, javaConstant); } @@ -41,6 +43,14 @@ static JavaConstant readFieldValue(MetaAccessProvider metaAccess, ConstantReflec JavaConstant readValue(MetaAccessProvider metaAccess, JavaConstant receiver); + default boolean isValueAvailableDuringAnalysis() { + return true; + } + + default boolean isValueAvailable() { + return true; + } + boolean allowConstantFolding(); boolean injectFinalForRuntimeCompilation(); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java index 2b83eb70a608..7e60908cef60 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java @@ -72,6 +72,11 @@ public class FieldsOffsetsFeature implements Feature { private static final Unsafe UNSAFE = GraalUnsafeAccess.getUnsafe(); abstract static class IterationMaskRecomputation implements RecomputeFieldValue.CustomFieldValueComputer { + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterAnalysis; + } + @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { Edges edges = getEdges((NodeClass) receiver); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index 5d6b7848316f..c1a63f80db56 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -864,7 +864,7 @@ private ResolvedJavaField fieldValueRecomputation(Class originalClass, Resolv targetName = recomputeAnnotation.name(); isFinal = recomputeAnnotation.isFinal(); disableCaching = recomputeAnnotation.disableCaching(); - guarantee(!isFinal || ComputedValueField.isFinalValid(kind), "@%s with %s can never be final during analysis: unset isFinal in the annotation on %s", + guarantee(!isFinal || !ComputedValueField.isOffsetRecomputation(kind), "@%s with %s can never be final during analysis: unset isFinal in the annotation on %s", RecomputeFieldValue.class.getSimpleName(), kind, annotated); guarantee(!isFinal || !disableCaching, "@%s can not be final if caching is disabled: unset isFinal in the annotation on %s", RecomputeFieldValue.class.getSimpleName(), kind, annotated); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java index 8ecffa14c994..2cf996f90cb4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java @@ -24,6 +24,9 @@ */ package com.oracle.svm.hosted.substitute; +import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.AtomicFieldUpdaterOffset; +import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.FieldOffset; +import static com.oracle.svm.core.annotate.RecomputeFieldValue.Kind.TranslateFieldOffset; import static com.oracle.svm.core.util.VMError.guarantee; import static com.oracle.svm.core.util.VMError.shouldNotReachHere; @@ -46,8 +49,10 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.util.GraalAccess; +import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; +import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueProvider; import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueTransformer; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.meta.ReadableJavaField; @@ -76,14 +81,18 @@ public class ComputedValueField implements ReadableJavaField, OriginalFieldProvider, ComputedValue { private static final Unsafe UNSAFE = GraalUnsafeAccess.getUnsafe(); + private static final EnumSet offsetComputationKinds = EnumSet.of(FieldOffset, TranslateFieldOffset, AtomicFieldUpdaterOffset); private final ResolvedJavaField original; private final ResolvedJavaField annotated; private final RecomputeFieldValue.Kind kind; private final Class targetClass; private final Field targetField; + private final CustomFieldValueProvider customValueProvider; private final boolean isFinal; private final boolean disableCaching; + private final boolean isCustomValueAvailableDuringAnalysis; + private final boolean isOffsetField; private JavaConstant constantValue; @@ -95,6 +104,10 @@ public class ComputedValueField implements ReadableJavaField, OriginalFieldProvi private JavaConstant valueCacheNullKey; private final ReentrantReadWriteLock valueCacheLock = new ReentrantReadWriteLock(); + public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotated, RecomputeFieldValue.Kind kind, Class targetClass, String targetName, boolean isFinal) { + this(original, annotated, kind, targetClass, targetName, isFinal, false); + } + public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotated, RecomputeFieldValue.Kind kind, Class targetClass, String targetName, boolean isFinal, boolean disableCaching) { assert original != null; @@ -106,7 +119,10 @@ public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotate this.targetClass = targetClass; this.isFinal = isFinal; this.disableCaching = disableCaching; + this.isOffsetField = isOffsetRecomputation(kind); + boolean customValueAvailableDuringAnalysis = true; + CustomFieldValueProvider customProvider = null; Field f = null; switch (kind) { case Reset: @@ -119,20 +135,44 @@ public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotate throw shouldNotReachHere("could not find target field " + targetClass.getName() + "." + targetName + " for alias " + annotated.format("%H.%n")); } break; + case Custom: + try { + Constructor[] constructors = targetClass.getDeclaredConstructors(); + if (constructors.length != 1) { + throw UserError.abort("The custom field value computer class %s has more than one constructor", targetClass.getName()); + } + Constructor constructor = constructors[0]; + + Object[] constructorArgs = new Object[constructor.getParameterCount()]; + for (int i = 0; i < constructorArgs.length; i++) { + constructorArgs[i] = configurationValue(constructor.getParameterTypes()[i]); + } + constructor.setAccessible(true); + customProvider = (CustomFieldValueProvider) constructor.newInstance(constructorArgs); + customValueAvailableDuringAnalysis = customProvider.isAvailableDuringAnalysis(); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException ex) { + throw shouldNotReachHere("Error creating custom field value computer for alias " + annotated.format("%H.%n"), ex); + } } - guarantee(!isFinal || isFinalValid(kind)); + guarantee(!isFinal || !isOffsetField); + this.isCustomValueAvailableDuringAnalysis = customValueAvailableDuringAnalysis; this.targetField = f; + this.customValueProvider = customProvider; this.valueCache = EconomicMap.create(); } - public ComputedValueField(ResolvedJavaField original, ResolvedJavaField annotated, RecomputeFieldValue.Kind kind, Class targetClass, String targetName, boolean isFinal) { - this(original, annotated, kind, targetClass, targetName, isFinal, false); + public static boolean isOffsetRecomputation(RecomputeFieldValue.Kind kind) { + return offsetComputationKinds.contains(kind); } - public static boolean isFinalValid(RecomputeFieldValue.Kind kind) { - EnumSet finalIllegal = EnumSet.of(RecomputeFieldValue.Kind.FieldOffset, - RecomputeFieldValue.Kind.TranslateFieldOffset, RecomputeFieldValue.Kind.AtomicFieldUpdaterOffset); - return !finalIllegal.contains(kind); + @Override + public boolean isValueAvailableDuringAnalysis() { + return isCustomValueAvailableDuringAnalysis && !isOffsetField; + } + + @Override + public boolean isValueAvailable() { + return constantValue != null || BuildPhaseProvider.isAnalysisFinished() || isValueAvailableDuringAnalysis(); } public ResolvedJavaField getAnnotated() { @@ -292,43 +332,26 @@ private JavaConstant computeValue(MetaAccessProvider metaAccess, JavaConstant re result = translateFieldOffset(metaAccess, receiver, targetClass); break; case Custom: - try { - Constructor[] constructors = targetClass.getDeclaredConstructors(); - if (constructors.length != 1) { - throw UserError.abort("The custom field value computer class %s has more than one constructor", targetClass.getName()); - } - Constructor constructor = constructors[0]; - - Object[] constructorArgs = new Object[constructor.getParameterCount()]; - for (int i = 0; i < constructorArgs.length; i++) { - constructorArgs[i] = configurationValue(constructor.getParameterTypes()[i]); - } - constructor.setAccessible(true); - Object instance = constructor.newInstance(constructorArgs); - - Object receiverValue = receiver == null ? null : originalSnippetReflection.asObject(Object.class, receiver); - Object newValue; - if (instance instanceof CustomFieldValueComputer) { - newValue = ((CustomFieldValueComputer) instance).compute(metaAccess, original, annotated, receiverValue); - } else if (instance instanceof CustomFieldValueTransformer) { - JavaConstant originalValueConstant = ReadableJavaField.readFieldValue(metaAccess, GraalAccess.getOriginalProviders().getConstantReflection(), original, receiver); - Object originalValue; - if (originalValueConstant.getJavaKind().isPrimitive()) { - originalValue = originalValueConstant.asBoxedPrimitive(); - } else { - originalValue = originalSnippetReflection.asObject(Object.class, originalValueConstant); - } - newValue = ((CustomFieldValueTransformer) instance).transform(metaAccess, original, annotated, receiverValue, originalValue); + Object receiverValue = receiver == null ? null : originalSnippetReflection.asObject(Object.class, receiver); + Object newValue; + if (customValueProvider instanceof CustomFieldValueComputer) { + newValue = ((CustomFieldValueComputer) customValueProvider).compute(metaAccess, original, annotated, receiverValue); + } else if (customValueProvider instanceof CustomFieldValueTransformer) { + JavaConstant originalValueConstant = ReadableJavaField.readFieldValue(metaAccess, GraalAccess.getOriginalProviders().getConstantReflection(), original, receiver); + Object originalValue; + if (originalValueConstant.getJavaKind().isPrimitive()) { + originalValue = originalValueConstant.asBoxedPrimitive(); } else { - throw UserError.abort("The custom field value computer class %s does not implement %s or %s", targetClass.getName(), - CustomFieldValueComputer.class.getSimpleName(), CustomFieldValueTransformer.class.getSimpleName()); + originalValue = originalSnippetReflection.asObject(Object.class, originalValueConstant); } - - result = originalSnippetReflection.forBoxed(annotated.getJavaKind(), newValue); - assert result.getJavaKind() == annotated.getJavaKind(); - } catch (InvocationTargetException | InstantiationException | IllegalAccessException ex) { - throw shouldNotReachHere("Error performing field recomputation for alias " + annotated.format("%H.%n"), ex); + newValue = ((CustomFieldValueTransformer) customValueProvider).transform(metaAccess, original, annotated, receiverValue, originalValue); + } else { + throw UserError.abort("The custom field value computer class %s does not implement %s or %s", targetClass.getName(), + CustomFieldValueComputer.class.getSimpleName(), CustomFieldValueTransformer.class.getSimpleName()); } + + result = originalSnippetReflection.forBoxed(annotated.getJavaKind(), newValue); + assert result.getJavaKind() == annotated.getJavaKind(); break; default: throw shouldNotReachHere("Field recomputation of kind " + kind + " for field " + original.format("%H.%n") + diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/FieldOffsetComputer.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/FieldOffsetComputer.java index af4dc5b6d7e8..68f5b7b79228 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/FieldOffsetComputer.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/FieldOffsetComputer.java @@ -28,6 +28,7 @@ import java.lang.reflect.Field; +import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.meta.HostedField; @@ -38,6 +39,11 @@ public class FieldOffsetComputer implements CustomFieldValueComputer { + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterAnalysis; + } + @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { VMError.guarantee(metaAccess instanceof HostedMetaAccess, "Field offset computation must be done during compilation."); diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 3ea37735132f..cd9cba2fa0c5 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -726,6 +726,11 @@ public static final class OffsetTransformer implements RecomputeFieldValue.Custo // Checkstyle: resume } + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterAnalysis; + } + @Override public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) { From 871554252f2846344948133204fb8e2dcf35533d Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 7 Dec 2021 09:35:55 -0800 Subject: [PATCH 07/23] Minor improvements. --- .../oracle/graal/pointsto/DefaultAnalysisPolicy.java | 12 ++++++++++-- .../graal/pointsto/flow/MethodTypeFlowBuilder.java | 6 ++++-- .../oracle/graal/pointsto/meta/AnalysisUniverse.java | 2 +- .../hosted/analysis/NativeImagePointsToAnalysis.java | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/DefaultAnalysisPolicy.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/DefaultAnalysisPolicy.java index 7650d5278a9f..5f27088aab59 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/DefaultAnalysisPolicy.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/DefaultAnalysisPolicy.java @@ -30,9 +30,9 @@ import java.util.Collections; import java.util.List; -import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import org.graalvm.compiler.options.OptionValues; +import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.flow.AbstractSpecialInvokeTypeFlow; import com.oracle.graal.pointsto.flow.AbstractVirtualInvokeTypeFlow; import com.oracle.graal.pointsto.flow.ActualReturnTypeFlow; @@ -47,6 +47,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import com.oracle.graal.pointsto.typestate.TypeState; import com.oracle.graal.pointsto.typestore.ArrayElementsTypeStore; import com.oracle.graal.pointsto.typestore.FieldTypeStore; @@ -208,7 +209,14 @@ public void onObservedUpdate(PointsToAnalysis bb) { continue; } - AnalysisMethod method = type.resolveConcreteMethod(getTargetMethod()); + AnalysisMethod method = null; + try { + method = type.resolveConcreteMethod(targetMethod); + } catch (UnsupportedFeatureException ex) { + /* Register the ex with UnsupportedFeatures and allow analysis to continue. */ + bb.getUnsupportedFeatures().addMessage("resolve_" + targetMethod.format("%H.%n(%p)"), targetMethod, ex.getMessage()); + } + if (method == null || Modifier.isAbstract(method.getModifiers())) { /* * Type states can be conservative, i.e., we can have receiver types that do not 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 e275eec692af..614f9216b959 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 @@ -134,6 +134,7 @@ import jdk.vm.ci.code.BytecodePosition; import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; public class MethodTypeFlowBuilder { @@ -316,12 +317,13 @@ public void registerUsedElements(boolean registerEmbeddedRoots) { } private void registerEmbeddedRoot(ConstantNode cn) { - if (bb.scanningPolicy().trackConstant(bb, cn.asJavaConstant())) { + JavaConstant root = cn.asJavaConstant(); + if (bb.scanningPolicy().trackConstant(bb, root)) { BytecodePosition position = cn.getNodeSourcePosition(); if (position == null) { position = new BytecodePosition(null, method, 0); } - bb.getUniverse().registerEmbeddedRoot(cn.asJavaConstant(), position); + bb.getUniverse().registerEmbeddedRoot(root, position); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index 6dfa91890ae3..b82c81e9881b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -489,7 +489,7 @@ public JavaConstant toHosted(JavaConstant constant) { if (constant == null) { return null; } else if (constant.getJavaKind().isObject() && !constant.isNull()) { - return originalSnippetReflection.forObject(getSnippetReflection().asObject(Object.class, constant)); + return originalSnippetReflection.forObject(snippetReflection.asObject(Object.class, constant)); } else { return constant; } 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 3f59f428fe4a..b1baf065e7be 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 @@ -60,7 +60,7 @@ public class NativeImagePointsToAnalysis extends PointsToAnalysis implements Inf public NativeImagePointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostedProviders providers, AnnotationSubstitutionProcessor annotationSubstitutionProcessor, ForkJoinPool executor, Runnable heartbeatCallback, UnsupportedFeatures unsupportedFeatures) { - super(options, universe, providers, universe.hostVM(), executor, heartbeatCallback, new SubstrateUnsupportedFeatures(), SubstrateOptions.parseOnce()); + super(options, universe, providers, universe.hostVM(), executor, heartbeatCallback, unsupportedFeatures, SubstrateOptions.parseOnce()); this.annotationSubstitutionProcessor = annotationSubstitutionProcessor; dynamicHubInitializer = new DynamicHubInitializer(metaAccess, unsupportedFeatures, providers.getConstantReflection()); From 75368111fba1ad726d2346c767c40d65bf1a468b Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 7 Dec 2021 18:36:55 -0800 Subject: [PATCH 08/23] Refactor heap scanning. --- .../org.graalvm.nativeimage/snapshot.sigtest | 1 + .../graalvm/nativeimage/hosted/Feature.java | 7 + substratevm/mx.substratevm/suite.py | 2 + .../AnalysisObjectScanningObserver.java | 22 +- .../com/oracle/graal/pointsto/BigBang.java | 3 + .../oracle/graal/pointsto/ObjectScanner.java | 192 ++++-- .../pointsto/ObjectScanningObserver.java | 28 +- .../graal/pointsto/PointsToAnalysis.java | 91 ++- .../pointsto/heap/HeapSnapshotVerifier.java | 235 +++++++ .../oracle/graal/pointsto/heap/ImageHeap.java | 161 +++++ .../graal/pointsto/heap/ImageHeapScanner.java | 636 ++++++++++++++++++ .../heap/value/EagerValueSupplier.java | 44 ++ .../heap/value/LazyValueSupplier.java | 51 ++ .../pointsto/heap/value/ValueSupplier.java | 59 ++ .../graal/pointsto/meta/AnalysisField.java | 18 +- .../graal/pointsto/meta/AnalysisType.java | 14 + .../graal/pointsto/meta/AnalysisUniverse.java | 24 + .../graal/pointsto/meta/HostedProviders.java | 11 + .../oracle/graal/pointsto/meta/TypeData.java | 70 ++ .../reports/AnalysisHeapHistogramPrinter.java | 13 +- .../pointsto/reports/ObjectTreePrinter.java | 19 +- .../graal/pointsto/reports/ReportUtils.java | 2 +- .../graal/pointsto/util/AnalysisFuture.java | 8 + .../posix/PosixSunSecuritySubstitutions.java | 6 +- .../snippets/SubstrateAllocationSnippets.java | 3 +- .../svm/core/hub/AnnotationTypeSupport.java | 7 +- .../com/oracle/svm/core/hub/DynamicHub.java | 4 +- .../svm/core/jdk/SecuritySubstitutions.java | 7 + .../oracle/svm/core/util/ImageHeapMap.java | 1 + .../com/oracle/svm/graal/GraalSupport.java | 49 +- .../graal/hosted/FieldsOffsetsFeature.java | 9 +- .../oracle/svm/graal/hosted/GraalFeature.java | 6 +- .../svm/graal/hosted/GraalObjectReplacer.java | 37 +- .../SubstrateRuntimeConfigurationBuilder.java | 2 +- .../com/oracle/svm/hosted/FeatureImpl.java | 30 + .../oracle/svm/hosted/ImageClassLoader.java | 8 +- .../com/oracle/svm/hosted/LoggingFeature.java | 2 + .../svm/hosted/NativeImageGenerator.java | 32 +- .../oracle/svm/hosted/ResourcesFeature.java | 1 + .../svm/hosted/SecurityServicesFeature.java | 72 +- .../svm/hosted/ServiceLoaderFeature.java | 1 + .../ameta/AnalysisConstantFieldProvider.java | 7 +- .../AnalysisConstantReflectionProvider.java | 36 +- .../analysis/DynamicHubInitializer.java | 22 +- .../analysis/NativeImagePointsToAnalysis.java | 19 +- .../analysis/heap/SVMImageHeapScanner.java | 146 ++++ .../analysis/heap/SVMImageHeapVerifier.java | 56 ++ .../hosted/annotation/AnnotationSupport.java | 5 +- .../annotation/AnnotationTypeFeature.java | 13 +- .../ClassInitializationFeature.java | 7 +- .../hosted/image/NativeImageCodeCache.java | 8 +- .../svm/hosted/jdk/JDKRegistrations.java | 3 + .../jdk/localization/LocalizationFeature.java | 33 +- .../hosted/substitute/ComputedValueField.java | 7 + .../svm/hosted/thread/VMThreadMTFeature.java | 7 +- .../svm/hosted/thread/VMThreadSTFeature.java | 7 +- .../src/com/oracle/svm/jfr/JfrFeature.java | 5 +- .../svm/jni/access/JNIAccessFeature.java | 4 +- .../svm/jni/access/JNIAccessibleClass.java | 7 +- .../svm/jni/access/JNINativeLinkage.java | 4 +- .../hosted/JNINativeCallWrapperMethod.java | 2 +- .../methodhandles/MethodHandleFeature.java | 49 +- .../reflect/hosted/ReflectionDataBuilder.java | 8 +- .../proxy/hosted/DynamicProxyFeature.java | 7 +- .../hosted/SerializationFeature.java | 8 + .../svm/truffle/TruffleBaseFeature.java | 51 +- .../oracle/svm/truffle/TruffleFeature.java | 13 + 67 files changed, 2240 insertions(+), 282 deletions(-) create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/EagerValueSupplier.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/LazyValueSupplier.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/ValueSupplier.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/TypeData.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapVerifier.java diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest index 2083fb7967bf..58c08e078c20 100644 --- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest +++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest @@ -893,6 +893,7 @@ CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$Dur intf org.graalvm.nativeimage.hosted.Feature$BeforeAnalysisAccess intf org.graalvm.nativeimage.hosted.Feature$QueryReachabilityAccess meth public abstract void requireAnalysisIteration() +meth public abstract void rescanObject(java.lang.Object) CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$DuringSetupAccess outer org.graalvm.nativeimage.hosted.Feature diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java index e8a3492499e8..e02f36f2551f 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java @@ -277,6 +277,13 @@ interface DuringAnalysisAccess extends BeforeAnalysisAccess, QueryReachabilityAc * @since 19.0 */ void requireAnalysisIteration(); + + /** + * Rescan an object to be included in the image heap. + * + * @since 22.0 + */ + void rescanObject(Object obj); } /** diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 92c29f5609ea..eb71bc81fb96 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -1457,6 +1457,8 @@ "exports" : [ "com.oracle.graal.pointsto", "com.oracle.graal.pointsto.api", + "com.oracle.graal.pointsto.heap", + "com.oracle.graal.pointsto.heap.value", "com.oracle.graal.pointsto.reports", "com.oracle.graal.pointsto.constraints", "com.oracle.graal.pointsto.util", diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java index 3dca19404a31..f986dd65a845 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java @@ -24,6 +24,7 @@ */ package com.oracle.graal.pointsto; +import com.oracle.graal.pointsto.ObjectScanner.ScanReason; import com.oracle.graal.pointsto.flow.ArrayElementsTypeFlow; import com.oracle.graal.pointsto.flow.FieldTypeFlow; import com.oracle.graal.pointsto.flow.context.object.AnalysisObject; @@ -42,23 +43,25 @@ public AnalysisObjectScanningObserver(BigBang bb) { } @Override - public void forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue) { + public boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { if (!field.isWritten()) { - field.registerAsWritten(null); + return field.registerAsWritten(null); } + return false; } @Override - public void forNullFieldValue(JavaConstant receiver, AnalysisField field) { + public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, ScanReason reason) { FieldTypeFlow fieldTypeFlow = getFieldTypeFlow(field, receiver); if (!fieldTypeFlow.getState().canBeNull()) { /* Signal that the field can contain null. */ - fieldTypeFlow.addState(getAnalysis(), TypeState.forNull()); + return fieldTypeFlow.addState(getAnalysis(), TypeState.forNull()); } + return false; } @Override - public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue) { + public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { PointsToAnalysis analysis = getAnalysis(); AnalysisType fieldType = analysis.getMetaAccess().lookupJavaType(analysis.getSnippetReflectionProvider().asObject(Object.class, fieldValue).getClass()); assert fieldType.isInstantiated() : fieldType; @@ -94,16 +97,17 @@ private FieldTypeFlow getFieldTypeFlow(AnalysisField field, JavaConstant receive } @Override - public void forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex) { + public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex, ScanReason reason) { ArrayElementsTypeFlow arrayObjElementsFlow = getArrayElementsFlow(array, arrayType); if (!arrayObjElementsFlow.getState().canBeNull()) { /* Signal that the constant array can contain null. */ - arrayObjElementsFlow.addState(getAnalysis(), TypeState.forNull()); + return arrayObjElementsFlow.addState(getAnalysis(), TypeState.forNull()); } + return false; } @Override - public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex) { + public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex, ScanReason reason) { assert elementType.isInstantiated() : elementType; ArrayElementsTypeFlow arrayObjElementsFlow = getArrayElementsFlow(array, arrayType); PointsToAnalysis analysis = getAnalysis(); @@ -125,7 +129,7 @@ private ArrayElementsTypeFlow getArrayElementsFlow(JavaConstant array, AnalysisT } @Override - public void forScannedConstant(JavaConstant value, ObjectScanner.ScanReason reason) { + public void forScannedConstant(JavaConstant value, ScanReason reason) { PointsToAnalysis analysis = getAnalysis(); Object valueObj = analysis.getSnippetReflectionProvider().asObject(Object.class, value); AnalysisType type = bb.getMetaAccess().lookupJavaType(valueObj.getClass()); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java index a22e0184c0b9..ce56018f46d0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java @@ -122,4 +122,7 @@ default void onFieldAccessed(AnalysisField field) { default void onTypeInstantiated(AnalysisType type, UsageKind usageKind) { } + @SuppressWarnings("unused") + default void onTypeScanned(AnalysisType type) { + } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index 8de2aa860a51..86966df7c6b5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -59,7 +59,7 @@ * structure can be reused over multiple scanning iterations to save CPU resources. (For details * {@link ReusableSet}). */ -public abstract class ObjectScanner { +public class ObjectScanner { protected final BigBang bb; private final ReusableSet scannedObjects; @@ -67,6 +67,10 @@ public abstract class ObjectScanner { private final Deque worklist; private final ObjectScanningObserver scanningObserver; + public ObjectScanner(BigBang bb, ObjectScanningObserver scanningObserver) { + this(bb, null, new ObjectScanner.ReusableSet(), scanningObserver); + } + public ObjectScanner(BigBang bb, CompletionExecutor executor, ReusableSet scannedObjects, ObjectScanningObserver scanningObserver) { this.bb = bb; this.scanningObserver = scanningObserver; @@ -80,6 +84,10 @@ public ObjectScanner(BigBang bb, CompletionExecutor executor, ReusableSet scanne this.scannedObjects = scannedObjects; } + public void scanBootImageHeapRoots() { + scanBootImageHeapRoots(null, null); + } + public void scanBootImageHeapRoots(Comparator fieldComparator, Comparator embeddedRootComparator) { // scan the original roots // the original roots are all the static fields, of object type, that were accessed @@ -125,7 +133,9 @@ default DebugContext getDebug(OptionValues options, List f private void scanEmbeddedRoot(JavaConstant root, BytecodePosition position) { AnalysisMethod method = (AnalysisMethod) position.getMethod(); try { - scanConstant(root, new MethodScan(method, position)); + EmbeddedRootScan reason = new EmbeddedRootScan(method, position, root); + scanConstant(root, reason); + scanningObserver.forEmbeddedRoot(root, reason); } catch (UnsupportedFeatureException ex) { bb.getUnsupportedFeatures().addMessage(method.format("%H.%n(%p)"), method, ex.getMessage(), null, ex); } @@ -145,34 +155,33 @@ protected final void scanRootField(AnalysisField field) { * * @param field the scanned field * @param receiver the receiver object - * @param previous reference to the work list entry containing parent object */ - protected final void scanField(AnalysisField field, JavaConstant receiver, WorklistEntry previous) { - ScanReason reason = new FieldScan(field); + protected final void scanField(AnalysisField field, JavaConstant receiver, ScanReason prevReason) { + ScanReason reason = new FieldScan(field, receiver, prevReason); try { JavaConstant fieldValue = bb.getConstantReflectionProvider().readFieldValue(field, receiver); if (fieldValue == null) { StringBuilder backtrace = new StringBuilder(); - buildObjectBacktrace(reason, previous, backtrace); + buildObjectBacktrace(bb, reason, backtrace); throw AnalysisError.shouldNotReachHere("Could not find field " + field.format("%H.%n") + (receiver == null ? "" : " on " + constantType(bb, receiver).toJavaName()) + System.lineSeparator() + backtrace); } if (fieldValue.getJavaKind() == JavaKind.Object && bb.getHostVM().isRelocatedPointer(constantAsObject(bb, fieldValue))) { - scanningObserver.forRelocatedPointerFieldValue(receiver, field, fieldValue); + scanningObserver.forRelocatedPointerFieldValue(receiver, field, fieldValue, reason); } else if (fieldValue.isNull()) { - scanningObserver.forNullFieldValue(receiver, field); + scanningObserver.forNullFieldValue(receiver, field, reason); } else if (fieldValue.getJavaKind() == JavaKind.Object) { /* Scan the field value. */ - scanConstant(fieldValue, reason, previous); + scanConstant(fieldValue, reason); /* Process the field value. */ - scanningObserver.forNonNullFieldValue(receiver, field, fieldValue); + scanningObserver.forNonNullFieldValue(receiver, field, fieldValue, reason); } } catch (UnsupportedFeatureException ex) { - unsupportedFeature(field.format("%H.%n"), ex.getMessage(), reason, previous); + unsupportedFeature(field.format("%H.%n"), ex.getMessage(), reason); } } @@ -180,42 +189,37 @@ protected final void scanField(AnalysisField field, JavaConstant receiver, Workl * Scans constant arrays, one element at the time. * * @param array the array to be scanned - * @param previous reference to the work list entry containing parent object */ - protected final void scanArray(JavaConstant array, WorklistEntry previous) { + protected final void scanArray(JavaConstant array, ScanReason prevReason) { Object valueObj = constantAsObject(bb, array); AnalysisType arrayType = analysisType(bb, valueObj); assert valueObj instanceof Object[]; - ScanReason reason = new ArrayScan(arrayType); + ScanReason reason = new ArrayScan(arrayType, array, prevReason); Object[] arrayObject = (Object[]) valueObj; for (int idx = 0; idx < arrayObject.length; idx++) { Object e = arrayObject[idx]; try { if (e == null) { - scanningObserver.forNullArrayElement(array, arrayType, idx); + scanningObserver.forNullArrayElement(array, arrayType, idx, reason); } else { Object element = bb.getUniverse().replaceObject(e); JavaConstant elementConstant = bb.getSnippetReflectionProvider().forObject(element); AnalysisType elementType = analysisType(bb, element); /* Scan the array element. */ - scanConstant(elementConstant, reason, previous); + scanConstant(elementConstant, reason); /* Process the array element. */ - scanningObserver.forNonNullArrayElement(array, arrayType, elementConstant, elementType, idx); + scanningObserver.forNonNullArrayElement(array, arrayType, elementConstant, elementType, idx, reason); } } catch (UnsupportedFeatureException ex) { - unsupportedFeature(arrayType.toJavaName(true), ex.getMessage(), reason, previous); + unsupportedFeature(arrayType.toJavaName(true), ex.getMessage(), reason); } } } public final void scanConstant(JavaConstant value, ScanReason reason) { - scanConstant(value, reason, null); - } - - public final void scanConstant(JavaConstant value, ScanReason reason, WorklistEntry previous) { Object valueObj = constantAsObject(bb, value); if (valueObj == null || valueObj instanceof WordBase) { return; @@ -229,7 +233,7 @@ public final void scanConstant(JavaConstant value, ScanReason reason, WorklistEn scanningObserver.forScannedConstant(value, reason); } finally { scannedObjects.release(valueObj); - WorklistEntry worklistEntry = new WorklistEntry(previous, value, reason); + WorklistEntry worklistEntry = new WorklistEntry(value, reason); if (executor != null) { executor.execute((ObjectScannerRunnable) debug -> doScan(worklistEntry)); } else { @@ -239,53 +243,83 @@ public final void scanConstant(JavaConstant value, ScanReason reason, WorklistEn } } - private void unsupportedFeature(String key, String message, ScanReason reason, WorklistEntry entry) { + private void unsupportedFeature(String key, String message, ScanReason reason) { StringBuilder objectBacktrace = new StringBuilder(); - AnalysisMethod method = buildObjectBacktrace(reason, entry, objectBacktrace); + AnalysisMethod method = buildObjectBacktrace(bb, reason, objectBacktrace); bb.getUnsupportedFeatures().addMessage(key, method, message, objectBacktrace.toString()); } - private AnalysisMethod buildObjectBacktrace(ScanReason reason, WorklistEntry entry, StringBuilder objectBacktrace) { - WorklistEntry cur = entry; - objectBacktrace.append("Object was reached by ").append(System.lineSeparator()); - objectBacktrace.append('\t').append(asString(reason)); - ScanReason rootReason = null; + static final String indent = " "; + + public static AnalysisMethod buildObjectBacktrace(BigBang bb, ScanReason reason, StringBuilder objectBacktrace) { + ScanReason cur = reason; + objectBacktrace.append("Object was reached by "); + objectBacktrace.append(System.lineSeparator()).append(indent).append(asString(bb, cur)); + ScanReason rootReason = cur; + cur = cur.previous; while (cur != null) { - objectBacktrace.append(System.lineSeparator()); - objectBacktrace.append("\t\t").append("constant ").append(asString(cur.constant)).append(" reached by ").append(System.lineSeparator()); - objectBacktrace.append('\t').append(asString(cur.reason)); - rootReason = cur.reason; + objectBacktrace.append(System.lineSeparator()).append(indent).append(asString(bb, cur)); + rootReason = cur.previous; cur = cur.previous; } - if (rootReason instanceof MethodScan) { + if (rootReason instanceof EmbeddedRootScan) { /* The root constant was found during scanning of 'method'. */ - return ((MethodScan) rootReason).method; + return ((EmbeddedRootScan) rootReason).getMethod(); } /* The root constant was not found during method scanning. */ return null; } - String asString(ScanReason reason) { + static String asString(BigBang bb, ScanReason reason) { if (reason instanceof FieldScan) { FieldScan fieldScan = (FieldScan) reason; if (fieldScan.field.isStatic()) { - return "reading field " + reason; + return "reading static field " + reason; } else { /* Instance field scans must have a receiver, hence the 'of'. */ - return "reading field " + reason + " of"; + return "reading field " + reason + " of constant \n " + asString(bb, reason.constant); } - } else if (reason instanceof MethodScan) { - return "scanning method " + reason; + } else if (reason instanceof EmbeddedRootScan) { + return "scanning root " + asString(bb, reason.constant) + " embedded in \n " + reason; } else if (reason instanceof ArrayScan) { - return "indexing into array"; + return "indexing into array " + asString(bb, reason.constant); } else { return reason.toString(); } } - private String asString(JavaConstant constant) { + public static String asString(BigBang bb, JavaConstant constant) { + return asString(bb, constant, true); + } + + public static String asString(BigBang bb, JavaConstant constant, boolean appendToString) { + if (constant == null) { + return "null"; + } Object obj = constantAsObject(bb, constant); - return obj.getClass().getTypeName() + '@' + Integer.toHexString(System.identityHashCode(obj)); + if (obj == null) { + return "null"; + } + String str = obj.getClass().getTypeName() + '@' + Integer.toHexString(System.identityHashCode(obj)); + if (appendToString) { + try { + str += ": " + limit(obj.toString(), 80).replace(System.lineSeparator(), ""); + } catch (Throwable e) { + // ignore any error in creating the string representation + } + } + + return str; + } + + public static String limit(String value, int length) { + StringBuilder buf = new StringBuilder(value); + if (buf.length() > length) { + buf.setLength(length); + buf.append("..."); + } + + return buf.toString(); } /** @@ -305,15 +339,15 @@ private void doScan(WorklistEntry entry) { for (AnalysisField field : type.getInstanceFields(true)) { if (field.getJavaKind() == JavaKind.Object && field.isRead()) { assert !Modifier.isStatic(field.getModifiers()); - scanField(field, entry.constant, entry); + scanField(field, entry.constant, entry.reason); } } } else if (type.isArray() && bb.getProviders().getWordTypes().asKind(type.getComponentType()) == JavaKind.Object) { /* Scan the array elements. */ - scanArray(entry.constant, entry); + scanArray(entry.constant, entry.reason); } } catch (UnsupportedFeatureException ex) { - unsupportedFeature("", ex.getMessage(), entry.reason, entry.previous); + unsupportedFeature("", ex.getMessage(), entry.reason); } } @@ -343,13 +377,11 @@ protected static AnalysisType constantType(BigBang bb, JavaConstant constant) { return bb.getMetaAccess().lookupJavaType(constant); } - protected static Object constantAsObject(BigBang bb, JavaConstant constant) { + public static Object constantAsObject(BigBang bb, JavaConstant constant) { return bb.getSnippetReflectionProvider().asObject(Object.class, constant); } static class WorklistEntry { - /** The previously processed entry. */ - private final WorklistEntry previous; /** The constant to be scanned. */ private final JavaConstant constant; /** @@ -359,46 +391,52 @@ static class WorklistEntry { */ private final ScanReason reason; - WorklistEntry(WorklistEntry previous, JavaConstant constant, ScanReason reason) { - this.previous = previous; + WorklistEntry(JavaConstant constant, ScanReason reason) { this.constant = constant; this.reason = reason; } - public WorklistEntry getPrevious() { - return previous; + public ScanReason getReason() { + return reason; } + } + + public abstract static class ScanReason { + final ScanReason previous; + final JavaConstant constant; - public JavaConstant getConstant() { - return constant; + protected ScanReason(ScanReason previous, JavaConstant constant) { + this.previous = previous; + this.constant = constant; } - public ScanReason getReason() { - return reason; + public ScanReason getPrevious() { + return previous; } } - public interface ScanReason { - OtherReason HUB = new OtherReason("Hub"); - } + public static class OtherReason extends ScanReason { + public static final ScanReason SCAN = new OtherReason("SCAN"); + public static final ScanReason RESCAN = new OtherReason("RESCAN"); + public static final ScanReason HUB = new OtherReason("HUB"); - static class OtherReason implements ScanReason { final String reason; - OtherReason(String reason) { + protected OtherReason(String reason) { + super(null, null); this.reason = reason; } - - @Override - public String toString() { - return reason; - } } - protected static class FieldScan implements ScanReason { + public static class FieldScan extends ScanReason { final AnalysisField field; - FieldScan(AnalysisField field) { + public FieldScan(AnalysisField field) { + this(field, null, null); + } + + public FieldScan(AnalysisField field, JavaConstant receiver, ScanReason previous) { + super(previous, receiver); this.field = field; } @@ -412,10 +450,11 @@ public String toString() { } } - static class ArrayScan implements ScanReason { + public static class ArrayScan extends ScanReason { final AnalysisType arrayType; - ArrayScan(AnalysisType arrayType) { + public ArrayScan(AnalysisType arrayType, JavaConstant array, ScanReason previous) { + super(previous, array); this.arrayType = arrayType; } @@ -425,11 +464,16 @@ public String toString() { } } - protected static class MethodScan implements ScanReason { + public static class EmbeddedRootScan extends ScanReason { final AnalysisMethod method; final BytecodePosition sourcePosition; - MethodScan(AnalysisMethod method, BytecodePosition nodeSourcePosition) { + public EmbeddedRootScan(AnalysisMethod method, BytecodePosition nodeSourcePosition, JavaConstant root) { + this(method, nodeSourcePosition, root, null); + } + + public EmbeddedRootScan(AnalysisMethod method, BytecodePosition nodeSourcePosition, JavaConstant root, ScanReason previous) { + super(previous, root); this.method = method; this.sourcePosition = nodeSourcePosition; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java index 9101fa4ab148..544899ba4024 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java @@ -38,6 +38,7 @@ * possible cases: the element value is either null or the field value is non-null. The implementers * of this API will call the appropriate method during scanning. */ +@SuppressWarnings("unused") public interface ObjectScanningObserver { /** @@ -46,30 +47,45 @@ public interface ObjectScanningObserver { * For relocated pointers the value is only known at runtime after methods are relocated, which * is pretty much the same as a field written at runtime: we do not have a constant value. */ - void forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue); + default boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + return false; + } /** * Hook for scanned null field value. */ - void forNullFieldValue(JavaConstant receiver, AnalysisField field); + default boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, ScanReason reason) { + return false; + } /** * Hook for scanned non-null field value. */ - void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue); + default void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + } /** * Hook for scanned null element value. */ - void forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex); + default boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex, ScanReason reason) { + return false; + } /** * Hook for scanned non-null element value. */ - void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex); + default void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex, ScanReason reason) { + } + + /** + * Hook for scanned embedded root. + */ + default void forEmbeddedRoot(JavaConstant root, ScanReason reason) { + } /** * Hook for scanned constant. */ - void forScannedConstant(JavaConstant scannedValue, ScanReason reason); + default void forScannedConstant(JavaConstant scannedValue, ScanReason reason) { + } } 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 7963f14bb380..ac4f085629f1 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 @@ -57,7 +57,6 @@ import org.graalvm.compiler.printer.GraalDebugHandlersFactory; import org.graalvm.nativeimage.hosted.Feature; -import com.oracle.graal.pointsto.ObjectScanner.ReusableSet; import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; @@ -128,7 +127,7 @@ public abstract class PointsToAnalysis implements BigBang { private final CompletionExecutor.Timing timing; public final Timer typeFlowTimer; - public final Timer checkObjectsTimer; + public final Timer verifyHeapTimer; public final Timer processFeaturesTimer; public final Timer analysisTimer; @@ -142,7 +141,7 @@ public PointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostedP this.hostVM = hostVM; String imageName = hostVM.getImageName(); this.typeFlowTimer = new Timer(imageName, "(typeflow)", false); - this.checkObjectsTimer = new Timer(imageName, "(objects)", false); + this.verifyHeapTimer = new Timer(imageName, "(verify)", false); this.processFeaturesTimer = new Timer(imageName, "(features)", false); this.analysisTimer = new Timer(imageName, "analysis", true); @@ -152,6 +151,7 @@ public PointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostedP this.unsupportedFeatures = unsupportedFeatures; this.providers = providers; this.strengthenGraalGraphs = strengthenGraalGraphs; + providers.setBigBang(this); this.objectType = metaAccess.lookupJavaType(Object.class); /* @@ -194,14 +194,14 @@ public Timer getProcessFeaturesTimer() { @Override public void printTimers() { typeFlowTimer.print(); - checkObjectsTimer.print(); + verifyHeapTimer.print(); processFeaturesTimer.print(); } @Override public void printTimerStatistics(PrintWriter out) { StatisticsPrinter.print(out, "typeflow_time_ms", typeFlowTimer.getTotalTime()); - StatisticsPrinter.print(out, "objects_time_ms", checkObjectsTimer.getTotalTime()); + StatisticsPrinter.print(out, "verify_time_ms", verifyHeapTimer.getTotalTime()); StatisticsPrinter.print(out, "features_time_ms", processFeaturesTimer.getTotalTime()); StatisticsPrinter.print(out, "total_analysis_time_ms", analysisTimer.getTotalTime()); @@ -323,13 +323,15 @@ public void cleanupAfterAnalysis() { allSynchronizedTypeFlow = null; unsafeLoads = null; unsafeStores = null; - scannedObjects = null; ConstantObjectsProfiler.constantTypes.clear(); universe.getTypes().forEach(AnalysisType::cleanupAfterAnalysis); universe.getFields().forEach(AnalysisField::cleanupAfterAnalysis); universe.getMethods().forEach(AnalysisMethod::cleanupAfterAnalysis); + + universe.getHeapScanner().cleanupAfterAnalysis(); + universe.getHeapVerifier().cleanupAfterAnalysis(); } @Override @@ -578,6 +580,10 @@ public CompletionExecutor getExecutor() { public void checkUserLimitations() { } + public void handleUnknownValueField(@SuppressWarnings("unused") AnalysisField field) { + + } + public interface TypeFlowRunnable extends DebugContextRunnable { TypeFlow getTypeFlow(); } @@ -642,10 +648,6 @@ public boolean finish() throws InterruptedException { */ assert executor.getPostedOperations() == 0; numTypes = universe.getTypes().size(); - try (StopTimer t = checkObjectsTimer.start()) { - // track static fields - checkObjectGraph(); - } } while (executor.getPostedOperations() != 0 || numTypes != universe.getTypes().size()); universe.setAnalysisDataValid(true); @@ -668,39 +670,11 @@ public boolean doTypeflow() throws InterruptedException { return didSomeWork; } - private ReusableSet scannedObjects = new ReusableSet(); - - @SuppressWarnings("try") - private void checkObjectGraph() throws InterruptedException { - scannedObjects.reset(); - // scan constants - boolean isParallel = PointstoOptions.ScanObjectsParallel.getValue(options); - ObjectScanner objectScanner = new AnalysisObjectScanner(this, isParallel ? executor : null, scannedObjects); - checkObjectGraph(objectScanner); - if (isParallel) { - executor.start(); - objectScanner.scanBootImageHeapRoots(null, null); - executor.complete(); - executor.shutdown(); - executor.init(timing); - } else { - objectScanner.scanBootImageHeapRoots(null, null); - } - } - @Override public HeapScanningPolicy scanningPolicy() { return heapScanningPolicy; } - /** - * Traverses the object graph to discover references to new types. - * - * @param objectScanner - */ - protected void checkObjectGraph(ObjectScanner objectScanner) { - } - @Override public HostVM getHostVM() { return hostVM; @@ -748,15 +722,25 @@ public void runAnalysis(DebugContext debugContext, Function 0; + if (pendingOperations) { + System.out.println("Found pending operations, continuing analysis."); + continue; + } + /* Outer analysis loop is done. Check if the heap snapshot is stable. */ + if (isHeapStable()) { return; } } @@ -764,6 +748,19 @@ public void runAnalysis(DebugContext debugContext, Function fieldValueTask = typeData.getStaticFieldValueTask(field); + if (fieldValueTask.isDone()) { + JavaConstant fieldSnapshot = fieldValueTask.get(); + if (!Objects.equals(fieldSnapshot, fieldValue)) { + warning(reason, "Value mismatch for static field %s %n snapshot: %s %n new value: %s %n", + field, fieldSnapshot, fieldValue); + scanner.patchStaticField(typeData, field, fieldValue, reason).ensureDone(); + } + } else { + warning(reason, "Snapshot not yet computed for field %s %n new value: %s %n", field, fieldValue); + fieldValueTask.ensureDone(); + } + } else { + AnalysisFuture receiverTask = imageHeap.getTask(receiver); + assert receiverTask != null && receiverTask.isDone() : message(reason, "Task %s for receiver %s.", + (receiverTask == null ? "is null" : "not yet executed"), receiver); + ImageHeapInstance receiverObject = (ImageHeapInstance) receiverTask.get(); + AnalysisFuture fieldValueTask = receiverObject.getFieldTask(field); + if (fieldValueTask.isDone()) { + JavaConstant fieldSnapshot = fieldValueTask.get(); + if (!Objects.equals(fieldSnapshot, fieldValue)) { + warning(reason, "Value mismatch for field %s %n snapshot: %s %n new value: %s %n", + field, fieldSnapshot, fieldValue); + scanner.patchInstanceField(receiverObject, field, fieldValue, reason).ensureDone(); + } + } else { + warning(reason, "Snapshot not yet computed for field %s %n new value: %s %n", field, fieldValue); + fieldValueTask.ensureDone(); + } + } + } catch (InterruptedException | ExecutionException e) { + throw AnalysisError.shouldNotReachHere(e); + } + } + + @Override + public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex, ScanReason reason) { + boolean result = scanner.getScanningObserver().forNullArrayElement(array, arrayType, elementIndex, reason); + if (result) { + foundMismatch = true; + } + return result; + } + + @Override + public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementValue, AnalysisType elementType, int index, ScanReason reason) { + try { + AnalysisFuture arrayTask = imageHeap.getTask(array); + assert arrayTask != null && arrayTask.isDone() : message(reason, "Task %s for array %s.", + (arrayTask == null ? "is null" : "not yet executed"), array); + ImageHeapArray receiverObject = (ImageHeapArray) arrayTask.get(); + JavaConstant elementSnapshot = receiverObject.getElement(index); + if (!Objects.equals(elementSnapshot, elementValue)) { + warning(reason, "Value mismatch for array element %n snapshot: %s %n new value: %s %n", elementSnapshot, elementValue); + receiverObject.setElement(index, scanner.onArrayElementReachable(array, arrayType, elementValue, index, reason)); + } + } catch (InterruptedException | ExecutionException e) { + throw AnalysisError.shouldNotReachHere(e); + } + } + + @Override + public void forEmbeddedRoot(JavaConstant root, ScanReason reason) { + AnalysisFuture rootTask = imageHeap.getTask(root); + if (rootTask == null) { + throw error(reason, "No snapshot task found for embedded root %s %n", root); + } else if (rootTask.isDone()) { + try { + JavaConstant rootSnapshot = rootTask.get().getObject(); + if (!Objects.equals(rootSnapshot, root)) { + throw error(reason, "Value mismatch for embedded root %n snapshot: %s %n new value: %s %n", rootSnapshot, root); + } + } catch (InterruptedException | ExecutionException e) { + throw AnalysisError.shouldNotReachHere(e); + } + } else { + throw error(reason, "Snapshot not yet computed for embedded root %n new value: %s %n", root); + } + } + + @Override + public void forScannedConstant(JavaConstant value, ScanReason reason) { + /* Make sure the value is scanned. */ + AnalysisFuture task = imageHeap.getTask(value); + if (task == null) { + scanner.toImageHeapObject(value, reason); + } + } + } + + private void warning(ScanReason reason, String format, Object... args) { + foundMismatch = true; + if (printOnMismatch) { + System.out.println("WARNING: " + message(reason, format, args)); + } + } + + private RuntimeException error(ScanReason reason, String format, Object... args) { + throw AnalysisError.shouldNotReachHere(message(reason, format, args)); + } + + private String message(ScanReason reason, String format, Object... args) { + String message = format(format, args); + StringBuilder objectBacktrace = new StringBuilder(); + ObjectScanner.buildObjectBacktrace(bb, reason, objectBacktrace); + message += objectBacktrace; + return message; + } + + private String format(String msg, Object... args) { + if (args != null) { + for (int i = 0; i < args.length; ++i) { + if (args[i] instanceof JavaConstant) { + args[i] = asString(bb, (JavaConstant) args[i]); + } else if (args[i] instanceof AnalysisField) { + args[i] = ((AnalysisField) args[i]).format("%H.%n"); + } + } + } + return String.format(msg, args); + } + +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java new file mode 100644 index 000000000000..e0f1725a6544 --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.heap; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.util.AnalysisFuture; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.ResolvedJavaField; + +public class ImageHeap { + + /** Map the original object *and* the replaced object to the HeapObject snapshot. */ + protected ConcurrentHashMap> heapObjects; + /** Store a mapping from types to all scanned objects. */ + protected Map> typesToObjects; + + public ImageHeap() { + heapObjects = new ConcurrentHashMap<>(); + typesToObjects = new ConcurrentHashMap<>(); + } + + public AnalysisFuture getTask(JavaConstant constant) { + return heapObjects.get(constant); + } + + public ImageHeapObject get(JavaConstant constant) { + return getTask(constant).ensureDone(); + } + + public Set getObjects(AnalysisType type) { + return typesToObjects.getOrDefault(type, Collections.emptySet()); + } + + public boolean add(AnalysisType type, ImageHeapObject heapObj) { + return heapObjects(type).add(heapObj); + } + + private Set heapObjects(AnalysisType type) { + return typesToObjects.computeIfAbsent(type, t -> Collections.newSetFromMap(new ConcurrentHashMap<>())); + } + + /** + * Model for snapshotted objects. It stores the replaced object, i.e., the result of applying + * object replacers on the original object, and the instance field values of this object. The + * field values are stored as JavaConstant to also encode primitive values. ImageHeapObject are + * created only after an object is processed through the object replacers. + */ + public static class ImageHeapObject { + final AnalysisType type; + /** Store the object, already processed by the object transformers. */ + final JavaConstant object; + + ImageHeapObject(JavaConstant object, AnalysisType type) { + this.object = object; + this.type = type; + } + + public JavaConstant getObject() { + return object; + } + + /* + * Equals and hascode just compare the replaced constant. Thus two HeapObject insantaces are + * considered equal if they snapshot the same constant, even if the instanceFieldValues are + * different, i.e., they were snapshotted at different times during analysis and one of them + * mutated. This is necessary for the removal of the old snapshotted value on forced + * updates. Alternativelly each AnalysisType needs a mapping from JavaConstant to HeapObject + * instead of just a set of HeapObject. + */ + + @Override + public boolean equals(Object o) { + // TODO == + if (o instanceof ImageHeapObject) { + ImageHeapObject other = (ImageHeapObject) o; + return this.object.equals(other.object); + } + return false; + } + + @Override + public int hashCode() { + return object.hashCode(); + } + } + + public static final class ImageHeapInstance extends ImageHeapObject { + + /** Store original field values of the object. */ + final Map> objectFieldValues; + + ImageHeapInstance(JavaConstant object, AnalysisType type, Map> objectFieldValues) { + super(object, type); + this.objectFieldValues = objectFieldValues; + } + + public JavaConstant readFieldValue(AnalysisField field) { + return objectFieldValues.get(field).ensureDone(); + } + + public AnalysisFuture getFieldTask(AnalysisField field) { + return objectFieldValues.get(field); + } + + public void setFieldTask(AnalysisField field, AnalysisFuture task) { + objectFieldValues.put(field, task); + } + } + + static final class ImageHeapArray extends ImageHeapObject { + /** Contains the already scanned array elements. */ + private final JavaConstant[] arrayElementValues; + + ImageHeapArray(JavaConstant object, AnalysisType type, JavaConstant[] arrayElementValues) { + super(object, type); + this.arrayElementValues = arrayElementValues; + } + + public JavaConstant getElement(int idx) { + return arrayElementValues[idx]; + } + + public void setElement(int idx, JavaConstant value) { + arrayElementValues[idx] = value; + } + + public int getLength() { + return arrayElementValues.length; + } + } + +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java new file mode 100644 index 000000000000..b08b8a676f82 --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.heap; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.MapCursor; +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionKey; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.ObjectScanner.ArrayScan; +import com.oracle.graal.pointsto.ObjectScanner.EmbeddedRootScan; +import com.oracle.graal.pointsto.ObjectScanner.FieldScan; +import com.oracle.graal.pointsto.ObjectScanner.OtherReason; +import com.oracle.graal.pointsto.ObjectScanner.ScanReason; +import com.oracle.graal.pointsto.ObjectScanningObserver; +import com.oracle.graal.pointsto.PointsToAnalysis; +import com.oracle.graal.pointsto.api.HostVM; +import com.oracle.graal.pointsto.heap.ImageHeap.ImageHeapArray; +import com.oracle.graal.pointsto.heap.ImageHeap.ImageHeapInstance; +import com.oracle.graal.pointsto.heap.ImageHeap.ImageHeapObject; +import com.oracle.graal.pointsto.heap.value.ValueSupplier; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.TypeData; +import com.oracle.graal.pointsto.util.AnalysisError; +import com.oracle.graal.pointsto.util.AnalysisFuture; +import com.oracle.graal.pointsto.util.GraalAccess; +import com.oracle.svm.util.ReflectionUtil; + +import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaField; + +/** + * Scanning is triggered when: + *
    + *
  • a static final field is marked as accessed, or
  • s + *
  • a method that is parsed and embedded roots are discovered
  • + *
+ *

+ * When an instance field is marked as accessed the objects of its declaring type (and all the + * subtypes) are re-scanned. + */ +public class ImageHeapScanner { + public static class Options { + @Option(help = "Enable manual rescanning of image heap objects.")// + public static final OptionKey EnableManualRescan = new OptionKey<>(true); + + @Option(help = "Print warnings when verifier finds mismatch.")// + public static final OptionKey PrintOnMismatch = new OptionKey<>(false); + } + + private final BigBang bb; + protected final ImageHeap imageHeap; + protected final AnalysisMetaAccess metaAccess; + protected final AnalysisUniverse universe; + protected final HostVM hostVM; + + protected final SnippetReflectionProvider snippetReflection; + protected final ConstantReflectionProvider constantReflection; + protected final ConstantReflectionProvider hostedConstantReflection; + protected final SnippetReflectionProvider hostedSnippetReflection; + protected ObjectScanningObserver scanningObserver; + protected boolean isEnabled; + private final boolean enableManualRescan; + + public ImageHeapScanner(BigBang bb, ImageHeap heap, AnalysisMetaAccess aMetaAccess, + SnippetReflectionProvider aSnippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { + this.bb = bb; + imageHeap = heap; + universe = aMetaAccess.getUniverse(); + metaAccess = aMetaAccess; + hostVM = aMetaAccess.getUniverse().hostVM(); + snippetReflection = aSnippetReflection; + constantReflection = aConstantReflection; + scanningObserver = aScanningObserver; + hostedConstantReflection = GraalAccess.getOriginalProviders().getConstantReflection(); + hostedSnippetReflection = GraalAccess.getOriginalProviders().getSnippetReflection(); + isEnabled = true; + enableManualRescan = Options.EnableManualRescan.getValue(hostVM.options()); + } + + public void disable() { + isEnabled = false; + } + + public void enable() { + isEnabled = true; + } + + public boolean isEnabled() { + return isEnabled; + } + + public ImageHeap getImageHeap() { + return imageHeap; + } + + public void scanEmbeddedRoot(JavaConstant root, BytecodePosition position) { + AnalysisMethod method = (AnalysisMethod) position.getMethod(); + AnalysisType type = metaAccess.lookupJavaType(root); + type.registerAsReachable(); + toImageHeapObject(root, new EmbeddedRootScan(method, position, root)); + } + + public void scanFieldValue(AnalysisField field, JavaConstant receiver) { + /* + * If this read is for constant folding then the read is not parsed yet and the field is not + * marked as reachable. + */ + field.markReachable(); + if (field.isStatic()) { + TypeData declaringClassData = field.getDeclaringClass().getOrComputeData(); + declaringClassData.getStaticFieldValueTask(field).ensureDone(); + } else { + ImageHeapObject receiverObject = getImageHeapObject(receiver); + if (receiverObject instanceof ImageHeapInstance) { + ((ImageHeapInstance) receiverObject).getFieldTask(field).ensureDone(); + } + } + } + + ImageHeapObject getImageHeapObject(Constant constant) { + if (constant instanceof JavaConstant) { + JavaConstant javaConstant = (JavaConstant) constant; + if (javaConstant.getJavaKind() == JavaKind.Object && javaConstant.isNonNull()) { + return toImageHeapObject(javaConstant); + } + } + return null; + } + + /** + * Computes the class initialization status and the snapshot of all static fields. This is an + * expensive operation and therefore done in an asynchronous task. + */ + public TypeData computeTypeData(AnalysisType type) { + GraalError.guarantee(type.isReachable(), "TypeData is only available for reachable types"); + + /* Decide if the type should be initialized at build time or at run time: */ + boolean initializeAtRunTime = initializeAtRunTime(type); + + Map> rawStaticFieldValues; + /* + * Snapshot all static fields. This reads the raw field value of all fields regardless of + * reachability status. The field value is processed when a field is marked as reachable, in + * onFieldReachable(). + */ + rawStaticFieldValues = new HashMap<>(); + for (AnalysisField field : type.getStaticFields()) { + ValueSupplier rawFieldValue = readHostedFieldValue(field, null); + rawStaticFieldValues.put(field, new AnalysisFuture<>(() -> onFieldValueReachable(field, null, rawFieldValue, new FieldScan(field)))); + } + + return new TypeData(initializeAtRunTime, rawStaticFieldValues); + } + + protected boolean initializeAtRunTime(@SuppressWarnings("unused") AnalysisType type) { + return false; + } + + void markTypeInstantiated(AnalysisType type) { + if (universe.sealed() && !type.isReachable()) { + throw AnalysisError.shouldNotReachHere("Universe is sealed. New type reachable: " + type.toJavaName()); + } + type.registerAsInHeap(); + } + + public void onFieldRead(AnalysisField field) { + assert field.isRead(); + execute(() -> onFieldReadTask(field)); + } + + private void onFieldReadTask(AnalysisField field) { + AnalysisType declaringClass = field.getDeclaringClass(); + if (field.isStatic()) { + TypeData declaringClassData = declaringClass.getOrComputeData(); + /* Check if the value is available before accessing it. */ + if (isValueAvailable(field)) { + execute(declaringClassData.getStaticFieldValueTask(field)); + } + } else { + if (isValueAvailable(field)) { + onInstanceFieldRead(field, declaringClass); + } + } + } + + private void onInstanceFieldRead(AnalysisField field, AnalysisType type) { + for (AnalysisType subtype : type.getSubTypes()) { + for (ImageHeapObject imageHeapObject : imageHeap.getObjects(subtype)) { + execute(((ImageHeapInstance) imageHeapObject).getFieldTask(field)); + } + /* Subtypes may include this type itself. */ + if (!subtype.equals(type)) { + onInstanceFieldRead(field, subtype); + } + } + } + + AnalysisFuture markConstantReachable(Constant constant, ScanReason reason) { + if (!(constant instanceof JavaConstant)) { + /* + * The bytecode parser sometimes embeds low-level VM constants for types into the + * high-level graph. Since these constants are the result of type lookups, these types + * are already marked as reachable. Eventually, the bytecode parser should be changed to + * only use JavaConstant. + */ + return null; + } + + JavaConstant javaConstant = (JavaConstant) constant; + if (javaConstant.getJavaKind() == JavaKind.Object && javaConstant.isNonNull()) { + if (!hostVM.platformSupported(asObject(javaConstant).getClass())) { + return null; + } + return getOrCreateConstantReachableTask(javaConstant, reason); + } + + return null; + } + + public ImageHeapObject toImageHeapObject(JavaConstant constant) { + return toImageHeapObject(constant, OtherReason.SCAN); + } + + public ImageHeapObject toImageHeapObject(JavaConstant javaConstant, ScanReason reason) { + assert javaConstant.getJavaKind() == JavaKind.Object && javaConstant.isNonNull(); + return getOrCreateConstantReachableTask(javaConstant, reason).ensureDone(); + } + + public void scan(JavaConstant javaConstant, ScanReason reason) { + assert javaConstant.getJavaKind() == JavaKind.Object && javaConstant.isNonNull(); + execute(getOrCreateConstantReachableTask(javaConstant, reason)); + } + + protected AnalysisFuture getOrCreateConstantReachableTask(JavaConstant javaConstant, ScanReason reason) { + ScanReason nonNullReason = Objects.requireNonNull(reason); + AnalysisFuture existingTask = imageHeap.heapObjects.get(javaConstant); + if (existingTask == null) { + /* + * TODO - this still true? is it ok to seal the heap? + * + * The constants can be generated at any stage, not only before/during analysis. For + * example `com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets.Templates. + * getProfilingData()` generates `AllocationProfilingData` during lowering. + */ + if (universe.sealed()) { + throw AnalysisError.shouldNotReachHere("Universe is sealed. New constant reachable: " + javaConstant.toValueString()); + } + AnalysisFuture newTask = new AnalysisFuture<>(() -> createImageHeapObject(javaConstant, nonNullReason)); + existingTask = imageHeap.heapObjects.putIfAbsent(javaConstant, newTask); + if (existingTask == null) { + /* + * Immediately schedule the new task. There is no need to have not-yet-reachable + * ImageHeapObject. + */ + execute(newTask); + return newTask; + } + } + return existingTask; + } + + private Optional maybeReplace(JavaConstant constant, ScanReason reason) { + Object unwrapped = unwrapObject(constant); + if (unwrapped == null) { + throw GraalError.shouldNotReachHere(formatReason("Could not unwrap constant", reason)); + } else if (unwrapped instanceof ImageHeapObject) { + throw GraalError.shouldNotReachHere(formatReason("Double wrapping of constant. Most likely, the reachability analysis code itself is seen as reachable.", reason)); + } + + /* Run all registered object replacers. */ + if (constant.getJavaKind() == JavaKind.Object) { + Object replaced = universe.replaceObject(unwrapped); + if (replaced != unwrapped) { + JavaConstant replacedConstant = universe.getSnippetReflection().forObject(replaced); + return Optional.of(replacedConstant); + } + } + return Optional.empty(); + } + + protected Object unwrapObject(JavaConstant constant) { + return snippetReflection.asObject(Object.class, constant); + } + + protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReason reason) { + assert constant.getJavaKind() == JavaKind.Object && !constant.isNull(); + + Optional replaced = maybeReplace(constant, reason); + if (replaced.isPresent()) { + /* + * This ensures that we have a unique ImageHeapObject for the original and replaced + * object. As a side effect, this runs all object transformer again on the replaced + * constant. + */ + return toImageHeapObject(replaced.get(), reason); + } + + if (!hostVM.platformSupported(asObject(constant).getClass())) { + return null; + } + + /* + * Access the constant type after the replacement. Some constants may have types that should + * not be reachable at run time and thus are replaced. + */ + AnalysisType type = metaAccess.lookupJavaType(constant); + + ImageHeapObject newImageHeapObject; + if (type.isArray()) { + int length = constantReflection.readArrayLength(constant); + JavaConstant[] arrayElements = new JavaConstant[length]; + ScanReason arrayReason = new ArrayScan(type, constant, reason); + for (int idx = 0; idx < length; idx++) { + final JavaConstant rawElementValue = constantReflection.readArrayElement(constant, idx); + arrayElements[idx] = onArrayElementReachable(constant, type, rawElementValue, idx, arrayReason); + } + newImageHeapObject = new ImageHeapArray(constant, type, arrayElements); + markTypeInstantiated(type); + } else { + Map> instanceFieldValues = new HashMap<>(); + /* + * We need to have the new ImageHeapInstance early so that we can reference it in the + * lambda when the field value gets reachable. But it must not be published to any other + * thread before all instanceFieldValues are filled in. + */ + newImageHeapObject = new ImageHeapInstance(constant, type, instanceFieldValues); + /* We are about to query the type's fields, the type must be marked as reachable. */ + markTypeInstantiated(type); + for (AnalysisField field : type.getInstanceFields(true)) { + ScanReason fieldReason = new FieldScan(field, constant, reason); + ValueSupplier rawFieldValue; + try { + rawFieldValue = readHostedFieldValue(field, universe.toHosted(constant)); + } catch (InternalError | TypeNotPresentException | LinkageError e) { + /* Ignore missing type errors. */ + continue; + } + instanceFieldValues.put(field, new AnalysisFuture<>(() -> onFieldValueReachable(field, constant, rawFieldValue, fieldReason))); + } + } + + /* + * Following all the array elements and reachable field values can be done asynchronously. + */ + execute(() -> onObjectReachable(newImageHeapObject)); + return newImageHeapObject; + } + + JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, JavaConstant fieldValue, ScanReason reason) { + return onFieldValueReachable(field, receiver, ValueSupplier.eagerValue(fieldValue), reason); + } + + JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, ValueSupplier rawValue, ScanReason reason) { + AnalysisError.guarantee(field.isReachable(), "Field value is only reachable when field is reachable " + field.format("%H.%n")); + + /* + * Check if the field value is available. If not, trying to access it is an error. This + * forces the callers to only trigger the execution of the future task when the value is + * ready to be materialized. + */ + AnalysisError.guarantee(rawValue.isAvailable(), "Value not yet available for " + field.format("%H.%n")); + + /* Attempting to materialize the value before it is available may result in an error. */ + JavaConstant transformedValue = transformFieldValue(field, receiver, rawValue.get()); + + if (scanningObserver != null) { + if (transformedValue.getJavaKind() == JavaKind.Object && hostVM.isRelocatedPointer(asObject(transformedValue))) { + scanningObserver.forRelocatedPointerFieldValue(receiver, field, transformedValue, reason); + } else if (transformedValue.isNull()) { + scanningObserver.forNullFieldValue(receiver, field, reason); + } else { + AnalysisFuture objectFuture = markConstantReachable(transformedValue, reason); + /* Notify the points-to analysis of the scan. */ + if (objectFuture != null) { + /* Add the transformed value to the image heap. */ + scanningObserver.forNonNullFieldValue(receiver, field, objectFuture.ensureDone().object, reason); + } + } + } + /* Return the transformed value, but NOT the image heap object. */ + return transformedValue; + } + + @SuppressWarnings("unused") + protected JavaConstant transformFieldValue(AnalysisField field, JavaConstant receiverConstant, JavaConstant originalValueConstant) { + return originalValueConstant; + } + + protected JavaConstant onArrayElementReachable(JavaConstant array, AnalysisType arrayType, JavaConstant rawElementValue, int elementIndex, ScanReason reason) { + AnalysisFuture objectFuture = markConstantReachable(rawElementValue, reason); + if (scanningObserver != null && arrayType.getComponentType().getJavaKind() == JavaKind.Object) { + if (objectFuture == null) { + scanningObserver.forNullArrayElement(array, arrayType, elementIndex, reason); + } else { + ImageHeapObject element = objectFuture.ensureDone(); + AnalysisType elementType = constantType(element.object); + markTypeInstantiated(elementType); + /* Process the array element. */ + scanningObserver.forNonNullArrayElement(array, arrayType, element.object, elementType, elementIndex, reason); + return element.object; + } + } + return null; + } + + void onObjectReachable(ImageHeapObject imageHeapObject) { + imageHeap.add(imageHeapObject.type, imageHeapObject); + + markTypeInstantiated(imageHeapObject.type); + + if (imageHeapObject instanceof ImageHeapInstance) { + ImageHeapInstance imageHeapInstance = (ImageHeapInstance) imageHeapObject; + for (AnalysisField field : imageHeapObject.type.getInstanceFields(true)) { + if (field.isReachable() && field.isRead() && isValueAvailable(field)) { + execute(imageHeapInstance.getFieldTask(field)); + } + } + } + } + + public boolean isValueAvailable(@SuppressWarnings("unused") AnalysisField field) { + return true; + } + + protected String formatReason(String message, ScanReason reason) { + return message + ' ' + reason; + } + + protected ValueSupplier readHostedFieldValue(AnalysisField field, JavaConstant object) { + assert !field.isStatic() || !initializeAtRunTime(field.getDeclaringClass()); + // Wrap the hosted constant into a substrate constant + JavaConstant value = universe.lookup(hostedConstantReflection.readFieldValue(field.wrapped, object)); + return ValueSupplier.eagerValue(value); + } + + protected boolean skipScanning() { + return false; + } + + public void rescanRoot(Field reflectionField) { + if (!enableManualRescan) { + return; + } + if (skipScanning()) { + return; + } + AnalysisType type = metaAccess.lookupJavaType(reflectionField.getDeclaringClass()); + if (type.isReachable()) { + AnalysisField field = metaAccess.lookupJavaField(reflectionField); + JavaConstant fieldValue = readHostedFieldValue(field, null).get(); + TypeData typeData = field.getDeclaringClass().getOrComputeData(); + AnalysisFuture fieldTask = patchStaticField(typeData, field, fieldValue, OtherReason.RESCAN); + if (field.isRead()) { + rescanCollectionElements(asObject(fieldTask.ensureDone())); + } + } + } + + public void rescanField(Object receiver, Class declaringClass, String fieldName) { + rescanField(receiver, ReflectionUtil.lookupField(declaringClass, fieldName)); + } + + public void rescanField(Object receiver, Field reflectionField) { + if (!enableManualRescan) { + return; + } + if (skipScanning()) { + return; + } + AnalysisType type = metaAccess.lookupJavaType(reflectionField.getDeclaringClass()); + if (type.isReachable()) { + AnalysisField field = metaAccess.lookupJavaField(reflectionField); + assert !field.isStatic(); + JavaConstant receiverConstant = asConstant(receiver); + Optional replaced = maybeReplace(receiverConstant, OtherReason.RESCAN); + if (replaced.isPresent()) { + receiverConstant = replaced.get(); + } + JavaConstant fieldValue = readHostedFieldValue(field, universe.toHosted(receiverConstant)).get(); + if (fieldValue != null) { + ImageHeapInstance receiverObject = (ImageHeapInstance) getImageHeapObject(receiverConstant); + AnalysisFuture fieldTask = patchInstanceField(receiverObject, field, fieldValue, OtherReason.RESCAN); + if (field.isRead()) { + rescanCollectionElements(asObject(fieldTask.ensureDone())); + } + } + } + } + + protected AnalysisFuture patchStaticField(TypeData typeData, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, null, fieldValue, reason)); + typeData.setStaticFieldValueTask(field, task); + return task; + } + + protected AnalysisFuture patchInstanceField(ImageHeapInstance receiverObject, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, receiverObject.object, fieldValue, reason)); + receiverObject.setFieldTask(field, task); + return task; + } + + /** Trigger rescanning of constants. */ + public void rescanObject(Object object) { + if (!enableManualRescan) { + return; + } + if (skipScanning()) { + return; + } + if (object == null) { + return; + } + doScan(asConstant(object)); + rescanCollectionElements(object); + } + + private void rescanCollectionElements(Object object) { + if (object instanceof Object[]) { + Object[] array = (Object[]) object; + for (Object element : array) { + doScan(asConstant(element)); + } + } else if (object instanceof Collection) { + Collection collection = (Collection) object; + collection.forEach(e -> doScan(asConstant(e))); + } else if (object instanceof Map) { + Map map = (Map) object; + map.forEach((k, v) -> { + doScan(asConstant(k)); + doScan(asConstant(v)); + }); + } else if (object instanceof EconomicMap) { + rescanEconomicMap((EconomicMap) object); + } + } + + protected void rescanEconomicMap(EconomicMap object) { + MapCursor cursor = object.getEntries(); + while (cursor.advance()) { + doScan(asConstant(cursor.getKey())); + doScan(asConstant(cursor.getValue())); + } + } + + void doScan(JavaConstant constant) { + if (constant.getJavaKind() == JavaKind.Object && constant.isNonNull()) { + toImageHeapObject(constant, OtherReason.SCAN); + } + } + + public void scanHub(AnalysisType type) { + /* Initialize dynamic hub metadata before scanning it. */ + bb.onTypeScanned(type); + metaAccess.lookupJavaType(java.lang.Class.class).registerAsReachable(); + /* We scan the original class here, the scanner does the replacement to DynamicHub. */ + createImageHeapObject(asConstant(type.getJavaClass()), OtherReason.HUB); + } + + protected AnalysisType analysisType(Object constant) { + return metaAccess.lookupJavaType(constant.getClass()); + } + + protected AnalysisType constantType(JavaConstant constant) { + return metaAccess.lookupJavaType(constant); + } + + protected Object asObject(JavaConstant constant) { + return snippetReflection.asObject(Object.class, constant); + } + + public JavaConstant asConstant(Object object) { + return snippetReflection.forObject(object); + } + + public void cleanupAfterAnalysis() { + scanningObserver = null; + imageHeap.heapObjects = null; + imageHeap.typesToObjects = null; + } + + public ObjectScanningObserver getScanningObserver() { + return scanningObserver; + } + + public void execute(AnalysisFuture future) { + if (future.isDone()) { + return; + } + ((PointsToAnalysis) universe.getBigbang()).postTask(debug -> future.run()); + } + + public void execute(Runnable task) { + ((PointsToAnalysis) universe.getBigbang()).postTask(debug -> task.run()); + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/EagerValueSupplier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/EagerValueSupplier.java new file mode 100644 index 000000000000..4010b72e5d9c --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/EagerValueSupplier.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.heap.value; + +public final class EagerValueSupplier implements ValueSupplier { + + private final V value; + + EagerValueSupplier(V value) { + this.value = value; + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public V get() { + return value; + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/LazyValueSupplier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/LazyValueSupplier.java new file mode 100644 index 000000000000..2df2c32db1ed --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/LazyValueSupplier.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.heap.value; + +import java.util.function.Supplier; + +import com.oracle.graal.pointsto.util.AnalysisError; + +public final class LazyValueSupplier implements ValueSupplier { + + private final Supplier valueSupplier; + private final Supplier isAvailable; + + LazyValueSupplier(Supplier valueSupplier, Supplier isAvailable) { + this.valueSupplier = valueSupplier; + this.isAvailable = isAvailable; + } + + @Override + public boolean isAvailable() { + return isAvailable.get(); + } + + @Override + public V get() { + AnalysisError.guarantee(isAvailable(), "Value is not yet available."); + return valueSupplier.get(); + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/ValueSupplier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/ValueSupplier.java new file mode 100644 index 000000000000..3616e889f17c --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/value/ValueSupplier.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.heap.value; + +import java.util.function.Supplier; + +/** + * Interface for accessing hosted heap values. It provides mechanisms for accessing hosted heap + * values either eagerly or lazily. + *

+ * Eager heap values are objects whose state doesn't change during heap snapshotting. Their value is + * immediately available. + *

+ * Lazy heap values are objects whose state can change during heap snapshotting, therefore reading + * the actual value is delayed. Instead, both a value supplier and an availability supplier are + * installed. The implementation guarantees that the value is indeed available, by checking the + * availability supplier, before it attempts to retrieve it. + */ +public interface ValueSupplier { + + static ValueSupplier eagerValue(V value) { + return new EagerValueSupplier<>(value); + } + + static ValueSupplier lazyValue(Supplier valueSupplier, Supplier isAvailable) { + return new LazyValueSupplier<>(valueSupplier, isAvailable); + } + + /** Checks if the value is available. */ + boolean isAvailable(); + + /** + * Retrieves the value, if available. Attempting to access a value before it is available + * results in error. + */ + V get(); +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java index f0d11729a2be..ccad6d103291 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java @@ -77,6 +77,7 @@ public class AnalysisField implements ResolvedJavaField, OriginalFieldProvider { private AtomicBoolean isAccessed = new AtomicBoolean(); private AtomicBoolean isRead = new AtomicBoolean(); private AtomicBoolean isWritten = new AtomicBoolean(); + private final AtomicBoolean isReachable = new AtomicBoolean(); private boolean isJNIAccessed; private boolean isUsedInComparison; @@ -267,9 +268,11 @@ public void cleanupAfterAnalysis() { public boolean registerAsAccessed() { boolean firstAttempt = AtomicUtils.atomicMark(isAccessed); + markReachable(); notifyUpdateAccessInfo(); if (firstAttempt) { getUniverse().onFieldAccessed(this); + getUniverse().getHeapScanner().onFieldRead(this); } return firstAttempt; } @@ -280,8 +283,10 @@ public boolean registerAsRead(MethodTypeFlow method) { if (readBy != null && method != null) { readBy.put(method, Boolean.TRUE); } + markReachable(); if (firstAttempt) { getUniverse().onFieldAccessed(this); + getUniverse().getHeapScanner().onFieldRead(this); } return firstAttempt; } @@ -298,12 +303,19 @@ public boolean registerAsWritten(MethodTypeFlow method) { if (writtenBy != null && method != null) { writtenBy.put(method, Boolean.TRUE); } + markReachable(); if (firstAttempt && (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object)) { getUniverse().onFieldAccessed(this); } return firstAttempt; } + public void markReachable() { + if (AtomicUtils.atomicMark(isReachable)) { + getDeclaringClass().registerAsReachable(); + } + } + public void registerAsUnsafeAccessed() { registerAsUnsafeAccessed(DefaultUnsafePartition.get()); } @@ -396,6 +408,10 @@ public boolean isRead() { return isAccessed.get() || isRead.get(); } + public boolean isReachable() { + return isReachable.get(); + } + public boolean isWritten() { return isAccessed.get() || isWritten.get(); } @@ -480,7 +496,7 @@ public T getAnnotation(Class annotationClass) { @Override public String toString() { - return "AnalysisField<" + format("%h.%n") + " accessed: " + isAccessed + " reads: " + isRead + " written: " + isWritten + ">"; + return "AnalysisField<" + format("%h.%n") + " accessed: " + isAccessed + " reads: " + isRead + " written: " + isWritten + " reachable: " + isReachable + ">"; } public void markAsUsedInComparison() { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index f69ce44cc804..da61268994f9 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -55,6 +55,7 @@ import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.infrastructure.WrappedJavaType; import com.oracle.graal.pointsto.typestate.TypeState; +import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.AnalysisFuture; import com.oracle.graal.pointsto.util.AtomicUtils; import com.oracle.graal.pointsto.util.ConcurrentLightHashSet; @@ -154,6 +155,10 @@ public enum UsageKind { } private final AnalysisFuture initializationTask; + /** + * Additional information that is only available for types that are marked as reachable. + */ + final AnalysisFuture typeData; AnalysisType(AnalysisUniverse universe, ResolvedJavaType javaType, JavaKind storageKind, AnalysisType objectType, AnalysisType cloneableType) { this.universe = universe; @@ -237,6 +242,10 @@ public enum UsageKind { /* The registration task initializes the type. */ this.initializationTask = new AnalysisFuture<>(() -> universe.hostVM.initializeType(this), null); + this.typeData = new AnalysisFuture<>(() -> { + AnalysisError.guarantee(universe.getHeapScanner() != null, "Heap scanner is not available."); + return universe.getHeapScanner().computeTypeData(this); + }); } private AnalysisType[] convertTypes(ResolvedJavaType[] originalTypes) { @@ -518,6 +527,11 @@ private synchronized void addAssignableType(BigBang bb, TypeState typeState) { assignableTypesNonNullState = assignableTypesState.forNonNull(((PointsToAnalysis) bb)); } + public TypeData getOrComputeData() { + GraalError.guarantee(isReachable.get(), "TypeData is only available for reachable types"); + return this.typeData.ensureDone(); + } + public void ensureInitialized() { /* Run the registration and wait for it to complete, if necessary. */ initializationTask.ensureDone(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index b82c81e9881b..fba7bc2d14c4 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -46,6 +46,8 @@ import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; +import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier; +import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.infrastructure.AnalysisConstantPool; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.infrastructure.Universe; @@ -116,6 +118,8 @@ public class AnalysisUniverse implements Universe { private AnalysisType cloneableClass; private final JavaKind wordKind; private AnalysisPolicy analysisPolicy; + private ImageHeapScanner heapScanner; + private HeapSnapshotVerifier heapVerifier; private BigBang bb; public JavaKind getWordKind() { @@ -521,6 +525,7 @@ public Map getEmbeddedRoots() { * Register an embedded root, i.e., a JavaConstant embedded in a Graal graph via a ConstantNode. */ public void registerEmbeddedRoot(JavaConstant root, BytecodePosition position) { + this.heapScanner.scanEmbeddedRoot(root, position); this.embeddedRoots.put(root, position); } @@ -699,4 +704,23 @@ public void setBigBang(BigBang bb) { this.bb = bb; } + public BigBang getBigbang() { + return bb; + } + + public void setHeapScanner(ImageHeapScanner heapScanner) { + this.heapScanner = heapScanner; + } + + public ImageHeapScanner getHeapScanner() { + return heapScanner; + } + + public void setHeapVerifier(HeapSnapshotVerifier heapVerifier) { + this.heapVerifier = heapVerifier; + } + + public HeapSnapshotVerifier getHeapVerifier() { + return heapVerifier; + } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/HostedProviders.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/HostedProviders.java index 076c731d911f..8ff5583eb9ca 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/HostedProviders.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/HostedProviders.java @@ -37,12 +37,15 @@ import org.graalvm.compiler.phases.util.Providers; import org.graalvm.compiler.word.WordTypes; +import com.oracle.graal.pointsto.PointsToAnalysis; + import jdk.vm.ci.code.CodeCacheProvider; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.MetaAccessProvider; public class HostedProviders extends Providers { + private PointsToAnalysis bigBang; private GraphBuilderConfiguration.Plugins graphBuilderPlugins; public HostedProviders(MetaAccessProvider metaAccess, CodeCacheProvider codeCache, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, @@ -60,6 +63,14 @@ public void setGraphBuilderPlugins(GraphBuilderConfiguration.Plugins graphBuilde this.graphBuilderPlugins = graphBuilderPlugins; } + public void setBigBang(PointsToAnalysis bigBang) { + this.bigBang = bigBang; + } + + public PointsToAnalysis getBigBang() { + return bigBang; + } + @Override public Providers copyWith(ConstantReflectionProvider substitution) { assert this.getClass() == HostedProviders.class : "must override in " + getClass(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/TypeData.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/TypeData.java new file mode 100644 index 000000000000..46e05129730f --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/TypeData.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.meta; + +import java.util.Map; + +import com.oracle.graal.pointsto.util.AnalysisFuture; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.ResolvedJavaField; + +/** + * Additional data for a {@link AnalysisType type} that is only available for types that are marked + * as reachable. Computed lazily once the type is seen as reachable. + */ +public final class TypeData { + /** The class initialization state: initialize the class at build time or at run time. */ + final boolean initializeAtRunTime; + /** + * The raw values of all static fields, regardless of field reachability status. Evaluating the + * {@link AnalysisFuture} runs + * com.oracle.graal.pointsto.heap.ImageHeapScanner#onFieldValueReachable adds the result to the + * image heap}. + */ + final Map> staticFieldValues; + + public TypeData(boolean initializeAtRunTime, Map> staticFieldValues) { + this.initializeAtRunTime = initializeAtRunTime; + this.staticFieldValues = staticFieldValues; + } + + public boolean initializeAtRunTime() { + return initializeAtRunTime; + } + + public AnalysisFuture getStaticFieldValueTask(AnalysisField field) { + return staticFieldValues.get(field); + } + + public JavaConstant readStaticFieldValue(AnalysisField field) { + return staticFieldValues.get(field).ensureDone(); + } + + public void setStaticFieldValueTask(AnalysisField field, AnalysisFuture task) { + staticFieldValues.put(field, task); + } + +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisHeapHistogramPrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisHeapHistogramPrinter.java index c9b855de77cf..6a3517c818e2 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisHeapHistogramPrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisHeapHistogramPrinter.java @@ -91,23 +91,26 @@ public void forScannedConstant(JavaConstant scannedValue, ScanReason reason) { } @Override - public void forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue) { + public boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + return false; } @Override - public void forNullFieldValue(JavaConstant receiver, AnalysisField field) { + public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, ScanReason reason) { + return false; } @Override - public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue) { + public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { } @Override - public void forNullArrayElement(JavaConstant array, AnalysisType arrayType, int index) { + public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int index, ScanReason reason) { + return false; } @Override - public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int index) { + public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int index, ScanReason reason) { } } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java index fafe5d3364ec..c27fd224c1e2 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java @@ -320,24 +320,26 @@ private ScanningObserver(BigBang bb, Map constantT } @Override - public void forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue) { + public boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + return false; } @Override - public void forNullFieldValue(JavaConstant receiver, AnalysisField field) { + public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, ScanReason reason) { if (receiver == null) { // static field - return; + return false; } assert constantToNode.containsKey(receiver); ObjectNode receiverNode = (ObjectNode) constantToNode.get(receiver); receiverNode.addField(field, ObjectNodeBase.forNull()); + return true; } @Override - public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue) { + public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { if (receiver == null) { // static field @@ -352,15 +354,16 @@ public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, Jav } @Override - public void forNullArrayElement(JavaConstant array, AnalysisType arrayType, int index) { + public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int index, ScanReason reason) { assert constantToNode.containsKey(array); ArrayObjectNode arrayNode = (ArrayObjectNode) constantToNode.get(array); arrayNode.addElement(index, ObjectNodeBase.forNull()); + return true; } @Override - public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int index) { + public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int index, ScanReason reason) { if (constantToNode.containsKey(array) && constantToNode.containsKey(elementConstant)) { ArrayObjectNode arrayNode = (ArrayObjectNode) constantToNode.get(array); ObjectNodeBase valueNode = constantToNode.get(elementConstant); @@ -380,8 +383,8 @@ public void forScannedConstant(JavaConstant scannedValue, ScanReason reason) { } else { node = ObjectNodeBase.fromConstant(bb, scannedValue); } - } else if (reason instanceof MethodScan) { - ResolvedJavaMethod method = ((MethodScan) reason).getMethod(); + } else if (reason instanceof EmbeddedRootScan) { + ResolvedJavaMethod method = ((EmbeddedRootScan) reason).getMethod(); node = ObjectNodeBase.fromConstant(bb, scannedValue, new RootSource(method)); } else { node = ObjectNodeBase.fromConstant(bb, scannedValue); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ReportUtils.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ReportUtils.java index 3ec8d973e6fa..fe82c1f7261e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ReportUtils.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ReportUtils.java @@ -62,7 +62,7 @@ public class ReportUtils { static final Comparator fieldComparator = Comparator.comparing(f -> f.format("%H.%n")); static final Comparator invokeInfoComparator = Comparator.comparing(i -> i.getTargetMethod().format("%H.%n(%p)")); static final Comparator positionMethodComparator = Comparator.comparing(pos -> pos.getMethod().format("%H.%n(%p)")); - static final Comparator positionComparator = positionMethodComparator.thenComparing(pos -> pos.getBCI()); + public static final Comparator positionComparator = positionMethodComparator.thenComparing(pos -> pos.getBCI()); public static Path report(String description, String path, String name, String extension, Consumer reporter) { return report(description, path, name, extension, reporter, true); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisFuture.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisFuture.java index cceb2af8e7b0..9dd26e85233b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisFuture.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisFuture.java @@ -67,4 +67,12 @@ public V ensureDone() { } } + public V guardedGet() { + try { + return get(); + } catch (InterruptedException | ExecutionException e) { + throw AnalysisError.shouldNotReachHere(e); + } + } + } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSunSecuritySubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSunSecuritySubstitutions.java index 5dc6f389d666..eeb0c59de3ae 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSunSecuritySubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSunSecuritySubstitutions.java @@ -58,7 +58,11 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { } private static void registerShutdownHook(@SuppressWarnings("unused") DuringAnalysisAccess access) { - RuntimeSupport.getRuntimeSupport().addTearDownHook(new NativeSecureRandomFilesCloserShutdownHook()); + NativeSecureRandomFilesCloserShutdownHook hook = new NativeSecureRandomFilesCloserShutdownHook(); + RuntimeSupport.getRuntimeSupport().addTearDownHook(hook); + // TODO just rescanning the tearDownHooks should be enough + access.rescanObject(RuntimeSupport.getRuntimeSupport()); + access.rescanObject(hook); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java index 776e25bd3f54..00ed54f1cfb9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/SubstrateAllocationSnippets.java @@ -77,6 +77,7 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.allocationprofile.AllocationCounter; import com.oracle.svm.core.allocationprofile.AllocationSite; +import com.oracle.svm.core.annotate.UnknownObjectField; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; import com.oracle.svm.core.graal.nodes.NewStoredContinuationNode; @@ -384,7 +385,7 @@ public SubstrateAllocationProfilingData(AllocationSnippetCounters snippetCounter public abstract static class Templates extends SubstrateTemplates { protected final AllocationSnippetCounters snippetCounters; - private final AllocationProfilingData profilingData; + @UnknownObjectField(types = {AllocationProfilingData.class}) private AllocationProfilingData profilingData; private final SnippetInfo allocateInstance; private final SnippetInfo allocateArray; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/AnnotationTypeSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/AnnotationTypeSupport.java index 063257212b10..addff38f96f7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/AnnotationTypeSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/AnnotationTypeSupport.java @@ -43,14 +43,17 @@ public class AnnotationTypeSupport { private Map, AnnotationType> annotationTypeMap = new HashMap<>(); @Platforms(Platform.HOSTED_ONLY.class) - public void createInstance(Class annotationClass) { - annotationTypeMap.putIfAbsent(annotationClass, AnnotationType.getInstance(annotationClass)); + public boolean createInstance(Class annotationClass) { + return annotationTypeMap.putIfAbsent(annotationClass, AnnotationType.getInstance(annotationClass)) == null; } public AnnotationType getInstance(Class annotationClass) { return annotationTypeMap.get(annotationClass); } + public Map, AnnotationType> getAnnotationTypeMap() { + return annotationTypeMap; + } } @TargetClass(className = "sun.reflect.annotation.AnnotationType") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index e1f19af7975c..b0839cce43d0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -424,8 +424,10 @@ public Object getInterfacesEncoding() { } @Platforms(Platform.HOSTED_ONLY.class) - public void setAnnotationsEncoding(Object annotationsEncoding) { + public boolean setAnnotationsEncoding(Object annotationsEncoding) { + boolean result = this.annotationsEncoding != annotationsEncoding; this.annotationsEncoding = annotationsEncoding; + return result; } @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java index a23352760526..96e5ea27fbd9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java @@ -658,6 +658,13 @@ private Target_sun_security_ssl_SunJSSE(java.security.Provider cryptoProvider, S @TargetClass(className = "sun.security.jca.Providers") final class Target_sun_security_jca_Providers { + /* + * TODO The computation for providerList relies on repeated scans and assumes that eventually + * SecurityServicesFeature.usedProviders contains all used providers. That's why caching is also + * disabled. When the field is read too early is going to clean all providers, since none is yet + * found as used. With the new heap scanning there is no repeated scanning of the field. + */ + // @UnknownObjectField isValidForAnalysis = false @Alias// @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ProviderListTransformer.class, disableCaching = true)// private static ProviderList providerList; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/ImageHeapMap.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/ImageHeapMap.java index 7374640e01c0..6ac76f56378f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/ImageHeapMap.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/ImageHeapMap.java @@ -112,6 +112,7 @@ public void duringAnalysis(DuringAnalysisAccess access) { for (HostedImageHeapMap hostedImageHeapMap : allInstances) { if (needsUpdate(hostedImageHeapMap)) { update(hostedImageHeapMap); + access.rescanObject(hostedImageHeapMap.runtimeMap); access.requireAnalysisIteration(); } } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java index 81ce0f65e4b0..ce4900022458 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java @@ -27,6 +27,7 @@ import static org.graalvm.word.LocationIdentity.ANY_LOCATION; import java.io.PrintStream; +import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; @@ -68,6 +69,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature.CompilationAccess; import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; +import org.graalvm.nativeimage.hosted.Feature.FeatureAccess; import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; @@ -82,6 +84,7 @@ import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.graal.meta.SubstrateMethod; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.util.ReflectionUtil; /** * Holds data that is pre-computed during native image generation and accessed at run time during a @@ -115,6 +118,11 @@ public class GraalSupport { protected final DiagnosticsOutputDirectory outputDirectory = new DiagnosticsOutputDirectory(RuntimeOptionValues.singleton()); protected final Map compilationProblemsPerAction = new EnumMap<>(ExceptionAction.class); + private static final Field graphEncodingField = ReflectionUtil.lookupField(GraalSupport.class, "graphEncoding"); + private static final Field graphObjectsField = ReflectionUtil.lookupField(GraalSupport.class, "graphObjects"); + private static final Field graphNodeTypesField = ReflectionUtil.lookupField(GraalSupport.class, "graphNodeTypes"); + private static final Field methodsToCompileField = ReflectionUtil.lookupField(GraalSupport.class, "methodsToCompile"); + private static final CGlobalData nextIsolateId = CGlobalDataFactory.createWord((Pointer) WordFactory.unsigned(1L)); private volatile long isolateId = 0; @@ -189,38 +197,50 @@ public static void setRuntimeConfig(RuntimeConfiguration runtimeConfig, Suites s } @Platforms(Platform.HOSTED_ONLY.class) - public static boolean setMethodsToCompile(SubstrateMethod[] methodsToCompile) { + public static boolean setMethodsToCompile(DuringAnalysisAccessImpl config, SubstrateMethod[] methodsToCompile) { boolean result = false; - if (!Arrays.equals(get().methodsToCompile, methodsToCompile)) { - get().methodsToCompile = methodsToCompile; + GraalSupport support = get(); + if (!Arrays.equals(support.methodsToCompile, methodsToCompile)) { + support.methodsToCompile = methodsToCompile; + GraalSupport.rescan(support, config, methodsToCompileField); result = true; } return result; } @Platforms(Platform.HOSTED_ONLY.class) - public static boolean setGraphEncoding(byte[] graphEncoding, Object[] graphObjects, NodeClass[] graphNodeTypes) { - if (get().graphObjects == null && graphObjects.length == 0) { + public static boolean setGraphEncoding(FeatureAccess a, byte[] graphEncoding, Object[] graphObjects, NodeClass[] graphNodeTypes) { + GraalSupport support = get(); + if (support.graphObjects == null && graphObjects.length == 0) { assert graphEncoding.length == 0; assert graphNodeTypes.length == 0; return false; } boolean result = false; - if (!Arrays.equals(get().graphEncoding, graphEncoding)) { - get().graphEncoding = graphEncoding; + if (!Arrays.equals(support.graphEncoding, graphEncoding)) { + support.graphEncoding = graphEncoding; + GraalSupport.rescan(support, a, graphEncodingField); result = true; } - if (!Arrays.deepEquals(get().graphObjects, graphObjects)) { - get().graphObjects = graphObjects; + if (!Arrays.deepEquals(support.graphObjects, graphObjects)) { + support.graphObjects = graphObjects; + GraalSupport.rescan(support, a, graphObjectsField); result = true; } - if (!Arrays.equals(get().graphNodeTypes, graphNodeTypes)) { - get().graphNodeTypes = graphNodeTypes; + if (!Arrays.equals(support.graphNodeTypes, graphNodeTypes)) { + support.graphNodeTypes = graphNodeTypes; + GraalSupport.rescan(support, a, graphNodeTypesField); result = true; } return result; } + private static void rescan(GraalSupport support, FeatureAccess a, Field field) { + if (a instanceof DuringAnalysisAccessImpl) { + ((DuringAnalysisAccessImpl) a).rescanField(support, field); + } + } + @Platforms(Platform.HOSTED_ONLY.class) public static void registerImmutableObjects(CompilationAccess access) { access.registerAsImmutable(get().graphEncoding); @@ -240,12 +260,11 @@ public static void registerPhaseStatistics(DuringAnalysisAccess a, Class newl DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; if (!Modifier.isAbstract(newlyReachableClass.getModifiers())) { + GraalSupport support = GraalSupport.get(); if (BasePhase.class.isAssignableFrom(newlyReachableClass)) { - registerStatistics(newlyReachableClass, GraalSupport.get().basePhaseStatistics, new BasePhase.BasePhaseStatistics(newlyReachableClass), access); - + registerStatistics(newlyReachableClass, support.basePhaseStatistics, new BasePhase.BasePhaseStatistics(newlyReachableClass), access); } else if (LIRPhase.class.isAssignableFrom(newlyReachableClass)) { - registerStatistics(newlyReachableClass, GraalSupport.get().lirPhaseStatistics, new LIRPhase.LIRPhaseStatistics(newlyReachableClass), access); - + registerStatistics(newlyReachableClass, support.lirPhaseStatistics, new LIRPhase.LIRPhaseStatistics(newlyReachableClass), access); } } } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java index 7e60908cef60..b55c9069bf36 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/FieldsOffsetsFeature.java @@ -163,14 +163,13 @@ private static Object replaceFieldsOffsets(Object source) { private static void classReachabilityListener(DuringAnalysisAccess a, Class newlyReachableClass) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + GraalSupport support = GraalSupport.get(); if (Node.class.isAssignableFrom(newlyReachableClass) && newlyReachableClass != Node.class) { - FieldsOffsetsFeature.> registerClass(newlyReachableClass, GraalSupport.get().nodeClasses, NodeClass::get, false, access); - + FieldsOffsetsFeature.registerClass(newlyReachableClass, support.nodeClasses, NodeClass::get, false, access); } else if (LIRInstruction.class.isAssignableFrom(newlyReachableClass) && newlyReachableClass != LIRInstruction.class) { - FieldsOffsetsFeature.> registerClass(newlyReachableClass, GraalSupport.get().instructionClasses, LIRInstructionClass::get, true, access); - + FieldsOffsetsFeature.registerClass(newlyReachableClass, support.instructionClasses, LIRInstructionClass::get, true, access); } else if (CompositeValue.class.isAssignableFrom(newlyReachableClass) && newlyReachableClass != CompositeValue.class) { - FieldsOffsetsFeature.> registerClass(newlyReachableClass, GraalSupport.get().compositeValueClasses, CompositeValueClass::get, true, access); + FieldsOffsetsFeature.registerClass(newlyReachableClass, support.compositeValueClasses, CompositeValueClass::get, true, access); } } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java index 206ab948af82..ed1eec15d0a8 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java @@ -493,7 +493,7 @@ public void duringAnalysis(DuringAnalysisAccess c) { for (CallTreeNode node : methods.values()) { methodsToCompileArr[idx++] = objectReplacer.createMethod(node.implementationMethod); } - if (GraalSupport.setMethodsToCompile(methodsToCompileArr)) { + if (GraalSupport.setMethodsToCompile(config, methodsToCompileArr)) { config.requireAnalysisIteration(); } @@ -503,7 +503,7 @@ public void duringAnalysis(DuringAnalysisAccess c) { for (NodeClass nodeClass : nodeClasses) { metaAccess.lookupJavaType(nodeClass.getClazz()).registerAsAllocated(null); } - if (GraalSupport.setGraphEncoding(graphEncoder.getEncoding(), graphEncoder.getObjects(), nodeClasses)) { + if (GraalSupport.setGraphEncoding(config, graphEncoder.getEncoding(), graphEncoder.getObjects(), nodeClasses)) { config.requireAnalysisIteration(); } @@ -727,7 +727,7 @@ public void beforeCompilation(BeforeCompilationAccess c) { } ProgressReporter.singleton().setGraphEncodingByteLength(graphEncoder.getEncoding().length); - GraalSupport.setGraphEncoding(graphEncoder.getEncoding(), graphEncoder.getObjects(), graphEncoder.getNodeClasses()); + GraalSupport.setGraphEncoding(config, graphEncoder.getEncoding(), graphEncoder.getObjects(), graphEncoder.getNodeClasses()); objectReplacer.updateDataDuringAnalysis((AnalysisMetaAccess) hMetaAccess.getWrapped()); } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java index 76dcd99d9580..012949e417b2 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; import org.graalvm.compiler.debug.MetricKey; +import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; import org.graalvm.compiler.hotspot.HotSpotBackendFactory; import org.graalvm.compiler.hotspot.SnippetResolvedJavaMethod; @@ -159,6 +160,9 @@ public Object apply(Object source) { } else if (source instanceof MetricKey) { /* Ensure lazily initialized name fields are computed. */ ((MetricKey) source).getName(); + } else if (source instanceof NodeClass) { + /* Ensure lazily initialized shortName field is computed. */ + ((NodeClass) source).shortName(); } else if (source instanceof HotSpotResolvedJavaMethod) { throw new UnsupportedFeatureException(source.toString()); @@ -217,7 +221,11 @@ public synchronized SubstrateMethod createMethod(ResolvedJavaMethod original) { * Annotations are updated in every analysis iteration, but this is a starting point. It * also ensures that all types used by annotations are created eagerly. */ - sMethod.setAnnotationsEncoding(AnnotationsProcessor.encodeAnnotations(aMetaAccess, aMethod.getAnnotations(), aMethod.getDeclaredAnnotations(), null)); + Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(aMetaAccess, aMethod.getAnnotations(), aMethod.getDeclaredAnnotations(), null); + if (sMethod.setAnnotationsEncoding(annotationsEncoding)) { + aUniverse.getHeapScanner().rescanField(sMethod, SubstrateField.class, "annotationsEncoding"); + // aUniverse.getHeapScanner().rescanObject(sMethod); + } } return sMethod; } @@ -246,12 +254,18 @@ public synchronized SubstrateField createField(ResolvedJavaField original) { fields.put(aField, sField); sField.setLinks(createType(aField.getType()), createType(aField.getDeclaringClass())); + aUniverse.getHeapScanner().rescanField(sField, SubstrateField.class, "type"); + aUniverse.getHeapScanner().rescanField(sField, SubstrateField.class, "declaringClass"); /* * Annotations are updated in every analysis iteration, but this is a starting point. It * also ensures that all types used by annotations are created eagerly. */ - sField.setAnnotationsEncoding(AnnotationsProcessor.encodeAnnotations(aMetaAccess, aField.getAnnotations(), aField.getDeclaredAnnotations(), null)); + Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(aMetaAccess, aField.getAnnotations(), aField.getDeclaredAnnotations(), null); + if (sField.setAnnotationsEncoding(annotationsEncoding)) { + aUniverse.getHeapScanner().rescanField(sField, SubstrateField.class, "annotationsEncoding"); + // aUniverse.getHeapScanner().rescanObject(sField); + } } return sField; } @@ -297,13 +311,16 @@ public synchronized SubstrateType createType(JavaType original) { sType = new SubstrateType(aType.getJavaKind(), hub); types.put(aType, sType); hub.setMetaType(sType); + aUniverse.getHeapScanner().rescanField(hub, DynamicHub.class, "metaType"); sType.setRawAllInstanceFields(createAllInstanceFields(aType)); + aUniverse.getHeapScanner().rescanField(sType, SubstrateType.class, "rawAllInstanceFields"); createType(aType.getSuperclass()); createType(aType.getComponentType()); for (AnalysisType aInterface : aType.getInterfaces()) { createType(aInterface); } + // aUniverse.getHeapScanner().rescanObject(sType); } return sType; } @@ -344,6 +361,7 @@ private synchronized SubstrateSignature createSignature(Signature original) { * infinite recursion. */ sSignature.setTypes(parameterTypes, createType(original.getReturnType(null))); + // aUniverse.getHeapScanner().rescanObject(sSignature); } return sSignature; } @@ -373,19 +391,26 @@ public boolean updateDataDuringAnalysis(AnalysisMetaAccess metaAccess) { implementations[idx++] = sImpl; } if (sMethod.setImplementations(implementations)) { + aUniverse.getHeapScanner().rescanField(sMethod, SubstrateMethod.class, "implementations"); result = true; } } for (Map.Entry entry : methods.entrySet()) { - if (entry.getValue().setAnnotationsEncoding(AnnotationsProcessor.encodeAnnotations(metaAccess, entry.getKey().getAnnotations(), entry.getKey().getDeclaredAnnotations(), - entry.getValue().getAnnotationsEncoding()))) { + SubstrateMethod method = entry.getValue(); + AnalysisMethod sMethod = entry.getKey(); + Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(metaAccess, sMethod.getAnnotations(), sMethod.getDeclaredAnnotations(), method.getAnnotationsEncoding()); + if (method.setAnnotationsEncoding(annotationsEncoding)) { + aUniverse.getHeapScanner().rescanField(method, SubstrateMethod.class, "annotationsEncoding"); result = true; } } for (Map.Entry entry : fields.entrySet()) { - if (entry.getValue().setAnnotationsEncoding(AnnotationsProcessor.encodeAnnotations(metaAccess, entry.getKey().getAnnotations(), entry.getKey().getDeclaredAnnotations(), - entry.getValue().getAnnotationsEncoding()))) { + SubstrateField field = entry.getValue(); + AnalysisField sField = entry.getKey(); + Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(metaAccess, sField.getAnnotations(), sField.getDeclaredAnnotations(), field.getAnnotationsEncoding()); + if (field.setAnnotationsEncoding(annotationsEncoding)) { + aUniverse.getHeapScanner().rescanField(field, SubstrateField.class, "annotationsEncoding"); result = true; } } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateRuntimeConfigurationBuilder.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateRuntimeConfigurationBuilder.java index e7e8bbffbc9f..f7dea69bfefc 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateRuntimeConfigurationBuilder.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateRuntimeConfigurationBuilder.java @@ -75,7 +75,7 @@ protected ConstantReflectionProvider createConstantReflectionProvider(Providers @Override protected ConstantFieldProvider createConstantFieldProvider(Providers p) { - return new AnalysisConstantFieldProvider(aUniverse, (AnalysisMetaAccess) p.getMetaAccess(), (AnalysisConstantReflectionProvider) p.getConstantReflection(), classInitializationSupport); + return new AnalysisConstantFieldProvider(aUniverse, (AnalysisMetaAccess) p.getMetaAccess(), classInitializationSupport); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index d5b9f3c7c68f..97784ba2e7c5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -81,6 +81,7 @@ import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.option.HostedOptionProvider; +import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.UnsafePartitionKind; import jdk.vm.ci.meta.JavaConstant; @@ -245,6 +246,35 @@ public Set reachableMethodOverrides(Executable baseMethod) { Set reachableMethodOverrides(AnalysisMethod baseMethod) { return AnalysisUniverse.getMethodImplementations(getBigBang(), baseMethod, true); } + + public void rescanObject(Object obj) { + getUniverse().getHeapScanner().rescanObject(obj); + } + + public void rescanField(Object receiver, Field field) { + getUniverse().getHeapScanner().rescanField(receiver, field); + } + + public void rescanField(Object receiver, Class declaringClass, String fieldName) { + rescanField(receiver, ReflectionUtil.lookupField(declaringClass, fieldName)); + } + + public void rescanField(Object receiver, String className, String fieldName) { + rescanField(receiver, getImageClassLoader().findClassOrFail(className), fieldName); + } + + public void rescanRoot(Field field) { + getUniverse().getHeapScanner().rescanRoot(field); + } + + public void rescanRoot(Class declaringClass, String fieldName) { + rescanRoot(ReflectionUtil.lookupField(declaringClass, fieldName)); + } + + public void rescanRoot(String declaringClassName, String fieldName) { + rescanRoot(getImageClassLoader().findClassOrFail(declaringClassName), fieldName); + } + } public static class DuringSetupAccessImpl extends AnalysisAccessBase implements Feature.DuringSetupAccess { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java index 7ab7aa98bd6c..feb6c911b8b8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java @@ -281,8 +281,12 @@ public TypeResult> findClass(String name) { } } - Class forName(String name) throws ClassNotFoundException { - return Class.forName(name, false, classLoaderSupport.getClassLoader()); + public Class forName(String className) throws ClassNotFoundException { + return forName(className, false); + } + + public Class forName(String className, boolean initialize) throws ClassNotFoundException { + return Class.forName(className, initialize, classLoaderSupport.getClassLoader()); } public Class forName(String className, Object module) throws ClassNotFoundException { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LoggingFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LoggingFeature.java index 6c11c1cff0f7..5a1f83ae4fda 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LoggingFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LoggingFeature.java @@ -66,6 +66,8 @@ public void duringSetup(DuringSetupAccess access) { public void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + access.rescanRoot("sun.util.logging.PlatformLogger", "loggers"); + if (!reflectionConfigured && access.getMetaAccess().optionalLookupJavaType(java.util.logging.Logger.class).isPresent()) { registerForReflection(java.util.logging.ConsoleHandler.class); registerForReflection(java.util.logging.SimpleFormatter.class); 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 c5ee518fac24..f74b31308577 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 @@ -56,9 +56,6 @@ import java.util.function.BooleanSupplier; import java.util.stream.Collectors; -import com.oracle.graal.pointsto.PointsToAnalysis; -import com.oracle.graal.pointsto.meta.PointsToAnalysisFactory; -import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import org.graalvm.collections.EconomicSet; import org.graalvm.collections.Pair; import org.graalvm.compiler.api.replacements.Fold; @@ -131,12 +128,17 @@ import org.graalvm.nativeimage.impl.clinit.ClassInitializationTracking; import org.graalvm.word.PointerBase; +import com.oracle.graal.pointsto.AnalysisObjectScanningObserver; import com.oracle.graal.pointsto.AnalysisPolicy; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.BytecodeSensitiveAnalysisPolicy; import com.oracle.graal.pointsto.DefaultAnalysisPolicy; +import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; +import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier; +import com.oracle.graal.pointsto.heap.ImageHeap; +import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.infrastructure.WrappedJavaMethod; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -146,6 +148,8 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.graal.pointsto.meta.PointsToAnalysisFactory; +import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import com.oracle.graal.pointsto.reports.AnalysisReporter; import com.oracle.graal.pointsto.typestate.TypeState; import com.oracle.graal.pointsto.util.AnalysisError; @@ -226,6 +230,8 @@ import com.oracle.svm.hosted.analysis.NativeImagePointsToAnalysis; import com.oracle.svm.hosted.analysis.SVMAnalysisMetaAccess; import com.oracle.svm.hosted.analysis.SubstrateUnsupportedFeatures; +import com.oracle.svm.hosted.analysis.heap.SVMImageHeapScanner; +import com.oracle.svm.hosted.analysis.heap.SVMImageHeapVerifier; import com.oracle.svm.hosted.annotation.AnnotationSupport; import com.oracle.svm.hosted.c.CAnnotationProcessorCache; import com.oracle.svm.hosted.c.CConstantValueSupportImpl; @@ -547,6 +553,7 @@ private void doRun(Map entryPoints, hUniverse = new HostedUniverse(bb); hMetaAccess = new HostedMetaAccess(hUniverse, bb.getMetaAccess()); + ((SVMImageHeapScanner) aUniverse.getHeapScanner()).setHostedMetaAccess(hMetaAccess); BeforeUniverseBuildingAccessImpl beforeUniverseBuildingConfig = new BeforeUniverseBuildingAccessImpl(featureHandler, loader, debug, hMetaAccess); featureHandler.forEachFeature(feature -> feature.beforeUniverseBuilding(beforeUniverseBuildingConfig)); @@ -700,8 +707,10 @@ private boolean runPointsToAnalysis(String imageName, OptionValues options, Debu DuringAnalysisAccessImpl config = new DuringAnalysisAccessImpl(featureHandler, loader, bb, nativeLibraries, debug); try { bb.runAnalysis(debug, (universe) -> { - bb.getHostVM().notifyClassReachabilityListener(universe, config); - featureHandler.forEachFeature(feature -> feature.duringAnalysis(config)); + try (StopTimer t2 = bb.getProcessFeaturesTimer().start()) { + bb.getHostVM().notifyClassReachabilityListener(universe, config); + featureHandler.forEachFeature(feature -> feature.duringAnalysis(config)); + } return !config.getAndResetRequireAnalysisIteration(); }); } catch (AnalysisError e) { @@ -829,6 +838,15 @@ private void setupNativeImage(String imageName, Timer classlistTimer, OptionValu bb = createBigBang(options, target, aUniverse, analysisExecutor, watchdog::recordActivity, aMetaAccess, aConstantReflection, aWordTypes, aSnippetReflection, annotationSubstitutions, aForeignCalls, classInitializationSupport, originalProviders); aUniverse.setBigBang(bb); + + /* Create the HeapScanner and install it into the universe. */ + ImageHeap imageHeap = new ImageHeap(); + AnalysisObjectScanningObserver aScanningObserver = new AnalysisObjectScanningObserver(bb); + ImageHeapScanner heapScanner = new SVMImageHeapScanner(bb, imageHeap, loader, aMetaAccess, aSnippetReflection, aConstantReflection, aScanningObserver); + aUniverse.setHeapScanner(heapScanner); + HeapSnapshotVerifier heapVerifier = new SVMImageHeapVerifier(bb, imageHeap, heapScanner); + aUniverse.setHeapVerifier(heapVerifier); + /* Register already created types as assignable. */ aUniverse.getTypes().forEach(t -> t.registerAsAssignable(bb)); @@ -992,7 +1010,7 @@ public static Inflation createBigBang(OptionValues options, TargetDescription ta ClassInitializationSupport classInitializationSupport, Providers originalProviders) { assert aUniverse != null : "Analysis universe must be initialized."; aMetaAccess.lookupJavaType(String.class).registerAsReachable(); - AnalysisConstantFieldProvider aConstantFieldProvider = new AnalysisConstantFieldProvider(aUniverse, aMetaAccess, aConstantReflection, classInitializationSupport); + AnalysisConstantFieldProvider aConstantFieldProvider = new AnalysisConstantFieldProvider(aUniverse, aMetaAccess, classInitializationSupport); /* * Install all snippets so that the types, methods, and fields used in the snippets get * added to the universe. @@ -1449,7 +1467,7 @@ private void checkUniverse() { if (SubstrateOptions.VerifyNamingConventions.getValue()) { for (AnalysisMethod method : aUniverse.getMethods()) { - if ((method.isInvoked() || method.isReachable()) && method.getAnnotation(Fold.class) == null) { + if ((method.isInvoked() || method.isImplementationInvoked()) && method.getAnnotation(Fold.class) == null) { checkName(method.format("%H.%n(%p)"), method); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index 7d1252f684d7..8e1b768abcd7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -268,6 +268,7 @@ public void duringAnalysis(DuringAnalysisAccess access) { ImageSingletons.lookup(ClassLoaderSupport.class).collectResources(collector); resourcePatternWorkSet.clear(); + // access.rescanObject(ImageSingletons.lookup(Resources.ResourcesSupport.class).hostedResources); } private ResourcePattern[] compilePatterns(Set patterns) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java index 7b3a05ae5474..2d9e814b5f4d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java @@ -191,6 +191,9 @@ public static class Options { /** List of providers deemed not to be used by this feature. */ private List removedProviders; + // TODO Filtering temporarily disabled see + // com.oracle.svm.core.jdk.Target_sun_security_jca_Providers.providerList for details. + // private boolean shouldFilterProviders = true; private boolean shouldFilterProviders = true; @Override @@ -296,6 +299,40 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { loader = ((BeforeAnalysisAccessImpl) access).getImageClassLoader(); verificationCacheCleaner = constructVerificationCacheCleaner(); + try { + /* Ensure sun.security.provider.certpath.CertPathHelper.instance is initialized. */ + loader.forName("java.security.cert.TrustAnchor", true); + /* + * Ensure jdk.internal.access.SharedSecrets.javaxCryptoSpecAccess is initialized before + * scanning. + */ + loader.forName("javax.crypto.spec.SecretKeySpec", true); + /* + * Ensure jdk.internal.access.SharedSecrets.javaxCryptoSealedObjectAccess is initialized + * before scanning. + */ + loader.forName("javax.crypto.SealedObject", true); + + /* + * Ensure jdk.internal.access.SharedSecrets.javaIOAccess is initialized before scanning. + */ + loader.forName("java.io.Console", true); + + /* + * Ensure jdk.internal.access.SharedSecrets.javaSecuritySignatureAccess is initialized + * before scanning. + */ + loader.forName("java.security.Signature", true); + + /* + * Ensure all X509Certificate caches are initialized. + */ + loader.forName("sun.security.util.AnchorCertificates", true); + + } catch (ClassNotFoundException e) { + VMError.shouldNotReachHere(e); + } + if (Options.EnableSecurityServicesFeature.getValue()) { registerServiceReachabilityHandlers(access); } @@ -546,7 +583,7 @@ private void doRegisterServices(DuringAnalysisAccess access, Object trigger, Str try (TracingAutoCloseable ignored = trace(access, trigger, serviceType)) { Set services = availableServices.get(serviceType); for (Service service : services) { - registerService(service); + registerService(access, service); } } } @@ -644,7 +681,8 @@ private void registerProvider(Provider provider) { if (!usedProviders.contains(provider)) { usedProviders.add(provider); registerForReflection(provider.getClass()); - + /* Trigger initialization of lazy field java.security.Provider.entrySet. */ + provider.entrySet(); try { Method getVerificationResult = ReflectionUtil.lookupMethod(loader.findClassOrFail("javax.crypto.JceSecurity"), "getVerificationResult", Provider.class); /* @@ -661,7 +699,7 @@ private void registerProvider(Provider provider) { } @SuppressWarnings("try") - private void registerService(Service service) { + private void registerService(DuringAnalysisAccess a, Service service) { TypeResult> serviceClassResult = loader.findClass(service.getClassName()); if (serviceClassResult.isPresent()) { try (TracingAutoCloseable ignored = trace(service)) { @@ -682,7 +720,7 @@ private void registerService(Service service) { registerJks(loader); } if (isCertificateFactory(service) && service.getAlgorithm().equals(X509)) { - registerX509Extensions(); + registerX509Extensions(a); } registerProvider(service.getProvider()); } @@ -706,7 +744,8 @@ private static void registerJks(ImageClassLoader loader) { /** * Register the x509 certificate extension classes for reflection. */ - private static void registerX509Extensions() { + private static void registerX509Extensions(DuringAnalysisAccess a) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; /* * The OIDInfo class which represents the values in the map is not visible. Get the list of * extension names through reflection, i.e., the keys in the map, and use the @@ -723,6 +762,29 @@ private static void registerX509Extensions() { throw VMError.shouldNotReachHere(e); } } + access.rescanRoot(OIDMap.class, "oidMap"); + } + + @Override + public void duringAnalysis(DuringAnalysisAccess a) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + access.rescanRoot("javax.crypto.JceSecurity", "verificationResults"); + access.rescanRoot("sun.security.jca.Providers", "providerList"); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + access.rescanRoot("sun.security.util.ObjectIdentifier", "oidTable"); + } + if (filteredProviderList != null) { + for (Provider provider : filteredProviderList.providers()) { + for (Service service : provider.getServices()) { + if (JavaVersionUtil.JAVA_SPEC >= 17) { + access.rescanField(service, Service.class, "classCache"); + access.rescanField(service, Service.class, "constructorCache"); + } else { + access.rescanField(service, Service.class, "classRef"); + } + } + } + } } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceLoaderFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceLoaderFeature.java index 3f504ac8b767..b68e0d5c758b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceLoaderFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceLoaderFeature.java @@ -363,6 +363,7 @@ private boolean handleType(AnalysisType type, DuringAnalysisAccessImpl access) { debugContext.log("ServiceLoaderFeature: registerResource: " + serviceResourceLocation); } Resources.registerResource(null, serviceResourceLocation, new ByteArrayInputStream(newResourceValue.toString().getBytes(StandardCharsets.UTF_8))); + // access.rescanObject(ImageSingletons.lookup(Resources.ResourcesSupport.class).hostedResources); /* Ensure that the static analysis runs again for the new implementation classes. */ access.requireAnalysisIteration(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java index 0688a83a2668..b305295dda9d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java @@ -42,14 +42,11 @@ public class AnalysisConstantFieldProvider extends SharedConstantFieldProvider { private final AnalysisUniverse universe; private final AnalysisMetaAccess metaAccess; - private final AnalysisConstantReflectionProvider constantReflection; - public AnalysisConstantFieldProvider(AnalysisUniverse universe, AnalysisMetaAccess metaAccess, AnalysisConstantReflectionProvider constantReflection, - ClassInitializationSupport classInitializationSupport) { + public AnalysisConstantFieldProvider(AnalysisUniverse universe, AnalysisMetaAccess metaAccess, ClassInitializationSupport classInitializationSupport) { super(metaAccess, classInitializationSupport); this.universe = universe; this.metaAccess = metaAccess; - this.constantReflection = constantReflection; } @Override @@ -63,7 +60,7 @@ public T readConstantField(ResolvedJavaField field, ConstantFieldTool ana if (readableField.allowConstantFolding()) { JavaConstant fieldValue = readableField.readValue(metaAccess, universe.toHosted(analysisTool.getReceiver())); if (fieldValue != null) { - return analysisTool.foldConstant(constantReflection.interceptValue(f, universe.lookup(fieldValue))); + return analysisTool.foldConstant(AnalysisConstantReflectionProvider.interceptValue(universe, f, universe.lookup(fieldValue))); } } return null; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java index b67be844f287..c377fd42f787 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java @@ -33,10 +33,12 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.word.WordBase; +import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.util.AnalysisError; +import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.RuntimeAssertionsSupport; import com.oracle.svm.core.annotate.InjectAccessors; import com.oracle.svm.core.graal.meta.SharedConstantReflectionProvider; @@ -99,7 +101,21 @@ public JavaConstant readValue(MetaAccessProvider suppliedMetaAccess, AnalysisFie value = universe.lookup(ReadableJavaField.readFieldValue(suppliedMetaAccess, originalConstantReflection, field.wrapped, universe.toHosted(receiver))); } - return interceptValue(field, value); + JavaConstant result = interceptValue(universe, field, value); + + if (!BuildPhaseProvider.isAnalysisFinished()) { + field.markReachable(); + } + + if (!BuildPhaseProvider.isAnalysisFinished() && field.getJavaKind() == JavaKind.Object && field.isRead()) { + /* Make sure the field value is scanned. */ + ImageHeapScanner scanner = universe.getHeapScanner(); + if (scanner.isEnabled()) { + scanner.scanFieldValue(field, receiver); + } + } + + return result; } /* @@ -126,7 +142,7 @@ public JavaConstant readValue(MetaAccessProvider suppliedMetaAccess, AnalysisFie * pretty likely (although not guaranteed) that we are not returning an unintended value for a * class that is re-initialized at run time. */ - private static JavaConstant readUninitializedStaticValue(AnalysisField field) { + public static JavaConstant readUninitializedStaticValue(AnalysisField field) { JavaKind kind = field.getJavaKind(); boolean canHaveConstantValueAttribute = kind.isPrimitive() || field.getType().getName().equals("Ljava/lang/String;"); @@ -175,19 +191,20 @@ private static JavaConstant readUninitializedStaticValue(AnalysisField field) { case Object: Object value = GraalUnsafeAccess.getUnsafe().getObject(base, offset); assert value == null || value instanceof String : "String is currently the only specified object type for the ConstantValue class file attribute"; + // return ImageHeapScanner.asConstant(value); return SubstrateObjectConstant.forObject(value); default: - throw VMError.shouldNotReachHere(); + throw AnalysisError.shouldNotReachHere(); } } - public JavaConstant interceptValue(AnalysisField field, JavaConstant value) { + public static JavaConstant interceptValue(AnalysisUniverse universe, AnalysisField field, JavaConstant value) { JavaConstant result = value; if (result != null) { result = filterInjectedAccessor(field, result); - result = replaceObject(result); + result = replaceObject(universe, result); result = interceptAssertionStatus(field, result); - result = interceptWordType(field, result); + result = interceptWordType(universe, field, result); } return result; } @@ -208,7 +225,7 @@ private static JavaConstant filterInjectedAccessor(AnalysisField field, JavaCons /** * Run all registered object replacers. */ - private JavaConstant replaceObject(JavaConstant value) { + private static JavaConstant replaceObject(AnalysisUniverse universe, JavaConstant value) { if (value == JavaConstant.NULL_POINTER) { return JavaConstant.NULL_POINTER; } @@ -242,7 +259,7 @@ private static JavaConstant interceptAssertionStatus(AnalysisField field, JavaCo * Intercept {@link Word} types. They are boxed objects in the hosted world, but primitive * values in the runtime world. */ - private JavaConstant interceptWordType(AnalysisField field, JavaConstant value) { + private static JavaConstant interceptWordType(AnalysisUniverse universe, AnalysisField field, JavaConstant value) { if (value.getJavaKind() == JavaKind.Object) { Object originalObject = universe.getSnippetReflection().asObject(Object.class, value); if (universe.hostVM().isRelocatedPointer(originalObject)) { @@ -269,7 +286,8 @@ public ResolvedJavaType asJavaType(Constant constant) { if (obj instanceof DynamicHub) { return getHostVM().lookupType((DynamicHub) obj); } else if (obj instanceof Class) { - throw VMError.shouldNotReachHere("Must not have java.lang.Class object: " + obj); + // TODO do we need to make sure the hub is scanned? + return metaAccess.lookupJavaType((Class) obj); } } return null; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java index 1c9101385ab9..76ba418513e6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java @@ -36,6 +36,7 @@ import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; +import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -71,25 +72,27 @@ public DynamicHubInitializer(AnalysisMetaAccess metaAccess, UnsupportedFeatures this.interfacesEncodings = new ConcurrentHashMap<>(); } - public void initializeMetaData(AnalysisType type) { + public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) { assert type.isReachable() : "Type " + type.toJavaName(true) + " is not marked as reachable."; DynamicHub hub = hostVM.dynamicHub(type); if (hub.getGenericInfo() == null) { - fillGenericInfo(type, hub); + fillGenericInfo(heapScanner, type, hub); } if (hub.getAnnotatedSuperInfo() == null) { - fillAnnotatedSuperInfo(type, hub); + fillAnnotatedSuperInfo(heapScanner, type, hub); } if (type.getJavaKind() == JavaKind.Object) { if (type.isArray()) { hub.getComponentHub().setArrayHub(hub); + heapScanner.rescanField(hub.getComponentHub(), DynamicHub.class, "arrayHub"); } try { AnalysisType enclosingType = type.getEnclosingType(); if (enclosingType != null) { hub.setEnclosingClass(hostVM.dynamicHub(enclosingType)); + heapScanner.rescanField(hub, DynamicHub.class, "enclosingClass"); } } catch (UnsupportedFeatureException ex) { unsupportedFeatures.addMessage(type.toJavaName(true), null, ex.getMessage(), null, ex); @@ -97,6 +100,7 @@ public void initializeMetaData(AnalysisType type) { if (hub.getInterfacesEncoding() == null) { fillInterfaces(type, hub); + heapScanner.rescanField(hub, DynamicHub.class, "interfacesEncoding"); } /* @@ -112,7 +116,10 @@ public void initializeMetaData(AnalysisType type) { */ Annotation[] annotations = type.getWrappedWithoutResolve().getAnnotations(); Annotation[] declared = type.getWrappedWithoutResolve().getDeclaredAnnotations(); - hub.setAnnotationsEncoding(AnnotationsProcessor.encodeAnnotations(metaAccess, annotations, declared, hub.getAnnotationsEncoding())); + Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(metaAccess, annotations, declared, hub.getAnnotationsEncoding()); + if (hub.setAnnotationsEncoding(annotationsEncoding)) { + heapScanner.rescanField(hub, DynamicHub.class, "annotationsEncoding"); + } } catch (ArrayStoreException e) { /* If we hit JDK-7183985 just encode the exception. */ hub.setAnnotationsEncoding(e); @@ -164,6 +171,7 @@ public void initializeMetaData(AnalysisType type) { } hub.initEnumConstants(enumConstants); } + heapScanner.rescanField(hub, DynamicHub.class, "enumConstantsReference"); } } } @@ -246,7 +254,7 @@ public int hashCode() { } } - private void fillGenericInfo(AnalysisType type, DynamicHub hub) { + private void fillGenericInfo(ImageHeapScanner heapScanner, AnalysisType type, DynamicHub hub) { Class javaClass = type.getJavaClass(); TypeVariable[] typeParameters = javaClass.getTypeParameters(); @@ -289,9 +297,10 @@ private void fillGenericInfo(AnalysisType type, DynamicHub hub) { genericSuperClass = null; } hub.setGenericInfo(GenericInfo.factory(typeParameters, cachedGenericInterfaces, genericSuperClass)); + heapScanner.rescanField(hub, DynamicHub.class, "genericInfo"); } - private void fillAnnotatedSuperInfo(AnalysisType type, DynamicHub hub) { + private void fillAnnotatedSuperInfo(ImageHeapScanner heapScanner, AnalysisType type, DynamicHub hub) { Class javaClass = type.getJavaClass(); AnnotatedType annotatedSuperclass; @@ -324,6 +333,7 @@ private void fillAnnotatedSuperInfo(AnalysisType type, DynamicHub hub) { AnnotatedType[] cachedAnnotatedInterfaces = annotatedInterfacesMap.computeIfAbsent( new AnnotatedInterfacesEncodingKey(annotatedInterfaces), k -> annotatedInterfaces); hub.setAnnotatedSuperInfo(AnnotatedSuperInfo.factory(annotatedSuperclass, cachedAnnotatedInterfaces)); + heapScanner.rescanField(hub, DynamicHub.class, "annotatedSuperInfo"); } private boolean isTypeAllowed(Type t) { 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 b1baf065e7be..42e7b0419fd4 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 @@ -29,8 +29,6 @@ import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.options.OptionValues; -import com.oracle.graal.pointsto.ObjectScanner; -import com.oracle.graal.pointsto.ObjectScanner.ScanReason; import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; import com.oracle.graal.pointsto.flow.MethodTypeFlow; @@ -42,13 +40,11 @@ import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.graal.meta.SubstrateReplacements; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.hosted.HostedConfiguration; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; -import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaType; public class NativeImagePointsToAnalysis extends PointsToAnalysis implements Inflation { @@ -78,14 +74,6 @@ public MethodTypeFlowBuilder createMethodTypeFlowBuilder(PointsToAnalysis bb, Me return HostedConfiguration.instance().createMethodTypeFlowBuilder(bb, methodFlow); } - @Override - protected void checkObjectGraph(ObjectScanner objectScanner) { - universe.getTypes().stream().filter(AnalysisType::isReachable).forEach(dynamicHubInitializer::initializeMetaData); - - /* Scan hubs of all types that end up in the native image. */ - universe.getTypes().stream().filter(AnalysisType::isReachable).forEach(type -> scanHub(objectScanner, type)); - } - @Override public SVMHost getHostVM() { return (SVMHost) hostVM; @@ -113,10 +101,9 @@ public void onFieldAccessed(AnalysisField field) { unknownFieldHandler.handleUnknownValueField(field); } - private void scanHub(ObjectScanner objectScanner, AnalysisType type) { - SVMHost svmHost = (SVMHost) hostVM; - JavaConstant hubConstant = SubstrateObjectConstant.forObject(svmHost.dynamicHub(type)); - objectScanner.scanConstant(hubConstant, ScanReason.HUB); + @Override + public void onTypeScanned(AnalysisType type) { + dynamicHubInitializer.initializeMetaData(universe.getHeapScanner(), type); } public static ResolvedJavaType toWrappedType(ResolvedJavaType type) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java new file mode 100644 index 000000000000..0455c4a70f59 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.analysis.heap; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.ObjectScanner.ScanReason; +import com.oracle.graal.pointsto.ObjectScanningObserver; +import com.oracle.graal.pointsto.heap.ImageHeap; +import com.oracle.graal.pointsto.heap.ImageHeapScanner; +import com.oracle.graal.pointsto.heap.value.ValueSupplier; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.util.AnalysisFuture; +import com.oracle.svm.core.BuildPhaseProvider; +import com.oracle.svm.core.meta.ReadableJavaField; +import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.ImageClassLoader; +import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; +import com.oracle.svm.hosted.meta.HostedMetaAccess; + +import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.JavaConstant; + +public class SVMImageHeapScanner extends ImageHeapScanner { + + protected HostedMetaAccess hostedMetaAccess; + private final Class economicMapImpl; + + public SVMImageHeapScanner(BigBang bb, ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflection, + ConstantReflectionProvider constantReflection, ObjectScanningObserver aScanningObserver) { + super(bb, imageHeap, metaAccess, snippetReflection, constantReflection, aScanningObserver); + this.economicMapImpl = loader.findClassOrFail("org.graalvm.collections.EconomicMapImpl"); + } + + public void setHostedMetaAccess(HostedMetaAccess hostedMetaAccess) { + this.hostedMetaAccess = hostedMetaAccess; + } + + @Override + protected boolean initializeAtRunTime(AnalysisType type) { + return ((SVMHost) hostVM).getClassInitializationSupport().shouldInitializeAtRuntime(type); + } + + @Override + protected AnalysisFuture getOrCreateConstantReachableTask(JavaConstant javaConstant, ScanReason reason) { + VMError.guarantee(javaConstant instanceof SubstrateObjectConstant, "Not a substrate constant " + javaConstant); + return super.getOrCreateConstantReachableTask(javaConstant, reason); + } + + @Override + protected Object unwrapObject(JavaConstant constant) { + /* + * Unwrap the original object from the constant. Unlike HostedSnippetReflectionProvider this + * will just return the wrapped object, without any transformation. This is important during + * scanning: when scanning a java.lang.Class it will be replaced by a DynamicHub which is + * then actually scanned. The HostedSnippetReflectionProvider returns the original Class for + * a DynamicHub, which would lead to a deadlock during scanning. + */ + return SubstrateObjectConstant.asObject(Object.class, constant); + } + + @Override + public boolean isValueAvailable(AnalysisField field) { + if (field.wrapped instanceof ReadableJavaField) { + ReadableJavaField readableField = (ReadableJavaField) field.wrapped; + return readableField.isValueAvailable(); + } + return super.isValueAvailable(field); + } + + @Override + protected ValueSupplier readHostedFieldValue(AnalysisField field, JavaConstant object) { + if (field.isStatic() && initializeAtRunTime(field.getDeclaringClass())) { + return ValueSupplier.eagerValue(AnalysisConstantReflectionProvider.readUninitializedStaticValue(field)); + } + + if (field.wrapped instanceof ReadableJavaField) { + ReadableJavaField readableField = (ReadableJavaField) field.wrapped; + if (readableField.isValueAvailableDuringAnalysis()) { + /* Materialize and return the value. */ + JavaConstant value = universe.lookup(readableField.readValue(metaAccess, object)); + return ValueSupplier.eagerValue(value); + } else { + /* + * Return a lazy value. This applies to RecomputeFieldValue.Kind.FieldOffset and + * RecomputeFieldValue.Kind.Custom. The value becomes available during hosted + * universe building and is installed by calling + * ComputedValueField.processSubstrate() or by ComputedValueField.readValue(). + * Attempts to materialize the value earlier will result in an error. + */ + return ValueSupplier.lazyValue(() -> universe.lookup(readableField.readValue(hostedMetaAccess, object)), + readableField::isValueAvailable); + } + } + return super.readHostedFieldValue(field, object); + } + + @Override + protected JavaConstant transformFieldValue(AnalysisField field, JavaConstant receiverConstant, JavaConstant originalValueConstant) { + return AnalysisConstantReflectionProvider.interceptValue(universe, field, originalValueConstant); + } + + @Override + protected boolean skipScanning() { + return BuildPhaseProvider.isAnalysisFinished(); + } + + @Override + protected void rescanEconomicMap(EconomicMap map) { + super.rescanEconomicMap(map); + /* Make sure any EconomicMapImpl$CollisionLink objects are scanned. */ + if (map.getClass() == economicMapImpl) { + rescanField(map, economicMapImpl, "entries"); + rescanField(map, economicMapImpl, "hashArray"); + } + + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapVerifier.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapVerifier.java new file mode 100644 index 000000000000..16df81086c33 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapVerifier.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.analysis.heap; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.heap.HeapSnapshotVerifier; +import com.oracle.graal.pointsto.heap.ImageHeap; +import com.oracle.graal.pointsto.heap.ImageHeapScanner; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.hosted.SVMHost; + +import jdk.vm.ci.meta.JavaConstant; + +public class SVMImageHeapVerifier extends HeapSnapshotVerifier { + public SVMImageHeapVerifier(BigBang bb, ImageHeap imageHeap, ImageHeapScanner scanner) { + super(bb, imageHeap, scanner); + } + + @Override + protected void scanTypes(ObjectScanner objectScanner) { + SVMHost svmHost = (SVMHost) bb.getHostVM(); + bb.getUniverse().getTypes().stream().filter(AnalysisType::isReachable).forEach(bb::onTypeScanned); + + bb.getUniverse().getTypes().stream().filter(AnalysisType::isReachable).forEach(t -> scanHub(svmHost, objectScanner, t)); + } + + private static void scanHub(SVMHost svmHost, ObjectScanner objectScanner, AnalysisType type) { + JavaConstant hubConstant = SubstrateObjectConstant.forObject(svmHost.dynamicHub(type)); + objectScanner.scanConstant(hubConstant, ObjectScanner.OtherReason.HUB); + } + +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java index a10facb8c8df..05ef8419ee08 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java @@ -211,7 +211,7 @@ public ResolvedJavaField lookup(ResolvedJavaField field) { @Override public ResolvedJavaMethod lookup(ResolvedJavaMethod method) { - if (isConstantAnnotationType(method.getDeclaringClass())) { + if (isConstantAnnotationType(method.getDeclaringClass()) && !method.getName().equals("proxyClassLookup")) { AnnotationSubstitutionType declaringClass = getSubstitution(method.getDeclaringClass()); AnnotationSubstitutionMethod result = declaringClass.getSubstitutionMethod(method); assert result != null && result.original.equals(method); @@ -236,6 +236,9 @@ private synchronized AnnotationSubstitutionType getSubstitution(ResolvedJavaType for (ResolvedJavaMethod originalMethod : type.getDeclaredMethods()) { AnnotationSubstitutionMethod substitutionMethod; String methodName = canonicalMethodName(originalMethod); + if (methodName.equals("proxyClassLookup")) { + continue; + } if (methodName.equals("equals")) { substitutionMethod = new AnnotationEqualsMethod(originalMethod); } else if (methodName.equals("hashCode")) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java index 81baa14f6f26..21375fd1b6a8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java @@ -78,13 +78,18 @@ public void duringAnalysis(DuringAnalysisAccess access) { }); Stream allElements = Stream.concat(Stream.concat(universe.getFields().stream(), universe.getMethods().stream()), universe.getTypes().stream()); Stream newElements = allElements.filter(visitedElements::add); - newElements.forEach(this::reportAnnotation); + newElements.forEach(e -> reportAnnotation(access, e)); } - private void reportAnnotation(AnnotatedElement element) { + private void reportAnnotation(DuringAnalysisAccess a, AnnotatedElement element) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; for (Annotation annotation : element.getDeclaredAnnotations()) { - if (repeatableAnnotationClasses.contains(annotation.annotationType())) { - ImageSingletons.lookup(AnnotationTypeSupport.class).createInstance(annotation.annotationType()); + Class annotationClass = annotation.annotationType(); + if (repeatableAnnotationClasses.contains(annotationClass)) { + AnnotationTypeSupport annotationTypeSupport = ImageSingletons.lookup(AnnotationTypeSupport.class); + if (annotationTypeSupport.createInstance(annotationClass)) { + access.rescanField(annotationTypeSupport, AnnotationTypeSupport.class, "annotationTypeMap"); + } } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java index 196757e27ac5..a408d0f518d0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java @@ -299,7 +299,9 @@ private Set> initializeSafeDelayedClasses(TypeInitializerGraph initGrap provenSafe.add(c); ClassInitializationInfo initializationInfo = type.getClassInitializer() == null ? ClassInitializationInfo.NO_INITIALIZER_INFO_SINGLETON : ClassInitializationInfo.INITIALIZED_INFO_SINGLETON; - ((SVMHost) universe.hostVM()).dynamicHub(type).setClassInitializationInfo(initializationInfo); + DynamicHub hub = ((SVMHost) universe.hostVM()).dynamicHub(type); + hub.setClassInitializationInfo(initializationInfo); + universe.getHeapScanner().rescanField(hub, DynamicHub.class, "classInitializationInfo"); } } }); @@ -324,6 +326,9 @@ private void buildClassInitializationInfo(FeatureImpl.DuringAnalysisAccessImpl a info = type.getClassInitializer() == null ? ClassInitializationInfo.NO_INITIALIZER_INFO_SINGLETON : ClassInitializationInfo.INITIALIZED_INFO_SINGLETON; } hub.setClassInitializationInfo(info); + // TODO rescanning the hub here should not be necessary + universe.getHeapScanner().scanHub(type); + universe.getHeapScanner().rescanField(hub, DynamicHub.class, "classInitializationInfo"); } private static ClassInitializationInfo buildRuntimeInitializationInfo(FeatureImpl.DuringAnalysisAccessImpl access, AnalysisType type) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java index f12ee3327105..0bf964450126 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java @@ -173,7 +173,7 @@ public void addConstantsToHeap() { for (DataSection.Data data : dataSection) { if (data instanceof SubstrateDataBuilder.ObjectData) { JavaConstant constant = ((SubstrateDataBuilder.ObjectData) data).getConstant(); - addConstantToHeap(constant); + addConstantToHeap(constant, "data section"); } } for (CompilationResult compilationResult : compilations.values()) { @@ -191,10 +191,6 @@ public void addConstantsToHeap() { } } - private void addConstantToHeap(Constant constant) { - addConstantToHeap(constant, null); - } - private void addConstantToHeap(Constant constant, Object reason) { Object obj = SubstrateObjectConstant.asObject(constant); @@ -203,7 +199,7 @@ private void addConstantToHeap(Constant constant, Object reason) { (reason != null ? " Method: " + reason : "")); } - imageHeap.addObject(obj, false, constantReasons.get(constant)); + imageHeap.addObject(obj, false, reason != null ? reason : constantReasons.get(constant)); } protected int getConstantsSize() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JDKRegistrations.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JDKRegistrations.java index c1dc373bf787..b3573db69330 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JDKRegistrations.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JDKRegistrations.java @@ -69,5 +69,8 @@ public void duringSetup(DuringSetupAccess a) { * members and do not allow instantiation. */ rerunClassInit(a, "java.lang.ApplicationShutdownHooks", "java.io.DeleteOnExitHook"); + + /* Trigger initialization of java.net.URLConnection.fileNameMap. */ + java.net.URLConnection.getFileNameMap(); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index 80587c48cdda..6c834d14f353 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -58,12 +58,6 @@ import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; -import com.oracle.svm.core.jdk.localization.BundleContentSubstitutedLocalizationSupport; -import com.oracle.svm.core.jdk.localization.LocalizationSupport; -import com.oracle.svm.core.jdk.localization.OptimizedLocalizationSupport; -import com.oracle.svm.hosted.NativeImageOptions; -import com.oracle.svm.core.jdk.localization.compression.GzipBundleCompression; -import com.oracle.svm.core.jdk.localization.substitutions.Target_sun_util_locale_provider_LocaleServiceProviderPool_OptimizedLocaleMode; import org.graalvm.collections.Pair; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; @@ -78,11 +72,18 @@ import com.oracle.svm.core.ClassLoaderSupport; import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.jdk.localization.BundleContentSubstitutedLocalizationSupport; +import com.oracle.svm.core.jdk.localization.LocalizationSupport; +import com.oracle.svm.core.jdk.localization.OptimizedLocalizationSupport; +import com.oracle.svm.core.jdk.localization.compression.GzipBundleCompression; +import com.oracle.svm.core.jdk.localization.substitutions.Target_sun_util_locale_provider_LocaleServiceProviderPool_OptimizedLocaleMode; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.option.OptionUtils; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.ResolvedJavaField; @@ -310,6 +311,26 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { addResourceBundles(); } + @Override + public void duringAnalysis(DuringAnalysisAccess a) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + if (JavaVersionUtil.JAVA_SPEC >= 17) { + scanLocaleCache(access, "sun.util.locale.BaseLocale$Cache", "CACHE"); + scanLocaleCache(access, "java.util.Locale$Cache", "LOCALECACHE"); + } else { + scanLocaleCache(access, "sun.util.locale.BaseLocale", "CACHE"); + scanLocaleCache(access, "java.util.Locale", "LOCALECACHE"); + } + } + + private static void scanLocaleCache(DuringAnalysisAccessImpl access, String localeClassName, String cacheFieldName) { + access.rescanRoot(localeClassName, cacheFieldName); + // Object localeCache = access.rescanRoot(localeClassName, cacheFieldName); + // Object localeCacheMap = ReflectionUtil.readField(LocaleObjectCache.class, "map", + // localeCache); + // access.rescanObject(localeCacheMap); + } + @Override public void afterAnalysis(AfterAnalysisAccess access) { if (compressionPool != null) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java index 2cf996f90cb4..51b974522260 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java @@ -225,6 +225,13 @@ public boolean isSynthetic() { return original.isSynthetic(); } + /* + * TODO remove this method + * + * It is a catch-all method in case we forgot to register some of the target fields as accessed. + * AnnotationSubstitutionProcessor.processComputedValueFields() also registers the target fields + * of the automatic re-computation as unsafe accessed. + */ public void processAnalysis(AnalysisMetaAccess aMetaAccess) { switch (kind) { case FieldOffset: diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadMTFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadMTFeature.java index 815d2acefc0e..425fc112a312 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadMTFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadMTFeature.java @@ -60,6 +60,7 @@ import com.oracle.svm.core.threadlocal.VMThreadLocalInfos; import com.oracle.svm.core.threadlocal.VMThreadLocalMTSupport; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import jdk.vm.ci.code.MemoryBarriers; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -226,13 +227,15 @@ private boolean handleGetAddress(GraphBuilderContext b, ResolvedJavaMethod targe } @Override - public void duringAnalysis(DuringAnalysisAccess access) { + public void duringAnalysis(DuringAnalysisAccess a) { /* * Update during analysis so that the static analysis sees all infos. After analysis only * the order is going to change. */ if (VMThreadLocalInfos.setInfos(threadLocalCollector.threadLocals.values())) { - access.requireAnalysisIteration(); + a.requireAnalysisIteration(); + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + access.rescanField(ImageSingletons.lookup(VMThreadLocalInfos.class), VMThreadLocalInfos.class, "infos"); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadSTFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadSTFeature.java index 13ec85cf0ca4..dd0be7c34406 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadSTFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadSTFeature.java @@ -56,6 +56,7 @@ import com.oracle.svm.core.threadlocal.VMThreadLocalInfo; import com.oracle.svm.core.threadlocal.VMThreadLocalInfos; import com.oracle.svm.core.threadlocal.VMThreadLocalSTSupport; +import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -205,13 +206,15 @@ private boolean handleGetAddress(GraphBuilderContext b, ResolvedJavaMethod targe } @Override - public void duringAnalysis(DuringAnalysisAccess access) { + public void duringAnalysis(DuringAnalysisAccess a) { /* * Update during analysis so that the static analysis sees all infos. After analysis only * the order is going to change. */ if (VMThreadLocalInfos.setInfos(threadLocalCollector.threadLocals.values())) { - access.requireAnalysisIteration(); + a.requireAnalysisIteration(); + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + access.rescanField(ImageSingletons.lookup(VMThreadLocalInfos.class), VMThreadLocalInfos.class, "infos"); } } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java index a3a49d7852f9..807769f10948 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java @@ -49,6 +49,7 @@ import com.oracle.svm.core.thread.ThreadListenerSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.jfr.traceid.JfrTraceId; import com.oracle.svm.jfr.traceid.JfrTraceIdEpoch; import com.oracle.svm.jfr.traceid.JfrTraceIdMap; @@ -174,7 +175,8 @@ public void beforeCompilation(BeforeCompilationAccess a) { } @Override - public void duringAnalysis(DuringAnalysisAccess access) { + public void duringAnalysis(DuringAnalysisAccess a) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; Class eventClass = access.findClassByName("jdk.internal.event.Event"); if (eventClass != null && access.isReachable(eventClass)) { Set> s = access.reachableSubtypes(eventClass); @@ -187,6 +189,7 @@ public void duringAnalysis(DuringAnalysisAccess access) { try { Field f = c.getDeclaredField("eventHandler"); RuntimeReflection.register(f); + access.rescanRoot(c, "eventHandler"); } catch (Exception e) { throw VMError.shouldNotReachHere("Unable to register eventHandler for: " + c.getCanonicalName(), e); } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessFeature.java index 13576d1f4d07..3c847bfb60c6 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessFeature.java @@ -281,7 +281,7 @@ private void addMethod(Executable method, DuringAnalysisAccessImpl access) { } JNIAccessibleClass jniClass = addClass(method.getDeclaringClass(), access); JNIAccessibleMethodDescriptor descriptor = JNIAccessibleMethodDescriptor.of(method); - jniClass.addMethodIfAbsent(descriptor, d -> { + jniClass.addMethodIfAbsent(access, descriptor, d -> { MetaAccessProvider wrappedMetaAccess = access.getMetaAccess().getWrapped(); JNIJavaCallWrapperMethod varargsCallWrapper = new JNIJavaCallWrapperMethod(method, CallVariant.VARARGS, false, wrappedMetaAccess, nativeLibraries); @@ -318,7 +318,7 @@ private static void addField(Field reflField, boolean writable, DuringAnalysisAc } JNIAccessibleClass jniClass = addClass(reflField.getDeclaringClass(), access); AnalysisField field = access.getMetaAccess().lookupJavaField(reflField); - jniClass.addFieldIfAbsent(field.getName(), name -> new JNIAccessibleField(jniClass, name, field.getJavaKind(), field.getModifiers())); + jniClass.addFieldIfAbsent(access, field.getName(), name -> new JNIAccessibleField(jniClass, name, field.getJavaKind(), field.getModifiers())); field.registerAsJNIAccessed(); field.registerAsRead(null); if (writable) { diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleClass.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleClass.java index ff87a61be8c0..13b18b1d6c24 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleClass.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleClass.java @@ -33,6 +33,7 @@ import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.util.ImageHeapMap; +import com.oracle.svm.hosted.FeatureImpl; import jdk.vm.ci.meta.MetaUtil; @@ -65,22 +66,24 @@ public JNIAccessibleField getField(String name) { } @Platforms(HOSTED_ONLY.class) - void addFieldIfAbsent(String name, Function mappingFunction) { + void addFieldIfAbsent(FeatureImpl.DuringAnalysisAccessImpl access, String name, Function mappingFunction) { if (fields == null) { fields = ImageHeapMap.create(); } if (!fields.containsKey(name)) { fields.put(name, mappingFunction.apply(name)); + access.rescanField(this, JNIAccessibleClass.class, "fields"); } } @Platforms(HOSTED_ONLY.class) - void addMethodIfAbsent(JNIAccessibleMethodDescriptor descriptor, Function mappingFunction) { + void addMethodIfAbsent(FeatureImpl.DuringAnalysisAccessImpl access, JNIAccessibleMethodDescriptor descriptor, Function mappingFunction) { if (methods == null) { methods = ImageHeapMap.create(); } if (!methods.containsKey(descriptor)) { methods.put(descriptor, mappingFunction.apply(descriptor)); + access.rescanField(this, JNIAccessibleClass.class, "methods"); } } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNINativeLinkage.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNINativeLinkage.java index 14389e851dc2..8af345c2574a 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNINativeLinkage.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNINativeLinkage.java @@ -28,6 +28,7 @@ import org.graalvm.word.PointerBase; import org.graalvm.word.WordFactory; +import com.oracle.graal.pointsto.BigBang; import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; import com.oracle.svm.core.graal.code.CGlobalDataInfo; @@ -85,11 +86,12 @@ public boolean isBuiltInFunction() { return (PlatformNativeLibrarySupport.singleton().isBuiltinPkgNative(this.getShortName())); } - public CGlobalDataInfo getBuiltInAddress() { + public CGlobalDataInfo getBuiltInAddress(BigBang bb) { assert this.isBuiltInFunction(); if (builtInAddress == null) { CGlobalData linkage = CGlobalDataFactory.forSymbol(this.getShortName()); builtInAddress = CGlobalDataFeature.singleton().registerAsAccessedOrGet(linkage); + bb.getUniverse().getHeapScanner().rescanField(this, JNINativeLinkage.class, "builtInAddress"); } return builtInAddress; } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperMethod.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperMethod.java index f7d58d95bba2..09422a089f22 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperMethod.java @@ -113,7 +113,7 @@ public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, ValueNode callAddress; if (linkage.isBuiltInFunction()) { - callAddress = kit.unique(new CGlobalDataLoadAddressNode(linkage.getBuiltInAddress())); + callAddress = kit.unique(new CGlobalDataLoadAddressNode(linkage.getBuiltInAddress(providers.getBigBang()))); } else { callAddress = kit.nativeCallAddress(kit.createObject(linkage)); } diff --git a/substratevm/src/com.oracle.svm.methodhandles/src/com/oracle/svm/methodhandles/MethodHandleFeature.java b/substratevm/src/com.oracle.svm.methodhandles/src/com/oracle/svm/methodhandles/MethodHandleFeature.java index 5089c3b4dfcf..9a7cba02cdfd 100644 --- a/substratevm/src/com.oracle.svm.methodhandles/src/com/oracle/svm/methodhandles/MethodHandleFeature.java +++ b/substratevm/src/com.oracle.svm.methodhandles/src/com/oracle/svm/methodhandles/MethodHandleFeature.java @@ -46,6 +46,7 @@ import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.invoke.MethodHandleIntrinsic; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.util.ReflectionUtil; // Checkstyle: stop @@ -99,6 +100,15 @@ public class MethodHandleFeature implements Feature { private Field lambdaFormArity; private Field nameFunction; private Field namedFunctionMemberName; + private Field lambdaFormLFIdentity; + private Field lambdaFormLFZero; + private Field lambdaFormNFIdentity; + private Field lambdaFormNFZero; + private Field typedAccessors; + private Field classSpecializerCache; + private Class lambdaFormClass; + private Class classSpecializerClass; + private Class arrayAccessorClass; @Override public boolean isInConfiguration(IsInConfigurationAccess access) { @@ -123,7 +133,7 @@ public void duringSetup(DuringSetupAccess access) { memberNameIsField = ReflectionUtil.lookupMethod(memberNameClass, "isField"); memberNameGetParameterTypes = ReflectionUtil.lookupMethod(memberNameClass, "getParameterTypes"); - Class lambdaFormClass = access.findClassByName("java.lang.invoke.LambdaForm"); + lambdaFormClass = access.findClassByName("java.lang.invoke.LambdaForm"); lambdaFormNames = ReflectionUtil.lookupField(lambdaFormClass, "names"); lambdaFormArity = ReflectionUtil.lookupField(lambdaFormClass, "arity"); Class nameClass = access.findClassByName("java.lang.invoke.LambdaForm$Name"); @@ -131,6 +141,16 @@ public void duringSetup(DuringSetupAccess access) { Class namedFunctionClass = access.findClassByName("java.lang.invoke.LambdaForm$NamedFunction"); namedFunctionMemberName = ReflectionUtil.lookupField(namedFunctionClass, "member"); + lambdaFormLFIdentity = ReflectionUtil.lookupField(lambdaFormClass, "LF_identity"); + lambdaFormLFZero = ReflectionUtil.lookupField(lambdaFormClass, "LF_zero"); + lambdaFormNFIdentity = ReflectionUtil.lookupField(lambdaFormClass, "NF_identity"); + lambdaFormNFZero = ReflectionUtil.lookupField(lambdaFormClass, "NF_zero"); + + classSpecializerClass = access.findClassByName("java.lang.invoke.ClassSpecializer"); + arrayAccessorClass = access.findClassByName("java.lang.invoke.MethodHandleImpl$ArrayAccessor"); + typedAccessors = ReflectionUtil.lookupField(arrayAccessorClass, "TYPED_ACCESSORS"); + classSpecializerCache = ReflectionUtil.lookupField(classSpecializerClass, "cache"); + access.registerObjectReplacer(this::registerMethodHandle); } @@ -174,6 +194,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { access.registerSubtypeReachabilityHandler(MethodHandleFeature::registerVarHandleMethodsForReflection, access.findClassByName("java.lang.invoke.VarHandle")); + + access.registerSubtypeReachabilityHandler(MethodHandleFeature::scanBoundMethodHandle, boundMethodHandleClass); } private static void registerMHImplFunctionsForReflection(DuringAnalysisAccess access) { @@ -357,4 +379,29 @@ private void registerMemberName(Object memberName) { throw VMError.shouldNotReachHere(e); } } + + @Override + public void duringAnalysis(DuringAnalysisAccess a) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + access.rescanRoot(lambdaFormLFIdentity); + access.rescanRoot(lambdaFormLFZero); + access.rescanRoot(lambdaFormNFIdentity); + access.rescanRoot(lambdaFormNFZero); + access.rescanRoot(typedAccessors); + + // Object specializer = ReflectionUtil.readStaticField(boundMethodHandleClass, + // "SPECIALIZER"); + // // Map cache = ReflectionUtil.readField(classSpecializerClass, "cache", + // specializer); + // // access.rescanObject(cache); + // access.rescanField(specializer, classSpecializerCache); + } + + private static void scanBoundMethodHandle(DuringAnalysisAccess a, Class bmhSubtype) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + Field bmhSpeciesField = ReflectionUtil.lookupField(true, bmhSubtype, "BMH_SPECIES"); + if (bmhSpeciesField != null) { + access.rescanRoot(bmhSpeciesField); + } + } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java index 36455d524d25..a7c1b681906d 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java @@ -411,6 +411,7 @@ private static void makeTypeReachable(DuringAnalysisAccessImpl access, Type type access.requireAnalysisIteration(); } ClassForNameSupport.registerClass(clazz); + // access.rescanObject(annotationTypeSupport.getAnnotationTypeMap()); } else if (type instanceof TypeVariable) { for (Type bound : ((TypeVariable) type).getBounds()) { makeTypeReachable(access, bound); @@ -443,7 +444,10 @@ private static void registerTypesForAnnotationValue(DuringAnalysisAccessImpl acc * Parsing annotation data in reflection classes requires being able to instantiate all * annotation types at runtime. */ - ImageSingletons.lookup(AnnotationTypeSupport.class).createInstance((Class) type); + AnnotationTypeSupport annotationTypeSupport = ImageSingletons.lookup(AnnotationTypeSupport.class); + if (annotationTypeSupport.createInstance((Class) type)) { + access.rescanField(annotationTypeSupport, AnnotationTypeSupport.class, "annotationTypeMap"); + } ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(type); Annotation annotation = (Annotation) value; @@ -507,6 +511,7 @@ private void processClass(DuringAnalysisAccessImpl access, Class clazz) { if (reflectionClasses.contains(clazz)) { ClassForNameSupport.registerClass(clazz); + // access.rescanObject(ImageSingletons.lookup(ClassForNameSupport.class).registeredClasses); } /* @@ -560,6 +565,7 @@ private void processClass(DuringAnalysisAccessImpl access, Class clazz) { buildRecordComponents(clazz, access)); } hub.setReflectionData(reflectionData); + access.rescanField(hub, DynamicHub.class, "rd"); if (type.isAnnotation()) { /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java index d32ddb8c634b..2ce30b961e1a 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java @@ -37,6 +37,7 @@ import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.hosted.ConfigurationTypeResolver; import com.oracle.svm.hosted.FallbackFeature; +import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.NativeImageOptions; @@ -79,8 +80,10 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { } @Override - public void duringAnalysis(DuringAnalysisAccess access) { - proxyRegistry().flushConditionalConfiguration(access); + public void duringAnalysis(DuringAnalysisAccess a) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + access.rescanField(ImageSingletons.lookup(DynamicProxyRegistry.class), DynamicProxySupport.class, "proxyCache"); + proxyRegistry().flushConditionalConfiguration(a); } @Override diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java index ce31066570ff..b0acaca0f7d4 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java @@ -99,6 +99,14 @@ public void duringSetup(DuringSetupAccess a) { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { serializationBuilder.flushConditionalConfiguration(access); + + try { + ImageClassLoader loader = ((FeatureImpl.BeforeAnalysisAccessImpl) access).getImageClassLoader(); + /* Ensure SharedSecrets.javaObjectInputStreamAccess is initialized before scanning. */ + loader.forName("java.io.ObjectInputStream", true); + } catch (ClassNotFoundException e) { + VMError.shouldNotReachHere(e); + } } @Override diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index cd9cba2fa0c5..66a571e62c78 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -209,6 +209,9 @@ public void afterRegistration(AfterRegistrationAccess a) { invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory", "reinitializeNativeImageState", Collections.emptyList()); + // pre-initialize TruffleLogger$LoggerCache.INSTANCE + invokeStaticMethod("com.oracle.truffle.api.TruffleLogger$LoggerCache", "getInstance", Collections.emptyList()); + profilingEnabled = false; } @@ -346,25 +349,39 @@ private static void registerTruffleLibrariesAsInHeap(DuringAnalysisAccess access } @Override - public void duringAnalysis(DuringAnalysisAccess access) { - StaticObjectSupport.duringAnalysis(access); + public void duringAnalysis(DuringAnalysisAccess a) { + DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; - for (Class clazz : access.reachableSubtypes(com.oracle.truffle.api.nodes.Node.class)) { - registerUnsafeAccess(access, clazz.asSubclass(com.oracle.truffle.api.nodes.Node.class)); + for (Class clazz : a.reachableSubtypes(com.oracle.truffle.api.nodes.Node.class)) { + registerUnsafeAccess(a, clazz.asSubclass(com.oracle.truffle.api.nodes.Node.class)); - AnalysisType type = ((DuringAnalysisAccessImpl) access).getMetaAccess().lookupJavaType(clazz); + AnalysisType type = access.getMetaAccess().lookupJavaType(clazz); if (type.isInstantiated()) { graalObjectReplacer.createType(type); } } - for (AnalysisType type : ((DuringAnalysisAccessImpl) access).getBigBang().getUniverse().getTypes()) { - if (!access.isReachable(type.getJavaClass())) { + for (AnalysisType type : access.getBigBang().getUniverse().getTypes()) { + if (!a.isReachable(type.getJavaClass())) { continue; } - initializeTruffleLibrariesAtBuildTime(type); + initializeTruffleLibrariesAtBuildTime(access, type); initializeDynamicObjectLayouts(type); } + // access.rescanRoot("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", + // "REGISTRY"); + // access.rescanRoot("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", + // "CACHE"); + access.rescanRoot("com.oracle.truffle.object.DefaultLayout$LayoutInfo", "LAYOUT_INFO_MAP"); + access.rescanRoot("com.oracle.truffle.polyglot.PolyglotEngineImpl", "ENGINES"); + // access.rescanRoot("com.oracle.truffle.api.TruffleLogger$LoggerCache", "INSTANCE"); + access.rescanRoot("com.oracle.truffle.object.DefaultLayout", "LAYOUT_MAP"); + + // Object instance = access.rescanRoot("com.oracle.truffle.api.TruffleLogger$LoggerCache", + // "INSTANCE"); + // access.rescanField(instance, instance.getClass(), "loggers"); + // Object root = access.rescanField(instance, instance.getClass(), "root"); + // access.rescanField(root, root.getClass(), "children"); } @Override @@ -424,15 +441,22 @@ private void registerUnsafeAccess(DuringAnalysisAccess access, * * @see #registerTruffleLibrariesAsInHeap */ - private static void initializeTruffleLibrariesAtBuildTime(AnalysisType type) { + private static void initializeTruffleLibrariesAtBuildTime(DuringAnalysisAccessImpl access, AnalysisType type) { if (type.isAnnotationPresent(GenerateLibrary.class)) { /* Eagerly resolve library type. */ - LibraryFactory.resolve(type.getJavaClass().asSubclass(Library.class)); + LibraryFactory factory = LibraryFactory.resolve(type.getJavaClass().asSubclass(Library.class)); + /* Trigger computation, then rescan uncachedDispatch. */ + factory.getUncached(); + access.rescanField(factory, LibraryFactory.class, "uncachedDispatch"); } if (type.getDeclaredAnnotationsByType(ExportLibrary.class).length != 0) { /* Eagerly resolve receiver type. */ - invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", "lookup", + Object dispatch = invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", "lookup", Collections.singleton(Class.class), type.getJavaClass()); + // access.rescanField(dispatch, + // "com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", + // "uncachedDispatch"); + access.rescanObject(dispatch); } } @@ -444,8 +468,9 @@ private void initializeDynamicObjectLayouts(AnalysisType type) { Class javaClass = type.getJavaClass(); if (DynamicObject.class.isAssignableFrom(javaClass) && dynamicObjectClasses.add(javaClass)) { // Force layout initialization. - com.oracle.truffle.api.object.Layout.newLayout().type(javaClass.asSubclass(DynamicObject.class)) - .build(); + // Object layout = + com.oracle.truffle.api.object.Layout.newLayout().type(javaClass.asSubclass(DynamicObject.class)).build(); + // access.rescanObject(layout); } } } diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java index 0ce94cf5869b..668aafb067a6 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java @@ -364,6 +364,19 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { */ TruffleBaseFeature.invokeStaticMethod("com.oracle.truffle.polyglot.PolyglotEngineImpl", "resetFallbackEngine", Collections.emptyList()); TruffleBaseFeature.preInitializeEngine(); + + try { + /* Ensure org.graalvm.polyglot.io.IOHelper.IMPL is initialized. */ + ((FeatureImpl.BeforeAnalysisAccessImpl) access).getImageClassLoader().forName("org.graalvm.polyglot.io.IOHelper", true); + } catch (ClassNotFoundException e) { + throw VMError.shouldNotReachHere(e); + } + } + + @Override + public void duringAnalysis(DuringAnalysisAccess a) { + FeatureImpl.DuringAnalysisAccessImpl access = (FeatureImpl.DuringAnalysisAccessImpl) a; + access.rescanRoot("com.oracle.truffle.polyglot.PolyglotEngineImpl", "ENGINES"); } static class TruffleParsingInlineInvokePlugin implements InlineInvokePlugin { From d8a263dce18121dbd0fb2e04900ab8df7fa24534 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 7 Dec 2021 16:35:04 -0800 Subject: [PATCH 09/23] Add Field.isReachable(). --- .../src/com/oracle/svm/core/meta/SharedField.java | 2 ++ .../src/com/oracle/svm/graal/meta/SubstrateField.java | 7 ++++++- .../src/com/oracle/svm/hosted/meta/HostedField.java | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/SharedField.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/SharedField.java index 0ea0d16660ec..e5a98cce3a08 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/SharedField.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/SharedField.java @@ -52,6 +52,8 @@ public interface SharedField extends ResolvedJavaField { boolean isAccessed(); + boolean isReachable(); + boolean isWritten(); JavaKind getStorageKind(); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java index b70bdc42ed1a..c86e6cc00a1e 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java @@ -28,7 +28,6 @@ import java.lang.annotation.Annotation; -import com.oracle.svm.core.util.VMError; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -38,6 +37,7 @@ import com.oracle.svm.core.meta.DirectSubstrateObjectConstant; import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.util.HostedStringDeduplication; +import com.oracle.svm.core.util.VMError; import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.nodes.Node.Children; import com.oracle.truffle.api.nodes.NodeCloneable; @@ -117,6 +117,11 @@ public boolean isAccessed() { return isAccessed; } + @Override + public boolean isReachable() { + return isAccessed || isWritten; + } + @Override public boolean isWritten() { return isWritten; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java index e0d9f51c1cb7..a8174004187c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java @@ -104,6 +104,11 @@ public boolean isAccessed() { return wrapped.isAccessed(); } + @Override + public boolean isReachable() { + return wrapped.isReachable(); + } + public boolean isRead() { return wrapped.isRead(); } From 0b87c2db9ebc0e4aed565486d465b0926662cb07 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Fri, 10 Dec 2021 17:51:56 -0800 Subject: [PATCH 10/23] Trigger initialization of AbstractScope.enclosingScope. --- substratevm/mx.substratevm/suite.py | 1 + .../hosted/ReflectionObjectReplacer.java | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index eb71bc81fb96..d5fb0ea783c5 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -1155,6 +1155,7 @@ "sun.reflect.generics.reflectiveObjects", "sun.reflect.generics.repository", "sun.reflect.generics.tree", + "sun.reflect.generics.scope", "sun.util.calendar", "sun.security.jca", "sun.security.util", diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java index 0458ed3c755a..a8daf5a64c0f 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java @@ -49,6 +49,7 @@ import sun.reflect.generics.repository.FieldRepository; import sun.reflect.generics.repository.GenericDeclRepository; import sun.reflect.generics.repository.MethodRepository; +import sun.reflect.generics.scope.AbstractScope; public class ReflectionObjectReplacer implements Function { private final AnalysisMetaAccess metaAccess; @@ -79,7 +80,8 @@ public boolean equals(Object obj) { @Override public Object apply(Object original) { - if (original instanceof AccessibleObject || original instanceof Parameter || original instanceof AbstractRepository) { + if (original instanceof AccessibleObject || original instanceof Parameter || + original instanceof AbstractRepository || original instanceof AbstractScope) { if (scanned.add(new Identity(original))) { scan(original); } @@ -181,5 +183,22 @@ private void scan(Object original) { classRepository.getSuperclass(); classRepository.getSuperInterfaces(); } + if (original instanceof AbstractScope) { + AbstractScope abstractScope = (AbstractScope) original; + /* + * Lookup a type variable in the scope to trigger creation of + * sun.reflect.generics.scope.AbstractScope.enclosingScope. The looked-up value is not + * important, we just want to trigger creation of lazy internal state. The same eager + * initialization is triggered by + * sun.reflect.generics.repository.MethodRepository.getReturnType() called above, + * however if the AbstractScope is seen first by the heap scanner then a `null` value + * will be snapshotted for the `enclosingScope`. + */ + try { + abstractScope.lookup(""); + } catch (UnsatisfiedLinkError | NoClassDefFoundError e) { + // The lookup calls Class.getEnclosingClass() which may fail. + } + } } } From 2bb96933382aca98101160ef9a8970ffaa333a23 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Fri, 10 Dec 2021 23:13:17 -0800 Subject: [PATCH 11/23] Refactor scanner/verifier. --- .../oracle/graal/pointsto/ObjectScanner.java | 2 +- .../graal/pointsto/PointsToAnalysis.java | 3 +- .../pointsto/heap/HeapSnapshotVerifier.java | 237 +++++++++++++----- .../graal/pointsto/heap/ImageHeapScanner.java | 42 ++-- .../graal/pointsto/meta/AnalysisUniverse.java | 4 + .../com/oracle/svm/hosted/FeatureImpl.java | 12 +- .../svm/hosted/NativeImageGenerator.java | 2 +- .../analysis/heap/SVMImageHeapScanner.java | 16 +- .../svm/truffle/TruffleBaseFeature.java | 6 +- 9 files changed, 230 insertions(+), 94 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index 86966df7c6b5..d53abc734863 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -373,7 +373,7 @@ protected static AnalysisType analysisType(BigBang bb, Object constant) { return bb.getMetaAccess().lookupJavaType(constant.getClass()); } - protected static AnalysisType constantType(BigBang bb, JavaConstant constant) { + public static AnalysisType constantType(BigBang bb, JavaConstant constant) { return bb.getMetaAccess().lookupJavaType(constant); } 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 ac4f085629f1..63d1963477c3 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 @@ -753,8 +753,7 @@ private boolean isHeapStable() throws InterruptedException { boolean foundMismatch; try (StopTimer ignored = verifyHeapTimer.start()) { foundMismatch = universe.getHeapVerifier().verifyHeapSnapshot(executor); - // System.out.println("Verification " + (foundMismatch ? " found " : " didn't find ") + - // " a mismatch."); + System.out.println("Verification " + (foundMismatch ? " found " : " didn't find ") + " a mismatch."); } /* Initialize for the next iteration. */ executor.init(timing); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java index 709d02839452..5340ba217ae3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java @@ -26,9 +26,16 @@ import static com.oracle.graal.pointsto.ObjectScanner.ScanReason; import static com.oracle.graal.pointsto.ObjectScanner.asString; +import static com.oracle.graal.pointsto.ObjectScanner.constantAsObject; +import java.util.HashSet; import java.util.Objects; -import java.util.concurrent.ExecutionException; +import java.util.Set; +import java.util.function.BooleanSupplier; + +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.options.OptionType; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ObjectScanner; @@ -47,6 +54,15 @@ import jdk.vm.ci.meta.JavaConstant; public class HeapSnapshotVerifier { + private static final int QUIET = 0; + private static final int MOST = 1; + private static final int ALL = 2; + + static class Options { + @Option(help = "Control heap verifier verbosity level: 0 - quiet, 1 - print most, 2 - print all.", type = OptionType.Expert)// + public static final OptionKey HeapVerifierVerbosity = new OptionKey<>(0); + } + protected final BigBang bb; protected final ImageHeapScanner scanner; protected final ImageHeap imageHeap; @@ -54,14 +70,51 @@ public class HeapSnapshotVerifier { private ReusableSet scannedObjects; private boolean foundMismatch; - private final boolean printOnMismatch; + private final int verbosity; + + private final Set skipArrayTypes = new HashSet<>(); + /** + * Internal data structure fields that can always be skipped from reporting, for example + * `java.util.HashMap$Node.next`. These fields can change for example when a map is balanced. + */ + private final Set internalFields = new HashSet<>(); + /** + * External data structure fields that should be reported when their value is not in the heap + * (even if they don't link to the value), but should be reported when the value is completely + * missing. + */ + private final Set externalFields = new HashSet<>(); public HeapSnapshotVerifier(BigBang bb, ImageHeap imageHeap, ImageHeapScanner scanner) { this.bb = bb; this.scanner = scanner; this.imageHeap = imageHeap; scannedObjects = new ReusableSet(); - printOnMismatch = ImageHeapScanner.Options.PrintOnMismatch.getValue(bb.getOptions()); + verbosity = Options.HeapVerifierVerbosity.getValue(bb.getOptions()); + + skipArrayTypes.add(scanner.getAnalysisType("java.util.concurrent.ConcurrentHashMap$Node").getArrayClass()); + skipArrayTypes.add(scanner.getAnalysisType("java.util.HashMap$Node").getArrayClass()); + skipArrayTypes.add(scanner.getAnalysisType("java.util.WeakHashMap$Entry").getArrayClass()); + + internalFields.add(scanner.getAnalysisField("java.util.concurrent.ConcurrentHashMap", "table")); + internalFields.add(scanner.getAnalysisField("java.util.concurrent.ConcurrentHashMap$Node", "next")); + internalFields.add(scanner.getAnalysisField("java.util.HashMap", "table")); + internalFields.add(scanner.getAnalysisField("java.util.HashMap$Node", "next")); + internalFields.add(scanner.getAnalysisField("java.util.LinkedList", "last")); + internalFields.add(scanner.getAnalysisField("java.util.LinkedList", "first")); + internalFields.add(scanner.getAnalysisField("java.util.LinkedHashMap", "head")); + internalFields.add(scanner.getAnalysisField("java.util.LinkedHashMap", "tail")); + internalFields.add(scanner.getAnalysisField("java.util.LinkedHashMap$Entry", "before")); + internalFields.add(scanner.getAnalysisField("java.util.LinkedHashMap$Entry", "after")); + internalFields.add(scanner.getAnalysisField("java.util.LinkedList$Node", "prev")); + internalFields.add(scanner.getAnalysisField("java.util.LinkedList$Node", "next")); + internalFields.add(scanner.getAnalysisField("java.util.ArrayList", "elementData")); + + externalFields.add(scanner.getAnalysisField("java.util.concurrent.ConcurrentHashMap$Node", "key")); + externalFields.add(scanner.getAnalysisField("java.util.concurrent.ConcurrentHashMap$Node", "val")); + externalFields.add(scanner.getAnalysisField("java.util.HashMap$Node", "key")); + externalFields.add(scanner.getAnalysisField("java.util.HashMap$Node", "value")); + // externalFields.add(scanner.getAnalysisField("java.lang.ref.Reference", "referent")); ? } public boolean verifyHeapSnapshot(CompletionExecutor executor) throws InterruptedException { @@ -107,41 +160,30 @@ public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sca @Override public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - try { - if (field.isStatic()) { - TypeData typeData = field.getDeclaringClass().getOrComputeData(); - AnalysisFuture fieldValueTask = typeData.getStaticFieldValueTask(field); - if (fieldValueTask.isDone()) { - JavaConstant fieldSnapshot = fieldValueTask.get(); - if (!Objects.equals(fieldSnapshot, fieldValue)) { - warning(reason, "Value mismatch for static field %s %n snapshot: %s %n new value: %s %n", - field, fieldSnapshot, fieldValue); - scanner.patchStaticField(typeData, field, fieldValue, reason).ensureDone(); - } - } else { - warning(reason, "Snapshot not yet computed for field %s %n new value: %s %n", field, fieldValue); - fieldValueTask.ensureDone(); - } - } else { - AnalysisFuture receiverTask = imageHeap.getTask(receiver); - assert receiverTask != null && receiverTask.isDone() : message(reason, "Task %s for receiver %s.", - (receiverTask == null ? "is null" : "not yet executed"), receiver); - ImageHeapInstance receiverObject = (ImageHeapInstance) receiverTask.get(); - AnalysisFuture fieldValueTask = receiverObject.getFieldTask(field); - if (fieldValueTask.isDone()) { - JavaConstant fieldSnapshot = fieldValueTask.get(); - if (!Objects.equals(fieldSnapshot, fieldValue)) { - warning(reason, "Value mismatch for field %s %n snapshot: %s %n new value: %s %n", - field, fieldSnapshot, fieldValue); - scanner.patchInstanceField(receiverObject, field, fieldValue, reason).ensureDone(); - } - } else { - warning(reason, "Snapshot not yet computed for field %s %n new value: %s %n", field, fieldValue); - fieldValueTask.ensureDone(); - } + if (field.isStatic()) { + TypeData typeData = field.getDeclaringClass().getOrComputeData(); + AnalysisFuture fieldValueTask = typeData.getStaticFieldValueTask(field); + if (!fieldValueTask.isDone()) { + warnStaticFieldNotComputed(field, fieldValue, reason); + fieldValueTask.ensureDone(); + } + JavaConstant fieldSnapshot = fieldValueTask.guardedGet(); + if (!Objects.equals(fieldSnapshot, fieldValue)) { + warnStaticFieldMismatch(field, fieldSnapshot, fieldValue, reason); + scanner.patchStaticField(typeData, field, fieldValue, reason).ensureDone(); + } + } else { + ImageHeapInstance receiverObject = (ImageHeapInstance) getReceiverObject(receiver, reason); + AnalysisFuture fieldValueTask = receiverObject.getFieldTask(field); + if (!fieldValueTask.isDone()) { + warnInstanceFieldNotComputed(field, fieldValue, reason); + fieldValueTask.ensureDone(); + } + JavaConstant fieldSnapshot = fieldValueTask.guardedGet(); + if (!Objects.equals(fieldSnapshot, fieldValue)) { + warnInstanceFieldMismatch(field, fieldSnapshot, fieldValue, reason); + scanner.patchInstanceField(receiverObject, field, fieldValue, reason).ensureDone(); } - } catch (InterruptedException | ExecutionException e) { - throw AnalysisError.shouldNotReachHere(e); } } @@ -156,19 +198,20 @@ public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, i @Override public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementValue, AnalysisType elementType, int index, ScanReason reason) { - try { - AnalysisFuture arrayTask = imageHeap.getTask(array); - assert arrayTask != null && arrayTask.isDone() : message(reason, "Task %s for array %s.", - (arrayTask == null ? "is null" : "not yet executed"), array); - ImageHeapArray receiverObject = (ImageHeapArray) arrayTask.get(); - JavaConstant elementSnapshot = receiverObject.getElement(index); - if (!Objects.equals(elementSnapshot, elementValue)) { - warning(reason, "Value mismatch for array element %n snapshot: %s %n new value: %s %n", elementSnapshot, elementValue); - receiverObject.setElement(index, scanner.onArrayElementReachable(array, arrayType, elementValue, index, reason)); - } - } catch (InterruptedException | ExecutionException e) { - throw AnalysisError.shouldNotReachHere(e); + ImageHeapArray arrayObject = (ImageHeapArray) getReceiverObject(array, reason); + JavaConstant elementSnapshot = arrayObject.getElement(index); + if (!Objects.equals(elementSnapshot, elementValue)) { + warnArrayElementMismatch(arrayType, elementSnapshot, elementValue, reason); + arrayObject.setElement(index, scanner.onArrayElementReachable(array, arrayType, elementValue, index, reason)); + } + } + + private ImageHeapObject getReceiverObject(JavaConstant constant, ScanReason reason) { + AnalysisFuture task = imageHeap.getTask(constant); + if (task == null || !task.isDone()) { + throw error(reason, "Task %s for constant %s.", (task == null ? "is null" : "not yet executed"), constant); } + return task.guardedGet(); } @Override @@ -177,13 +220,9 @@ public void forEmbeddedRoot(JavaConstant root, ScanReason reason) { if (rootTask == null) { throw error(reason, "No snapshot task found for embedded root %s %n", root); } else if (rootTask.isDone()) { - try { - JavaConstant rootSnapshot = rootTask.get().getObject(); - if (!Objects.equals(rootSnapshot, root)) { - throw error(reason, "Value mismatch for embedded root %n snapshot: %s %n new value: %s %n", rootSnapshot, root); - } - } catch (InterruptedException | ExecutionException e) { - throw AnalysisError.shouldNotReachHere(e); + JavaConstant rootSnapshot = rootTask.guardedGet().getObject(); + if (!Objects.equals(rootSnapshot, root)) { + throw error(reason, "Value mismatch for embedded root %n snapshot: %s %n new value: %s %n", rootSnapshot, root); } } else { throw error(reason, "Snapshot not yet computed for embedded root %n new value: %s %n", root); @@ -192,21 +231,95 @@ public void forEmbeddedRoot(JavaConstant root, ScanReason reason) { @Override public void forScannedConstant(JavaConstant value, ScanReason reason) { - /* Make sure the value is scanned. */ AnalysisFuture task = imageHeap.getTask(value); - if (task == null) { - scanner.toImageHeapObject(value, reason); + if (constantAsObject(bb, value).getClass().equals(Class.class)) { + /* Make sure the DynamicHub value is scanned. */ + if (task == null) { + warnNoTaskForHub(value, reason); + scanner.toImageHeapObject(value, reason); + } else { + if (!task.isDone()) { + /* If there is a task for the hub it should have been triggered. */ + warnNoTaskComputedForHub(value, reason); + task.ensureDone(); + } + JavaConstant snapshot = task.guardedGet().getObject(); + if (!Objects.equals(snapshot, value)) { + throw error(reason, "Value mismatch for hub snapshot: %s %n new value: %s %n", snapshot, value); + } + } } } } - private void warning(ScanReason reason, String format, Object... args) { + private void warnNoTaskForHub(JavaConstant value, ScanReason reason) { + foundMismatch = true; + if (!skipWarning()) { + warning(reason, "No snapshot task found for hub %s %n", value); + } + } + + private void warnNoTaskComputedForHub(JavaConstant value, ScanReason reason) { foundMismatch = true; - if (printOnMismatch) { - System.out.println("WARNING: " + message(reason, format, args)); + if (!skipWarning()) { + warning(reason, "Snapshot not yet computed for hub %n new value: %s %n", value); } } + private void warnArrayElementMismatch(AnalysisType arrayType, JavaConstant elementSnapshot, JavaConstant elementValue, ScanReason reason) { + foundMismatch = true; + if (skipWarning(() -> skipArrayTypes.contains(arrayType))) { + return; + } + warning(reason, "Value mismatch for array element %n snapshot: %s %n new value: %s %n", elementSnapshot, elementValue); + } + + private void warnStaticFieldMismatch(AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { + foundMismatch = true; + if (!skipWarning(() -> internalFields.contains(field))) { + warning(reason, "Value mismatch for static field %s %n snapshot: %s %n new value: %s %n", field, fieldSnapshot, fieldValue); + } + } + + private void warnStaticFieldNotComputed(AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + foundMismatch = true; + if (!skipWarning(() -> internalFields.contains(field))) { + warning(reason, "Snapshot not yet computed for static field %s %n new value: %s %n", field, fieldValue); + } + } + + private void warnInstanceFieldMismatch(AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { + foundMismatch = true; + if (!skipWarning(() -> internalFields.contains(field))) { + warning(reason, "Value mismatch for instance field %s %n snapshot: %s %n new value: %s %n", field, fieldSnapshot, fieldValue); + } + } + + private void warnInstanceFieldNotComputed(AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + foundMismatch = true; + if (!skipWarning(() -> internalFields.contains(field) || externalFields.contains(field))) { + warning(reason, "Snapshot not yet computed for instance field %s %n new value: %s %n", field, fieldValue); + } + } + + private boolean skipWarning() { + return skipWarning(() -> false); + } + + private boolean skipWarning(BooleanSupplier skip) { + if (verbosity == QUIET) { + return true; + } else if (verbosity == MOST) { + return skip.getAsBoolean(); + } + assert verbosity == ALL; + return false; + } + + private void warning(ScanReason reason, String format, Object... args) { + System.out.println("WARNING: " + message(reason, format, args)); + } + private RuntimeException error(ScanReason reason, String format, Object... args) { throw AnalysisError.shouldNotReachHere(message(reason, format, args)); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index b08b8a676f82..b6a4000dca71 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -38,7 +38,7 @@ import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; -import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.ObjectScanner.ArrayScan; import com.oracle.graal.pointsto.ObjectScanner.EmbeddedRootScan; import com.oracle.graal.pointsto.ObjectScanner.FieldScan; @@ -79,16 +79,12 @@ * When an instance field is marked as accessed the objects of its declaring type (and all the * subtypes) are re-scanned. */ -public class ImageHeapScanner { +public abstract class ImageHeapScanner { public static class Options { @Option(help = "Enable manual rescanning of image heap objects.")// public static final OptionKey EnableManualRescan = new OptionKey<>(true); - - @Option(help = "Print warnings when verifier finds mismatch.")// - public static final OptionKey PrintOnMismatch = new OptionKey<>(false); } - private final BigBang bb; protected final ImageHeap imageHeap; protected final AnalysisMetaAccess metaAccess; protected final AnalysisUniverse universe; @@ -102,9 +98,8 @@ public static class Options { protected boolean isEnabled; private final boolean enableManualRescan; - public ImageHeapScanner(BigBang bb, ImageHeap heap, AnalysisMetaAccess aMetaAccess, - SnippetReflectionProvider aSnippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { - this.bb = bb; + public ImageHeapScanner(ImageHeap heap, AnalysisMetaAccess aMetaAccess, SnippetReflectionProvider aSnippetReflection, + ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { imageHeap = heap; universe = aMetaAccess.getUniverse(); metaAccess = aMetaAccess; @@ -134,6 +129,16 @@ public ImageHeap getImageHeap() { return imageHeap; } + protected abstract Class getClass(String className); + + protected AnalysisType getAnalysisType(String className) { + return metaAccess.lookupJavaType(getClass(className)); + } + + protected AnalysisField getAnalysisField(String className, String fieldName) { + return metaAccess.lookupJavaField(ReflectionUtil.lookupField(getClass(className), fieldName)); + } + public void scanEmbeddedRoot(JavaConstant root, BytecodePosition position) { AnalysisMethod method = (AnalysisMethod) position.getMethod(); AnalysisType type = metaAccess.lookupJavaType(root); @@ -402,6 +407,7 @@ JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, V AnalysisError.guarantee(rawValue.isAvailable(), "Value not yet available for " + field.format("%H.%n")); /* Attempting to materialize the value before it is available may result in an error. */ + // TODO why run the transformer here, it is run by markConstantReachable anyway JavaConstant transformedValue = transformFieldValue(field, receiver, rawValue.get()); if (scanningObserver != null) { @@ -410,6 +416,9 @@ JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, V } else if (transformedValue.isNull()) { scanningObserver.forNullFieldValue(receiver, field, reason); } else { + // TODO this adds the transformedValue in the heap, should we do this here? + // This will also run the replacer again; transformFieldValue already run the + // replacer AnalysisFuture objectFuture = markConstantReachable(transformedValue, reason); /* Notify the points-to analysis of the scan. */ if (objectFuture != null) { @@ -478,12 +487,12 @@ protected boolean skipScanning() { return false; } - public void rescanRoot(Field reflectionField) { + public Object rescanRoot(Field reflectionField) { if (!enableManualRescan) { - return; + return null; } if (skipScanning()) { - return; + return null; } AnalysisType type = metaAccess.lookupJavaType(reflectionField.getDeclaringClass()); if (type.isReachable()) { @@ -492,9 +501,12 @@ public void rescanRoot(Field reflectionField) { TypeData typeData = field.getDeclaringClass().getOrComputeData(); AnalysisFuture fieldTask = patchStaticField(typeData, field, fieldValue, OtherReason.RESCAN); if (field.isRead()) { - rescanCollectionElements(asObject(fieldTask.ensureDone())); + Object root = asObject(fieldTask.ensureDone()); + rescanCollectionElements(root); + return root; } } + return null; } public void rescanField(Object receiver, Class declaringClass, String fieldName) { @@ -591,10 +603,10 @@ void doScan(JavaConstant constant) { public void scanHub(AnalysisType type) { /* Initialize dynamic hub metadata before scanning it. */ - bb.onTypeScanned(type); + universe.onTypeScanned(type); metaAccess.lookupJavaType(java.lang.Class.class).registerAsReachable(); /* We scan the original class here, the scanner does the replacement to DynamicHub. */ - createImageHeapObject(asConstant(type.getJavaClass()), OtherReason.HUB); + toImageHeapObject(asConstant(type.getJavaClass()), ObjectScanner.OtherReason.HUB); } protected AnalysisType analysisType(Object constant) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index fba7bc2d14c4..51cb89d25d1e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -688,6 +688,10 @@ public void onTypeInstantiated(AnalysisType type, UsageKind usage) { bb.onTypeInstantiated(type, usage); } + public void onTypeScanned(AnalysisType type) { + bb.onTypeScanned(type); + } + public SubstitutionProcessor getSubstitutions() { return substitutions; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index 97784ba2e7c5..cec1d697db91 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -263,16 +263,16 @@ public void rescanField(Object receiver, String className, String fieldName) { rescanField(receiver, getImageClassLoader().findClassOrFail(className), fieldName); } - public void rescanRoot(Field field) { - getUniverse().getHeapScanner().rescanRoot(field); + public Object rescanRoot(Field field) { + return getUniverse().getHeapScanner().rescanRoot(field); } - public void rescanRoot(Class declaringClass, String fieldName) { - rescanRoot(ReflectionUtil.lookupField(declaringClass, fieldName)); + public Object rescanRoot(Class declaringClass, String fieldName) { + return rescanRoot(ReflectionUtil.lookupField(declaringClass, fieldName)); } - public void rescanRoot(String declaringClassName, String fieldName) { - rescanRoot(getImageClassLoader().findClassOrFail(declaringClassName), fieldName); + public Object rescanRoot(String declaringClassName, String fieldName) { + return rescanRoot(getImageClassLoader().findClassOrFail(declaringClassName), fieldName); } } 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 f74b31308577..afd414bd4372 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 @@ -842,7 +842,7 @@ private void setupNativeImage(String imageName, Timer classlistTimer, OptionValu /* Create the HeapScanner and install it into the universe. */ ImageHeap imageHeap = new ImageHeap(); AnalysisObjectScanningObserver aScanningObserver = new AnalysisObjectScanningObserver(bb); - ImageHeapScanner heapScanner = new SVMImageHeapScanner(bb, imageHeap, loader, aMetaAccess, aSnippetReflection, aConstantReflection, aScanningObserver); + ImageHeapScanner heapScanner = new SVMImageHeapScanner(imageHeap, loader, aMetaAccess, aSnippetReflection, aConstantReflection, aScanningObserver); aUniverse.setHeapScanner(heapScanner); HeapSnapshotVerifier heapVerifier = new SVMImageHeapVerifier(bb, imageHeap, heapScanner); aUniverse.setHeapVerifier(heapVerifier); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java index 0455c4a70f59..b1b8886e3397 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java @@ -27,7 +27,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; -import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ObjectScanner.ScanReason; import com.oracle.graal.pointsto.ObjectScanningObserver; import com.oracle.graal.pointsto.heap.ImageHeap; @@ -51,19 +50,26 @@ public class SVMImageHeapScanner extends ImageHeapScanner { + private final ImageClassLoader loader; protected HostedMetaAccess hostedMetaAccess; private final Class economicMapImpl; - public SVMImageHeapScanner(BigBang bb, ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflection, - ConstantReflectionProvider constantReflection, ObjectScanningObserver aScanningObserver) { - super(bb, imageHeap, metaAccess, snippetReflection, constantReflection, aScanningObserver); - this.economicMapImpl = loader.findClassOrFail("org.graalvm.collections.EconomicMapImpl"); + public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess, + SnippetReflectionProvider snippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { + super(imageHeap, metaAccess, snippetReflection, aConstantReflection, aScanningObserver); + this.loader = loader; + this.economicMapImpl = this.loader.findClassOrFail("org.graalvm.collections.EconomicMapImpl"); } public void setHostedMetaAccess(HostedMetaAccess hostedMetaAccess) { this.hostedMetaAccess = hostedMetaAccess; } + @Override + protected Class getClass(String className) { + return loader.findClassOrFail(className); + } + @Override protected boolean initializeAtRunTime(AnalysisType type) { return ((SVMHost) hostVM).getClassInitializationSupport().shouldInitializeAtRuntime(type); diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 66a571e62c78..7b707bdd61eb 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -352,6 +352,8 @@ private static void registerTruffleLibrariesAsInHeap(DuringAnalysisAccess access public void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; + StaticObjectSupport.duringAnalysis(a); + for (Class clazz : a.reachableSubtypes(com.oracle.truffle.api.nodes.Node.class)) { registerUnsafeAccess(a, clazz.asSubclass(com.oracle.truffle.api.nodes.Node.class)); @@ -373,9 +375,9 @@ public void duringAnalysis(DuringAnalysisAccess a) { // access.rescanRoot("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", // "CACHE"); access.rescanRoot("com.oracle.truffle.object.DefaultLayout$LayoutInfo", "LAYOUT_INFO_MAP"); - access.rescanRoot("com.oracle.truffle.polyglot.PolyglotEngineImpl", "ENGINES"); - // access.rescanRoot("com.oracle.truffle.api.TruffleLogger$LoggerCache", "INSTANCE"); access.rescanRoot("com.oracle.truffle.object.DefaultLayout", "LAYOUT_MAP"); + // access.rescanRoot("com.oracle.truffle.polyglot.PolyglotEngineImpl", "ENGINES"); + // access.rescanRoot("com.oracle.truffle.api.TruffleLogger$LoggerCache", "INSTANCE"); // Object instance = access.rescanRoot("com.oracle.truffle.api.TruffleLogger$LoggerCache", // "INSTANCE"); From fba7d4bf647825a98937ebce8645c3e4a10e15af Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Fri, 10 Dec 2021 23:28:12 -0800 Subject: [PATCH 12/23] Pre-initialize engine beforeAnalysis. --- .../src/com/oracle/svm/truffle/TruffleBaseFeature.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 7b707bdd61eb..d75a6c4d2448 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -354,8 +354,8 @@ public void duringAnalysis(DuringAnalysisAccess a) { StaticObjectSupport.duringAnalysis(a); - for (Class clazz : a.reachableSubtypes(com.oracle.truffle.api.nodes.Node.class)) { - registerUnsafeAccess(a, clazz.asSubclass(com.oracle.truffle.api.nodes.Node.class)); + for (Class clazz : access.reachableSubtypes(com.oracle.truffle.api.nodes.Node.class)) { + registerUnsafeAccess(access, clazz.asSubclass(com.oracle.truffle.api.nodes.Node.class)); AnalysisType type = access.getMetaAccess().lookupJavaType(clazz); if (type.isInstantiated()) { From 8778a7d83473bf51f734d0a79c46743c0e985f72 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Fri, 10 Dec 2021 22:45:11 -0800 Subject: [PATCH 13/23] Refactor LocalizationFeature. --- .../hosted/jdk/localization/LocalizationFeature.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index 6c834d14f353..a0f74091648c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -89,6 +89,7 @@ import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; +import sun.util.locale.LocaleObjectCache; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.ResourceBundleBasedAdapter; import sun.util.resources.LocaleData; @@ -321,14 +322,14 @@ public void duringAnalysis(DuringAnalysisAccess a) { scanLocaleCache(access, "sun.util.locale.BaseLocale", "CACHE"); scanLocaleCache(access, "java.util.Locale", "LOCALECACHE"); } + scanLocaleCache(access, "java.util.ResourceBundle$Control", "CANDIDATES_CACHE"); } private static void scanLocaleCache(DuringAnalysisAccessImpl access, String localeClassName, String cacheFieldName) { - access.rescanRoot(localeClassName, cacheFieldName); - // Object localeCache = access.rescanRoot(localeClassName, cacheFieldName); - // Object localeCacheMap = ReflectionUtil.readField(LocaleObjectCache.class, "map", - // localeCache); - // access.rescanObject(localeCacheMap); + Object localeCache = access.rescanRoot(localeClassName, cacheFieldName); + if (localeCache != null) { + access.rescanField(localeCache, LocaleObjectCache.class, "map"); + } } @Override From 1596e4a152f0bda5c81aee6fe9b7ec6b672be891 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Sun, 12 Dec 2021 01:53:47 -0800 Subject: [PATCH 14/23] Refactor. --- .../oracle/graal/pointsto/ObjectScanner.java | 20 +- .../pointsto/heap/HeapSnapshotVerifier.java | 47 +++-- .../oracle/graal/pointsto/heap/ImageHeap.java | 16 ++ .../graal/pointsto/heap/ImageHeapScanner.java | 189 ++++++++---------- .../pointsto/{meta => heap}/TypeData.java | 34 ++-- .../graal/pointsto/meta/AnalysisField.java | 4 +- .../graal/pointsto/meta/AnalysisType.java | 1 + .../analysis/heap/SVMImageHeapScanner.java | 12 +- 8 files changed, 159 insertions(+), 164 deletions(-) rename substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/{meta => heap}/TypeData.java (60%) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index d53abc734863..7577993c908f 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -131,12 +131,12 @@ default DebugContext getDebug(OptionValues options, List f } private void scanEmbeddedRoot(JavaConstant root, BytecodePosition position) { - AnalysisMethod method = (AnalysisMethod) position.getMethod(); try { - EmbeddedRootScan reason = new EmbeddedRootScan(method, position, root); + EmbeddedRootScan reason = new EmbeddedRootScan(position, root); scanConstant(root, reason); scanningObserver.forEmbeddedRoot(root, reason); } catch (UnsupportedFeatureException ex) { + AnalysisMethod method = (AnalysisMethod) position.getMethod(); bb.getUnsupportedFeatures().addMessage(method.format("%H.%n(%p)"), method, ex.getMessage(), null, ex); } } @@ -465,26 +465,24 @@ public String toString() { } public static class EmbeddedRootScan extends ScanReason { - final AnalysisMethod method; - final BytecodePosition sourcePosition; + final BytecodePosition position; - public EmbeddedRootScan(AnalysisMethod method, BytecodePosition nodeSourcePosition, JavaConstant root) { - this(method, nodeSourcePosition, root, null); + public EmbeddedRootScan(BytecodePosition nodeSourcePosition, JavaConstant root) { + this(nodeSourcePosition, root, null); } - public EmbeddedRootScan(AnalysisMethod method, BytecodePosition nodeSourcePosition, JavaConstant root, ScanReason previous) { + public EmbeddedRootScan(BytecodePosition nodeSourcePosition, JavaConstant root, ScanReason previous) { super(previous, root); - this.method = method; - this.sourcePosition = nodeSourcePosition; + this.position = nodeSourcePosition; } public AnalysisMethod getMethod() { - return method; + return (AnalysisMethod) position.getMethod(); } @Override public String toString() { - return sourcePosition == null ? method.format("%H.%n(%p)") : method.asStackTraceElement(sourcePosition.getBCI()).toString(); + return position.getMethod().asStackTraceElement(position.getBCI()).toString(); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java index 5340ba217ae3..08c56d3f4e6e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java @@ -46,7 +46,6 @@ import com.oracle.graal.pointsto.heap.ImageHeap.ImageHeapObject; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.TypeData; import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.AnalysisFuture; import com.oracle.graal.pointsto.util.CompletionExecutor; @@ -92,28 +91,28 @@ public HeapSnapshotVerifier(BigBang bb, ImageHeap imageHeap, ImageHeapScanner sc scannedObjects = new ReusableSet(); verbosity = Options.HeapVerifierVerbosity.getValue(bb.getOptions()); - skipArrayTypes.add(scanner.getAnalysisType("java.util.concurrent.ConcurrentHashMap$Node").getArrayClass()); - skipArrayTypes.add(scanner.getAnalysisType("java.util.HashMap$Node").getArrayClass()); - skipArrayTypes.add(scanner.getAnalysisType("java.util.WeakHashMap$Entry").getArrayClass()); - - internalFields.add(scanner.getAnalysisField("java.util.concurrent.ConcurrentHashMap", "table")); - internalFields.add(scanner.getAnalysisField("java.util.concurrent.ConcurrentHashMap$Node", "next")); - internalFields.add(scanner.getAnalysisField("java.util.HashMap", "table")); - internalFields.add(scanner.getAnalysisField("java.util.HashMap$Node", "next")); - internalFields.add(scanner.getAnalysisField("java.util.LinkedList", "last")); - internalFields.add(scanner.getAnalysisField("java.util.LinkedList", "first")); - internalFields.add(scanner.getAnalysisField("java.util.LinkedHashMap", "head")); - internalFields.add(scanner.getAnalysisField("java.util.LinkedHashMap", "tail")); - internalFields.add(scanner.getAnalysisField("java.util.LinkedHashMap$Entry", "before")); - internalFields.add(scanner.getAnalysisField("java.util.LinkedHashMap$Entry", "after")); - internalFields.add(scanner.getAnalysisField("java.util.LinkedList$Node", "prev")); - internalFields.add(scanner.getAnalysisField("java.util.LinkedList$Node", "next")); - internalFields.add(scanner.getAnalysisField("java.util.ArrayList", "elementData")); - - externalFields.add(scanner.getAnalysisField("java.util.concurrent.ConcurrentHashMap$Node", "key")); - externalFields.add(scanner.getAnalysisField("java.util.concurrent.ConcurrentHashMap$Node", "val")); - externalFields.add(scanner.getAnalysisField("java.util.HashMap$Node", "key")); - externalFields.add(scanner.getAnalysisField("java.util.HashMap$Node", "value")); + skipArrayTypes.add(scanner.lookupJavaType("java.util.concurrent.ConcurrentHashMap$Node").getArrayClass()); + skipArrayTypes.add(scanner.lookupJavaType("java.util.HashMap$Node").getArrayClass()); + skipArrayTypes.add(scanner.lookupJavaType("java.util.WeakHashMap$Entry").getArrayClass()); + + internalFields.add(scanner.lookupJavaField("java.util.concurrent.ConcurrentHashMap", "table")); + internalFields.add(scanner.lookupJavaField("java.util.concurrent.ConcurrentHashMap$Node", "next")); + internalFields.add(scanner.lookupJavaField("java.util.HashMap", "table")); + internalFields.add(scanner.lookupJavaField("java.util.HashMap$Node", "next")); + internalFields.add(scanner.lookupJavaField("java.util.LinkedList", "last")); + internalFields.add(scanner.lookupJavaField("java.util.LinkedList", "first")); + internalFields.add(scanner.lookupJavaField("java.util.LinkedHashMap", "head")); + internalFields.add(scanner.lookupJavaField("java.util.LinkedHashMap", "tail")); + internalFields.add(scanner.lookupJavaField("java.util.LinkedHashMap$Entry", "before")); + internalFields.add(scanner.lookupJavaField("java.util.LinkedHashMap$Entry", "after")); + internalFields.add(scanner.lookupJavaField("java.util.LinkedList$Node", "prev")); + internalFields.add(scanner.lookupJavaField("java.util.LinkedList$Node", "next")); + internalFields.add(scanner.lookupJavaField("java.util.ArrayList", "elementData")); + + externalFields.add(scanner.lookupJavaField("java.util.concurrent.ConcurrentHashMap$Node", "key")); + externalFields.add(scanner.lookupJavaField("java.util.concurrent.ConcurrentHashMap$Node", "val")); + externalFields.add(scanner.lookupJavaField("java.util.HashMap$Node", "key")); + externalFields.add(scanner.lookupJavaField("java.util.HashMap$Node", "value")); // externalFields.add(scanner.getAnalysisField("java.lang.ref.Reference", "referent")); ? } @@ -162,7 +161,7 @@ public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sca public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { if (field.isStatic()) { TypeData typeData = field.getDeclaringClass().getOrComputeData(); - AnalysisFuture fieldValueTask = typeData.getStaticFieldValueTask(field); + AnalysisFuture fieldValueTask = typeData.getFieldTask(field); if (!fieldValueTask.isDone()) { warnStaticFieldNotComputed(field, fieldValue, reason); fieldValueTask.ensureDone(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java index e0f1725a6544..25667883cf62 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.AnalysisFuture; @@ -127,10 +128,21 @@ public JavaConstant readFieldValue(AnalysisField field) { return objectFieldValues.get(field).ensureDone(); } + /** + * Return a task for transforming and snapshotting the field value, effectively a future for + * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, JavaConstant, ObjectScanner.ScanReason)}. + */ public AnalysisFuture getFieldTask(AnalysisField field) { return objectFieldValues.get(field); } + /** + * Read the field value, executing the field task in this thread if not already executed. + */ + public JavaConstant readField(AnalysisField field) { + return objectFieldValues.get(field).ensureDone(); + } + public void setFieldTask(AnalysisField field, AnalysisFuture task) { objectFieldValues.put(field, task); } @@ -145,6 +157,10 @@ static final class ImageHeapArray extends ImageHeapObject { this.arrayElementValues = arrayElementValues; } + /** + * Return the value of the element at the specified index as computed by + * {@link ImageHeapScanner#onArrayElementReachable(JavaConstant, AnalysisType, JavaConstant, int, ObjectScanner.ScanReason)}. + */ public JavaConstant getElement(int idx) { return arrayElementValues[idx]; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index b6a4000dca71..59479500a72e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -53,10 +53,8 @@ import com.oracle.graal.pointsto.heap.value.ValueSupplier; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; -import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; -import com.oracle.graal.pointsto.meta.TypeData; import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.AnalysisFuture; import com.oracle.graal.pointsto.util.GraalAccess; @@ -94,6 +92,7 @@ public static class Options { protected final ConstantReflectionProvider constantReflection; protected final ConstantReflectionProvider hostedConstantReflection; protected final SnippetReflectionProvider hostedSnippetReflection; + protected ObjectScanningObserver scanningObserver; protected boolean isEnabled; private final boolean enableManualRescan; @@ -101,8 +100,8 @@ public static class Options { public ImageHeapScanner(ImageHeap heap, AnalysisMetaAccess aMetaAccess, SnippetReflectionProvider aSnippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { imageHeap = heap; - universe = aMetaAccess.getUniverse(); metaAccess = aMetaAccess; + universe = aMetaAccess.getUniverse(); hostVM = aMetaAccess.getUniverse().hostVM(); snippetReflection = aSnippetReflection; constantReflection = aConstantReflection; @@ -125,52 +124,45 @@ public boolean isEnabled() { return isEnabled; } - public ImageHeap getImageHeap() { - return imageHeap; - } - - protected abstract Class getClass(String className); - - protected AnalysisType getAnalysisType(String className) { - return metaAccess.lookupJavaType(getClass(className)); - } - - protected AnalysisField getAnalysisField(String className, String fieldName) { - return metaAccess.lookupJavaField(ReflectionUtil.lookupField(getClass(className), fieldName)); - } - public void scanEmbeddedRoot(JavaConstant root, BytecodePosition position) { - AnalysisMethod method = (AnalysisMethod) position.getMethod(); AnalysisType type = metaAccess.lookupJavaType(root); type.registerAsReachable(); - toImageHeapObject(root, new EmbeddedRootScan(method, position, root)); + getOrCreateConstantReachableTask(root, new EmbeddedRootScan(position, root)).ensureDone(); } public void scanFieldValue(AnalysisField field, JavaConstant receiver) { - /* - * If this read is for constant folding then the read is not parsed yet and the field is not - * marked as reachable. - */ - field.markReachable(); + assert field.isReachable() && isValueAvailable(field); if (field.isStatic()) { - TypeData declaringClassData = field.getDeclaringClass().getOrComputeData(); - declaringClassData.getStaticFieldValueTask(field).ensureDone(); + postTask(() -> field.getDeclaringClass().getOrComputeData().readField(field)); } else { - ImageHeapObject receiverObject = getImageHeapObject(receiver); - if (receiverObject instanceof ImageHeapInstance) { - ((ImageHeapInstance) receiverObject).getFieldTask(field).ensureDone(); + postTask(() -> ((ImageHeapInstance) toImageHeapObject(receiver)).readField(field)); + } + } + + public void onFieldRead(AnalysisField field) { + assert field.isRead(); + /* Check if the value is available before accessing it. */ + if (isValueAvailable(field)) { + AnalysisType declaringClass = field.getDeclaringClass(); + if (field.isStatic()) { + postTask(() -> declaringClass.getOrComputeData().readField(field)); + } else { + /* Trigger field scanning for the already processed objects. */ + postTask(() -> onInstanceFieldRead(field, declaringClass)); } } } - ImageHeapObject getImageHeapObject(Constant constant) { - if (constant instanceof JavaConstant) { - JavaConstant javaConstant = (JavaConstant) constant; - if (javaConstant.getJavaKind() == JavaKind.Object && javaConstant.isNonNull()) { - return toImageHeapObject(javaConstant); + private void onInstanceFieldRead(AnalysisField field, AnalysisType type) { + for (AnalysisType subtype : type.getSubTypes()) { + for (ImageHeapObject imageHeapObject : imageHeap.getObjects(subtype)) { + postTask(((ImageHeapInstance) imageHeapObject).getFieldTask(field)); + } + /* Subtypes include this type itself. */ + if (!subtype.equals(type)) { + onInstanceFieldRead(field, subtype); } } - return null; } /** @@ -181,15 +173,14 @@ public TypeData computeTypeData(AnalysisType type) { GraalError.guarantee(type.isReachable(), "TypeData is only available for reachable types"); /* Decide if the type should be initialized at build time or at run time: */ - boolean initializeAtRunTime = initializeAtRunTime(type); + boolean initializeAtRunTime = shouldInitializeAtRunTime(type); - Map> rawStaticFieldValues; /* * Snapshot all static fields. This reads the raw field value of all fields regardless of * reachability status. The field value is processed when a field is marked as reachable, in - * onFieldReachable(). + * onFieldValueReachable(). */ - rawStaticFieldValues = new HashMap<>(); + Map> rawStaticFieldValues = new HashMap<>(); for (AnalysisField field : type.getStaticFields()) { ValueSupplier rawFieldValue = readHostedFieldValue(field, null); rawStaticFieldValues.put(field, new AnalysisFuture<>(() -> onFieldValueReachable(field, null, rawFieldValue, new FieldScan(field)))); @@ -198,7 +189,7 @@ public TypeData computeTypeData(AnalysisType type) { return new TypeData(initializeAtRunTime, rawStaticFieldValues); } - protected boolean initializeAtRunTime(@SuppressWarnings("unused") AnalysisType type) { + protected boolean shouldInitializeAtRunTime(@SuppressWarnings("unused") AnalysisType type) { return false; } @@ -209,38 +200,6 @@ void markTypeInstantiated(AnalysisType type) { type.registerAsInHeap(); } - public void onFieldRead(AnalysisField field) { - assert field.isRead(); - execute(() -> onFieldReadTask(field)); - } - - private void onFieldReadTask(AnalysisField field) { - AnalysisType declaringClass = field.getDeclaringClass(); - if (field.isStatic()) { - TypeData declaringClassData = declaringClass.getOrComputeData(); - /* Check if the value is available before accessing it. */ - if (isValueAvailable(field)) { - execute(declaringClassData.getStaticFieldValueTask(field)); - } - } else { - if (isValueAvailable(field)) { - onInstanceFieldRead(field, declaringClass); - } - } - } - - private void onInstanceFieldRead(AnalysisField field, AnalysisType type) { - for (AnalysisType subtype : type.getSubTypes()) { - for (ImageHeapObject imageHeapObject : imageHeap.getObjects(subtype)) { - execute(((ImageHeapInstance) imageHeapObject).getFieldTask(field)); - } - /* Subtypes may include this type itself. */ - if (!subtype.equals(type)) { - onInstanceFieldRead(field, subtype); - } - } - } - AnalysisFuture markConstantReachable(Constant constant, ScanReason reason) { if (!(constant instanceof JavaConstant)) { /* @@ -267,14 +226,14 @@ public ImageHeapObject toImageHeapObject(JavaConstant constant) { return toImageHeapObject(constant, OtherReason.SCAN); } - public ImageHeapObject toImageHeapObject(JavaConstant javaConstant, ScanReason reason) { - assert javaConstant.getJavaKind() == JavaKind.Object && javaConstant.isNonNull(); - return getOrCreateConstantReachableTask(javaConstant, reason).ensureDone(); + public ImageHeapObject toImageHeapObject(JavaConstant constant, ScanReason reason) { + assert constant != null && constant.getJavaKind() == JavaKind.Object && constant.isNonNull(); + return getOrCreateConstantReachableTask(constant, reason).ensureDone(); } public void scan(JavaConstant javaConstant, ScanReason reason) { assert javaConstant.getJavaKind() == JavaKind.Object && javaConstant.isNonNull(); - execute(getOrCreateConstantReachableTask(javaConstant, reason)); + postTask(getOrCreateConstantReachableTask(javaConstant, reason)); } protected AnalysisFuture getOrCreateConstantReachableTask(JavaConstant javaConstant, ScanReason reason) { @@ -298,36 +257,13 @@ protected AnalysisFuture getOrCreateConstantReachableTask(JavaC * Immediately schedule the new task. There is no need to have not-yet-reachable * ImageHeapObject. */ - execute(newTask); + postTask(newTask); return newTask; } } return existingTask; } - private Optional maybeReplace(JavaConstant constant, ScanReason reason) { - Object unwrapped = unwrapObject(constant); - if (unwrapped == null) { - throw GraalError.shouldNotReachHere(formatReason("Could not unwrap constant", reason)); - } else if (unwrapped instanceof ImageHeapObject) { - throw GraalError.shouldNotReachHere(formatReason("Double wrapping of constant. Most likely, the reachability analysis code itself is seen as reachable.", reason)); - } - - /* Run all registered object replacers. */ - if (constant.getJavaKind() == JavaKind.Object) { - Object replaced = universe.replaceObject(unwrapped); - if (replaced != unwrapped) { - JavaConstant replacedConstant = universe.getSnippetReflection().forObject(replaced); - return Optional.of(replacedConstant); - } - } - return Optional.empty(); - } - - protected Object unwrapObject(JavaConstant constant) { - return snippetReflection.asObject(Object.class, constant); - } - protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReason reason) { assert constant.getJavaKind() == JavaKind.Object && !constant.isNull(); @@ -388,10 +324,33 @@ protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReaso /* * Following all the array elements and reachable field values can be done asynchronously. */ - execute(() -> onObjectReachable(newImageHeapObject)); + postTask(() -> onObjectReachable(newImageHeapObject)); return newImageHeapObject; } + private Optional maybeReplace(JavaConstant constant, ScanReason reason) { + Object unwrapped = unwrapObject(constant); + if (unwrapped == null) { + throw GraalError.shouldNotReachHere(formatReason("Could not unwrap constant", reason)); + } else if (unwrapped instanceof ImageHeapObject) { + throw GraalError.shouldNotReachHere(formatReason("Double wrapping of constant. Most likely, the reachability analysis code itself is seen as reachable.", reason)); + } + + /* Run all registered object replacers. */ + if (constant.getJavaKind() == JavaKind.Object) { + Object replaced = universe.replaceObject(unwrapped); + if (replaced != unwrapped) { + JavaConstant replacedConstant = universe.getSnippetReflection().forObject(replaced); + return Optional.of(replacedConstant); + } + } + return Optional.empty(); + } + + protected Object unwrapObject(JavaConstant constant) { + return snippetReflection.asObject(Object.class, constant); + } + JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, JavaConstant fieldValue, ScanReason reason) { return onFieldValueReachable(field, receiver, ValueSupplier.eagerValue(fieldValue), reason); } @@ -462,7 +421,7 @@ void onObjectReachable(ImageHeapObject imageHeapObject) { ImageHeapInstance imageHeapInstance = (ImageHeapInstance) imageHeapObject; for (AnalysisField field : imageHeapObject.type.getInstanceFields(true)) { if (field.isReachable() && field.isRead() && isValueAvailable(field)) { - execute(imageHeapInstance.getFieldTask(field)); + postTask(imageHeapInstance.getFieldTask(field)); } } } @@ -476,10 +435,10 @@ protected String formatReason(String message, ScanReason reason) { return message + ' ' + reason; } - protected ValueSupplier readHostedFieldValue(AnalysisField field, JavaConstant object) { - assert !field.isStatic() || !initializeAtRunTime(field.getDeclaringClass()); + protected ValueSupplier readHostedFieldValue(AnalysisField field, JavaConstant receiver) { + assert !field.isStatic() || !shouldInitializeAtRunTime(field.getDeclaringClass()); // Wrap the hosted constant into a substrate constant - JavaConstant value = universe.lookup(hostedConstantReflection.readFieldValue(field.wrapped, object)); + JavaConstant value = universe.lookup(hostedConstantReflection.readFieldValue(field.wrapped, receiver)); return ValueSupplier.eagerValue(value); } @@ -531,7 +490,7 @@ public void rescanField(Object receiver, Field reflectionField) { } JavaConstant fieldValue = readHostedFieldValue(field, universe.toHosted(receiverConstant)).get(); if (fieldValue != null) { - ImageHeapInstance receiverObject = (ImageHeapInstance) getImageHeapObject(receiverConstant); + ImageHeapInstance receiverObject = (ImageHeapInstance) toImageHeapObject(receiverConstant); AnalysisFuture fieldTask = patchInstanceField(receiverObject, field, fieldValue, OtherReason.RESCAN); if (field.isRead()) { rescanCollectionElements(asObject(fieldTask.ensureDone())); @@ -542,7 +501,7 @@ public void rescanField(Object receiver, Field reflectionField) { protected AnalysisFuture patchStaticField(TypeData typeData, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, null, fieldValue, reason)); - typeData.setStaticFieldValueTask(field, task); + typeData.setFieldTask(field, task); return task; } @@ -635,14 +594,24 @@ public ObjectScanningObserver getScanningObserver() { return scanningObserver; } - public void execute(AnalysisFuture future) { + protected abstract Class getClass(String className); + + protected AnalysisType lookupJavaType(String className) { + return metaAccess.lookupJavaType(getClass(className)); + } + + protected AnalysisField lookupJavaField(String className, String fieldName) { + return metaAccess.lookupJavaField(ReflectionUtil.lookupField(getClass(className), fieldName)); + } + + public void postTask(AnalysisFuture future) { if (future.isDone()) { return; } - ((PointsToAnalysis) universe.getBigbang()).postTask(debug -> future.run()); + ((PointsToAnalysis) universe.getBigbang()).postTask(debug -> future.ensureDone()); } - public void execute(Runnable task) { + public void postTask(Runnable task) { ((PointsToAnalysis) universe.getBigbang()).postTask(debug -> task.run()); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/TypeData.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/TypeData.java similarity index 60% rename from substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/TypeData.java rename to substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/TypeData.java index 46e05129730f..27afd60e5ae0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/TypeData.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/TypeData.java @@ -22,10 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.graal.pointsto.meta; +package com.oracle.graal.pointsto.heap; import java.util.Map; +import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.AnalysisFuture; import jdk.vm.ci.meta.JavaConstant; @@ -36,34 +39,41 @@ * as reachable. Computed lazily once the type is seen as reachable. */ public final class TypeData { - /** The class initialization state: initialize the class at build time or at run time. */ - final boolean initializeAtRunTime; + /** + * The class initialization state: initialize the class at build time or at run time. + */ + final boolean shouldInitializeAtRunTime; /** * The raw values of all static fields, regardless of field reachability status. Evaluating the * {@link AnalysisFuture} runs - * com.oracle.graal.pointsto.heap.ImageHeapScanner#onFieldValueReachable adds the result to the - * image heap}. + * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, JavaConstant, ObjectScanner.ScanReason)} + * adds the result to the image heap}. */ final Map> staticFieldValues; - public TypeData(boolean initializeAtRunTime, Map> staticFieldValues) { - this.initializeAtRunTime = initializeAtRunTime; + public TypeData(boolean shouldInitializeAtRunTime, Map> staticFieldValues) { + this.shouldInitializeAtRunTime = shouldInitializeAtRunTime; this.staticFieldValues = staticFieldValues; } - public boolean initializeAtRunTime() { - return initializeAtRunTime; + public boolean shouldInitializeAtRunTime() { + return shouldInitializeAtRunTime; } - public AnalysisFuture getStaticFieldValueTask(AnalysisField field) { + /** + * Return a task for transforming and snapshotting the field value, effectively a future for + * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, JavaConstant, ObjectScanner.ScanReason)}. + */ + public AnalysisFuture getFieldTask(AnalysisField field) { return staticFieldValues.get(field); } - public JavaConstant readStaticFieldValue(AnalysisField field) { + /** Read the field value, executing the field task in this thread if not already executed. */ + public JavaConstant readField(AnalysisField field) { return staticFieldValues.get(field).ensureDone(); } - public void setStaticFieldValueTask(AnalysisField field, AnalysisFuture task) { + public void setFieldTask(AnalysisField field, AnalysisFuture task) { staticFieldValues.put(field, task); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java index ccad6d103291..291a124d64bd 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java @@ -101,6 +101,7 @@ public class AnalysisField implements ResolvedJavaField, OriginalFieldProvider { private final AnalysisType declaringClass; private final AnalysisType fieldType; + private final boolean isStatic; public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField) { assert !wrappedField.isInternal(); @@ -111,6 +112,7 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField) this.wrapped = wrappedField; this.id = universe.nextFieldId.getAndIncrement(); + this.isStatic = Modifier.isStatic(this.getModifiers()); boolean trackAccessChain = PointstoOptions.TrackAccessChain.getValue(universe.hostVM().options()); readBy = trackAccessChain ? new ConcurrentHashMap<>() : null; @@ -476,7 +478,7 @@ public boolean isSynthetic() { @Override public boolean isStatic() { - return Modifier.isStatic(this.getModifiers()); + return isStatic; } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index da61268994f9..0b6af67853c9 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -52,6 +52,7 @@ import com.oracle.graal.pointsto.flow.AllInstantiatedTypeFlow; import com.oracle.graal.pointsto.flow.context.object.AnalysisObject; import com.oracle.graal.pointsto.flow.context.object.ConstantContextSensitiveObject; +import com.oracle.graal.pointsto.heap.TypeData; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.infrastructure.WrappedJavaType; import com.oracle.graal.pointsto.typestate.TypeState; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java index b1b8886e3397..14a246fe3308 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java @@ -71,7 +71,7 @@ protected Class getClass(String className) { } @Override - protected boolean initializeAtRunTime(AnalysisType type) { + protected boolean shouldInitializeAtRunTime(AnalysisType type) { return ((SVMHost) hostVM).getClassInitializationSupport().shouldInitializeAtRuntime(type); } @@ -103,8 +103,8 @@ public boolean isValueAvailable(AnalysisField field) { } @Override - protected ValueSupplier readHostedFieldValue(AnalysisField field, JavaConstant object) { - if (field.isStatic() && initializeAtRunTime(field.getDeclaringClass())) { + protected ValueSupplier readHostedFieldValue(AnalysisField field, JavaConstant receiver) { + if (field.isStatic() && shouldInitializeAtRunTime(field.getDeclaringClass())) { return ValueSupplier.eagerValue(AnalysisConstantReflectionProvider.readUninitializedStaticValue(field)); } @@ -112,7 +112,7 @@ protected ValueSupplier readHostedFieldValue(AnalysisField field, ReadableJavaField readableField = (ReadableJavaField) field.wrapped; if (readableField.isValueAvailableDuringAnalysis()) { /* Materialize and return the value. */ - JavaConstant value = universe.lookup(readableField.readValue(metaAccess, object)); + JavaConstant value = universe.lookup(readableField.readValue(metaAccess, receiver)); return ValueSupplier.eagerValue(value); } else { /* @@ -122,11 +122,11 @@ protected ValueSupplier readHostedFieldValue(AnalysisField field, * ComputedValueField.processSubstrate() or by ComputedValueField.readValue(). * Attempts to materialize the value earlier will result in an error. */ - return ValueSupplier.lazyValue(() -> universe.lookup(readableField.readValue(hostedMetaAccess, object)), + return ValueSupplier.lazyValue(() -> universe.lookup(readableField.readValue(hostedMetaAccess, receiver)), readableField::isValueAvailable); } } - return super.readHostedFieldValue(field, object); + return super.readHostedFieldValue(field, receiver); } @Override From 9578b119957ebc3795c7a91ed20231b342371cf9 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Sat, 11 Dec 2021 19:08:24 -0800 Subject: [PATCH 15/23] Don't trigger scanning from AnalysisConstantReflectionProvider. --- .../graal/pointsto/heap/HeapSnapshotVerifier.java | 2 -- .../graal/pointsto/heap/ImageHeapScanner.java | 15 +-------------- .../ameta/AnalysisConstantReflectionProvider.java | 9 --------- 3 files changed, 1 insertion(+), 25 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java index 08c56d3f4e6e..d9d9392a240f 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java @@ -117,7 +117,6 @@ public HeapSnapshotVerifier(BigBang bb, ImageHeap imageHeap, ImageHeapScanner sc } public boolean verifyHeapSnapshot(CompletionExecutor executor) throws InterruptedException { - scanner.disable(); foundMismatch = false; scannedObjects.reset(); executor.start(); @@ -126,7 +125,6 @@ public boolean verifyHeapSnapshot(CompletionExecutor executor) throws Interrupte objectScanner.scanBootImageHeapRoots(); executor.complete(); executor.shutdown(); - scanner.enable(); return foundMismatch; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index 59479500a72e..8d41c316b9ed 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -94,7 +94,6 @@ public static class Options { protected final SnippetReflectionProvider hostedSnippetReflection; protected ObjectScanningObserver scanningObserver; - protected boolean isEnabled; private final boolean enableManualRescan; public ImageHeapScanner(ImageHeap heap, AnalysisMetaAccess aMetaAccess, SnippetReflectionProvider aSnippetReflection, @@ -108,28 +107,16 @@ public ImageHeapScanner(ImageHeap heap, AnalysisMetaAccess aMetaAccess, SnippetR scanningObserver = aScanningObserver; hostedConstantReflection = GraalAccess.getOriginalProviders().getConstantReflection(); hostedSnippetReflection = GraalAccess.getOriginalProviders().getSnippetReflection(); - isEnabled = true; enableManualRescan = Options.EnableManualRescan.getValue(hostVM.options()); } - public void disable() { - isEnabled = false; - } - - public void enable() { - isEnabled = true; - } - - public boolean isEnabled() { - return isEnabled; - } - public void scanEmbeddedRoot(JavaConstant root, BytecodePosition position) { AnalysisType type = metaAccess.lookupJavaType(root); type.registerAsReachable(); getOrCreateConstantReachableTask(root, new EmbeddedRootScan(position, root)).ensureDone(); } + @SuppressWarnings("unused") public void scanFieldValue(AnalysisField field, JavaConstant receiver) { assert field.isReachable() && isValueAvailable(field); if (field.isStatic()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java index c377fd42f787..94392e153140 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java @@ -33,7 +33,6 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.word.WordBase; -import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; @@ -107,14 +106,6 @@ public JavaConstant readValue(MetaAccessProvider suppliedMetaAccess, AnalysisFie field.markReachable(); } - if (!BuildPhaseProvider.isAnalysisFinished() && field.getJavaKind() == JavaKind.Object && field.isRead()) { - /* Make sure the field value is scanned. */ - ImageHeapScanner scanner = universe.getHeapScanner(); - if (scanner.isEnabled()) { - scanner.scanFieldValue(field, receiver); - } - } - return result; } From a76a0b4766ff299af3eb6f1fd7cf944f5fe95923 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Sat, 11 Dec 2021 21:32:36 -0800 Subject: [PATCH 16/23] Refactor rescaning API. --- substratevm/mx.substratevm/suite.py | 1 + .../graal/pointsto/heap/ImageHeapScanner.java | 8 +--- .../oracle/svm/graal/hosted/GraalFeature.java | 2 +- .../svm/graal/hosted/GraalObjectReplacer.java | 37 ++++++++++++++----- .../com/oracle/svm/hosted/FeatureImpl.java | 17 ++------- .../com/oracle/svm/hosted/LoggingFeature.java | 7 +++- .../svm/hosted/SecurityServicesFeature.java | 37 +++++++++++++++---- .../analysis/DynamicHubInitializer.java | 32 ++++++++++++---- .../analysis/heap/SVMImageHeapScanner.java | 13 +++++-- .../annotation/AnnotationTypeFeature.java | 11 +++++- .../ClassInitializationFeature.java | 7 +++- .../jdk/localization/LocalizationFeature.java | 35 ++++++++++++------ .../svm/hosted/thread/VMThreadMTFeature.java | 8 +++- .../svm/hosted/thread/VMThreadSTFeature.java | 6 ++- .../src/com/oracle/svm/jfr/JfrFeature.java | 2 +- .../svm/jni/access/JNIAccessibleClass.java | 5 ++- .../svm/jni/access/JNINativeLinkage.java | 8 +++- .../reflect/hosted/ReflectionDataBuilder.java | 17 +++++++-- .../svm/reflect/hosted/ReflectionFeature.java | 1 + .../proxy/hosted/DynamicProxyFeature.java | 8 +++- .../svm/truffle/TruffleBaseFeature.java | 18 ++++++--- .../oracle/svm/truffle/TruffleFeature.java | 6 --- 22 files changed, 201 insertions(+), 85 deletions(-) diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index d5fb0ea783c5..52b93813b5d1 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -1157,6 +1157,7 @@ "sun.reflect.generics.tree", "sun.reflect.generics.scope", "sun.util.calendar", + "sun.util.locale", "sun.security.jca", "sun.security.util", "sun.security.provider", diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index 8d41c316b9ed..a0ea32288d03 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -455,10 +455,6 @@ public Object rescanRoot(Field reflectionField) { return null; } - public void rescanField(Object receiver, Class declaringClass, String fieldName) { - rescanField(receiver, ReflectionUtil.lookupField(declaringClass, fieldName)); - } - public void rescanField(Object receiver, Field reflectionField) { if (!enableManualRescan) { return; @@ -543,7 +539,7 @@ protected void rescanEconomicMap(EconomicMap object) { void doScan(JavaConstant constant) { if (constant.getJavaKind() == JavaKind.Object && constant.isNonNull()) { - toImageHeapObject(constant, OtherReason.SCAN); + getOrCreateConstantReachableTask(constant, OtherReason.SCAN); } } @@ -552,7 +548,7 @@ public void scanHub(AnalysisType type) { universe.onTypeScanned(type); metaAccess.lookupJavaType(java.lang.Class.class).registerAsReachable(); /* We scan the original class here, the scanner does the replacement to DynamicHub. */ - toImageHeapObject(asConstant(type.getJavaClass()), ObjectScanner.OtherReason.HUB); + getOrCreateConstantReachableTask(asConstant(type.getJavaClass()), ObjectScanner.OtherReason.HUB); } protected AnalysisType analysisType(Object constant) { diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java index ed1eec15d0a8..0ae44414b925 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java @@ -338,7 +338,7 @@ public void duringSetup(DuringSetupAccess c) { ImageSingletons.add(RuntimeGraalSetup.class, new SubstrateRuntimeGraalSetup()); } GraalProviderObjectReplacements providerReplacements = ImageSingletons.lookup(RuntimeGraalSetup.class).getProviderObjectReplacements(aMetaAccess); - objectReplacer = new GraalObjectReplacer(config.getUniverse(), aMetaAccess, providerReplacements); + objectReplacer = new GraalObjectReplacer(config, config.getUniverse(), aMetaAccess, providerReplacements); config.registerObjectReplacer(objectReplacer); config.registerClassReachabilityListener(GraalSupport::registerPhaseStatistics); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java index 012949e417b2..66ec71027ece 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalObjectReplacer.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.graal.hosted; +import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; @@ -62,6 +63,7 @@ import com.oracle.svm.graal.meta.SubstrateMethod; import com.oracle.svm.graal.meta.SubstrateSignature; import com.oracle.svm.graal.meta.SubstrateType; +import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.AnalysisConstantFieldProvider; import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; @@ -106,11 +108,26 @@ public class GraalObjectReplacer implements Function { private final HostedStringDeduplication stringTable; - public GraalObjectReplacer(AnalysisUniverse aUniverse, AnalysisMetaAccess aMetaAccess, GraalProviderObjectReplacements providerReplacements) { + private static Field substrateFieldAnnotationsEncodingField; + private static Field substrateFieldTypeField; + private static Field substrateFieldDeclaringClassField; + private static Field dynamicHubMetaTypeField; + private static Field substrateTypeRawAllInstanceFields; + private static Field substrateMethodImplementationsFields; + private static Field substrateMethodAnnotationsEncodingFields; + + public GraalObjectReplacer(FeatureImpl.DuringSetupAccessImpl config, AnalysisUniverse aUniverse, AnalysisMetaAccess aMetaAccess, GraalProviderObjectReplacements providerReplacements) { this.aUniverse = aUniverse; this.aMetaAccess = aMetaAccess; this.providerReplacements = providerReplacements; this.stringTable = HostedStringDeduplication.singleton(); + substrateFieldAnnotationsEncodingField = config.findField(SubstrateField.class, "annotationsEncoding"); + substrateFieldTypeField = config.findField(SubstrateField.class, "type"); + substrateFieldDeclaringClassField = config.findField(SubstrateField.class, "declaringClass"); + dynamicHubMetaTypeField = config.findField(DynamicHub.class, "metaType"); + substrateTypeRawAllInstanceFields = config.findField(SubstrateType.class, "rawAllInstanceFields"); + substrateMethodImplementationsFields = config.findField(SubstrateMethod.class, "implementations"); + substrateMethodAnnotationsEncodingFields = config.findField(SubstrateMethod.class, "annotationsEncoding"); } public void setGraalRuntime(SubstrateGraalRuntime sGraalRuntime) { @@ -223,7 +240,7 @@ public synchronized SubstrateMethod createMethod(ResolvedJavaMethod original) { */ Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(aMetaAccess, aMethod.getAnnotations(), aMethod.getDeclaredAnnotations(), null); if (sMethod.setAnnotationsEncoding(annotationsEncoding)) { - aUniverse.getHeapScanner().rescanField(sMethod, SubstrateField.class, "annotationsEncoding"); + aUniverse.getHeapScanner().rescanField(sMethod, substrateFieldAnnotationsEncodingField); // aUniverse.getHeapScanner().rescanObject(sMethod); } } @@ -254,8 +271,8 @@ public synchronized SubstrateField createField(ResolvedJavaField original) { fields.put(aField, sField); sField.setLinks(createType(aField.getType()), createType(aField.getDeclaringClass())); - aUniverse.getHeapScanner().rescanField(sField, SubstrateField.class, "type"); - aUniverse.getHeapScanner().rescanField(sField, SubstrateField.class, "declaringClass"); + aUniverse.getHeapScanner().rescanField(sField, substrateFieldTypeField); + aUniverse.getHeapScanner().rescanField(sField, substrateFieldDeclaringClassField); /* * Annotations are updated in every analysis iteration, but this is a starting point. It @@ -263,7 +280,7 @@ public synchronized SubstrateField createField(ResolvedJavaField original) { */ Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(aMetaAccess, aField.getAnnotations(), aField.getDeclaredAnnotations(), null); if (sField.setAnnotationsEncoding(annotationsEncoding)) { - aUniverse.getHeapScanner().rescanField(sField, SubstrateField.class, "annotationsEncoding"); + aUniverse.getHeapScanner().rescanField(sField, substrateFieldAnnotationsEncodingField); // aUniverse.getHeapScanner().rescanObject(sField); } } @@ -311,10 +328,10 @@ public synchronized SubstrateType createType(JavaType original) { sType = new SubstrateType(aType.getJavaKind(), hub); types.put(aType, sType); hub.setMetaType(sType); - aUniverse.getHeapScanner().rescanField(hub, DynamicHub.class, "metaType"); + aUniverse.getHeapScanner().rescanField(hub, dynamicHubMetaTypeField); sType.setRawAllInstanceFields(createAllInstanceFields(aType)); - aUniverse.getHeapScanner().rescanField(sType, SubstrateType.class, "rawAllInstanceFields"); + aUniverse.getHeapScanner().rescanField(sType, substrateTypeRawAllInstanceFields); createType(aType.getSuperclass()); createType(aType.getComponentType()); for (AnalysisType aInterface : aType.getInterfaces()) { @@ -391,7 +408,7 @@ public boolean updateDataDuringAnalysis(AnalysisMetaAccess metaAccess) { implementations[idx++] = sImpl; } if (sMethod.setImplementations(implementations)) { - aUniverse.getHeapScanner().rescanField(sMethod, SubstrateMethod.class, "implementations"); + aUniverse.getHeapScanner().rescanField(sMethod, substrateMethodImplementationsFields); result = true; } } @@ -401,7 +418,7 @@ public boolean updateDataDuringAnalysis(AnalysisMetaAccess metaAccess) { AnalysisMethod sMethod = entry.getKey(); Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(metaAccess, sMethod.getAnnotations(), sMethod.getDeclaredAnnotations(), method.getAnnotationsEncoding()); if (method.setAnnotationsEncoding(annotationsEncoding)) { - aUniverse.getHeapScanner().rescanField(method, SubstrateMethod.class, "annotationsEncoding"); + aUniverse.getHeapScanner().rescanField(method, substrateMethodAnnotationsEncodingFields); result = true; } } @@ -410,7 +427,7 @@ public boolean updateDataDuringAnalysis(AnalysisMetaAccess metaAccess) { AnalysisField sField = entry.getKey(); Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(metaAccess, sField.getAnnotations(), sField.getDeclaredAnnotations(), field.getAnnotationsEncoding()); if (field.setAnnotationsEncoding(annotationsEncoding)) { - aUniverse.getHeapScanner().rescanField(field, SubstrateField.class, "annotationsEncoding"); + aUniverse.getHeapScanner().rescanField(field, substrateMethodAnnotationsEncodingFields); result = true; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index cec1d697db91..252337a29891 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -255,26 +255,17 @@ public void rescanField(Object receiver, Field field) { getUniverse().getHeapScanner().rescanField(receiver, field); } - public void rescanField(Object receiver, Class declaringClass, String fieldName) { - rescanField(receiver, ReflectionUtil.lookupField(declaringClass, fieldName)); - } - - public void rescanField(Object receiver, String className, String fieldName) { - rescanField(receiver, getImageClassLoader().findClassOrFail(className), fieldName); - } - public Object rescanRoot(Field field) { return getUniverse().getHeapScanner().rescanRoot(field); } - public Object rescanRoot(Class declaringClass, String fieldName) { - return rescanRoot(ReflectionUtil.lookupField(declaringClass, fieldName)); + public Field findField(String declaringClassName, String fieldName) { + return findField(imageClassLoader.findClassOrFail(declaringClassName), fieldName); } - public Object rescanRoot(String declaringClassName, String fieldName) { - return rescanRoot(getImageClassLoader().findClassOrFail(declaringClassName), fieldName); + public Field findField(Class declaringClass, String fieldName) { + return ReflectionUtil.lookupField(declaringClass, fieldName); } - } public static class DuringSetupAccessImpl extends AnalysisAccessBase implements Feature.DuringSetupAccess { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LoggingFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LoggingFeature.java index 5a1f83ae4fda..cb47747277c0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LoggingFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/LoggingFeature.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.hosted; +import java.lang.reflect.Field; import java.util.logging.LogManager; import org.graalvm.compiler.options.Option; @@ -35,6 +36,7 @@ import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; @AutomaticFeature public class LoggingFeature implements Feature { @@ -51,6 +53,8 @@ public static class Options { private boolean reflectionConfigured = false; + private Field loggersField; + @Override public boolean isInConfiguration(IsInConfigurationAccess access) { return LoggingFeature.Options.EnableLoggingFeature.getValue(); @@ -60,13 +64,14 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { public void duringSetup(DuringSetupAccess access) { /* Ensure that the log manager is initialized and the initial configuration is read. */ LogManager.getLogManager(); + loggersField = ((DuringSetupAccessImpl) access).findField("sun.util.logging.PlatformLogger", "loggers"); } @Override public void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; - access.rescanRoot("sun.util.logging.PlatformLogger", "loggers"); + access.rescanRoot(loggersField); if (!reflectionConfigured && access.getMetaAccess().optionalLookupJavaType(java.util.logging.Logger.class).isPresent()) { registerForReflection(java.util.logging.ConsoleHandler.class); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java index 2d9e814b5f4d..b3035d4a533d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java @@ -196,6 +196,14 @@ public static class Options { // private boolean shouldFilterProviders = true; private boolean shouldFilterProviders = true; + private Field verificationResultsField; + private Field providerListField; + private Field oidTableField; + private Field oidMapField; + private Field classCacheField; + private Field constructorCacheField; + private Field classRefField; + @Override public void afterRegistration(AfterRegistrationAccess a) { ModuleSupport.exportAndOpenPackageToClass("java.base", "sun.security.x509", false, getClass()); @@ -225,6 +233,19 @@ public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; addManuallyConfiguredUsedProviders(a); + verificationResultsField = access.findField("javax.crypto.JceSecurity", "verificationResults"); + providerListField = access.findField("sun.security.jca.Providers", "providerList"); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + oidTableField = access.findField("sun.security.util.ObjectIdentifier", "oidTable"); + } + oidMapField = access.findField(OIDMap.class, "oidMap"); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + classCacheField = access.findField(Service.class, "classCache"); + constructorCacheField = access.findField(Service.class, "constructorCache"); + } else { + classRefField = access.findField(Service.class, "classRef"); + } + RuntimeClassInitializationSupport rci = ImageSingletons.lookup(RuntimeClassInitializationSupport.class); /* * The SecureRandom implementations open the /dev/random and /dev/urandom files which are @@ -744,7 +765,7 @@ private static void registerJks(ImageClassLoader loader) { /** * Register the x509 certificate extension classes for reflection. */ - private static void registerX509Extensions(DuringAnalysisAccess a) { + private void registerX509Extensions(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; /* * The OIDInfo class which represents the values in the map is not visible. Get the list of @@ -762,25 +783,25 @@ private static void registerX509Extensions(DuringAnalysisAccess a) { throw VMError.shouldNotReachHere(e); } } - access.rescanRoot(OIDMap.class, "oidMap"); + access.rescanRoot(oidMapField); } @Override public void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; - access.rescanRoot("javax.crypto.JceSecurity", "verificationResults"); - access.rescanRoot("sun.security.jca.Providers", "providerList"); + access.rescanRoot(verificationResultsField); + access.rescanRoot(providerListField); if (JavaVersionUtil.JAVA_SPEC >= 17) { - access.rescanRoot("sun.security.util.ObjectIdentifier", "oidTable"); + access.rescanRoot(oidTableField); } if (filteredProviderList != null) { for (Provider provider : filteredProviderList.providers()) { for (Service service : provider.getServices()) { if (JavaVersionUtil.JAVA_SPEC >= 17) { - access.rescanField(service, Service.class, "classCache"); - access.rescanField(service, Service.class, "constructorCache"); + access.rescanField(service, classCacheField); + access.rescanField(service, constructorCacheField); } else { - access.rescanField(service, Service.class, "classRef"); + access.rescanField(service, classRefField); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java index 76ba418513e6..52db9e14b917 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java @@ -26,6 +26,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Field; import java.lang.reflect.MalformedParameterizedTypeException; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -45,6 +46,7 @@ import com.oracle.svm.core.hub.GenericInfo; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaKind; @@ -61,6 +63,14 @@ public class DynamicHubInitializer { private final Map annotatedInterfacesMap; private final Map interfacesEncodings; + private final Field dynamicHubArrayHubField; + private final Field dynamicHubEnclosingClassField; + private final Field dynamicHubInterfacesEncodingField; + private final Field dynamicHubAnnotationsEncodingField; + private final Field dynamicHubAnnotationsEnumConstantsReferenceField; + private final Field dynamicHubAnnotatedSuperInfoField; + private final Field dynamicHubGenericInfoField; + public DynamicHubInitializer(AnalysisMetaAccess metaAccess, UnsupportedFeatures unsupportedFeatures, ConstantReflectionProvider constantReflection) { this.metaAccess = metaAccess; this.hostVM = (SVMHost) metaAccess.getUniverse().hostVM(); @@ -70,6 +80,14 @@ public DynamicHubInitializer(AnalysisMetaAccess metaAccess, UnsupportedFeatures this.genericInterfacesMap = new ConcurrentHashMap<>(); this.annotatedInterfacesMap = new ConcurrentHashMap<>(); this.interfacesEncodings = new ConcurrentHashMap<>(); + + dynamicHubArrayHubField = ReflectionUtil.lookupField(DynamicHub.class, "arrayHub"); + dynamicHubEnclosingClassField = ReflectionUtil.lookupField(DynamicHub.class, "enclosingClass"); + dynamicHubInterfacesEncodingField = ReflectionUtil.lookupField(DynamicHub.class, "interfacesEncoding"); + dynamicHubAnnotationsEncodingField = ReflectionUtil.lookupField(DynamicHub.class, "annotationsEncoding"); + dynamicHubAnnotationsEnumConstantsReferenceField = ReflectionUtil.lookupField(DynamicHub.class, "enumConstantsReference"); + dynamicHubAnnotatedSuperInfoField = ReflectionUtil.lookupField(DynamicHub.class, "annotatedSuperInfo"); + dynamicHubGenericInfoField = ReflectionUtil.lookupField(DynamicHub.class, "genericInfo"); } public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) { @@ -85,14 +103,14 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) if (type.getJavaKind() == JavaKind.Object) { if (type.isArray()) { hub.getComponentHub().setArrayHub(hub); - heapScanner.rescanField(hub.getComponentHub(), DynamicHub.class, "arrayHub"); + heapScanner.rescanField(hub.getComponentHub(), dynamicHubArrayHubField); } try { AnalysisType enclosingType = type.getEnclosingType(); if (enclosingType != null) { hub.setEnclosingClass(hostVM.dynamicHub(enclosingType)); - heapScanner.rescanField(hub, DynamicHub.class, "enclosingClass"); + heapScanner.rescanField(hub, dynamicHubEnclosingClassField); } } catch (UnsupportedFeatureException ex) { unsupportedFeatures.addMessage(type.toJavaName(true), null, ex.getMessage(), null, ex); @@ -100,7 +118,7 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) if (hub.getInterfacesEncoding() == null) { fillInterfaces(type, hub); - heapScanner.rescanField(hub, DynamicHub.class, "interfacesEncoding"); + heapScanner.rescanField(hub, dynamicHubInterfacesEncodingField); } /* @@ -118,7 +136,7 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) Annotation[] declared = type.getWrappedWithoutResolve().getDeclaredAnnotations(); Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(metaAccess, annotations, declared, hub.getAnnotationsEncoding()); if (hub.setAnnotationsEncoding(annotationsEncoding)) { - heapScanner.rescanField(hub, DynamicHub.class, "annotationsEncoding"); + heapScanner.rescanField(hub, dynamicHubAnnotationsEncodingField); } } catch (ArrayStoreException e) { /* If we hit JDK-7183985 just encode the exception. */ @@ -171,7 +189,7 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) } hub.initEnumConstants(enumConstants); } - heapScanner.rescanField(hub, DynamicHub.class, "enumConstantsReference"); + heapScanner.rescanField(hub, dynamicHubAnnotationsEnumConstantsReferenceField); } } } @@ -297,7 +315,7 @@ private void fillGenericInfo(ImageHeapScanner heapScanner, AnalysisType type, Dy genericSuperClass = null; } hub.setGenericInfo(GenericInfo.factory(typeParameters, cachedGenericInterfaces, genericSuperClass)); - heapScanner.rescanField(hub, DynamicHub.class, "genericInfo"); + heapScanner.rescanField(hub, dynamicHubGenericInfoField); } private void fillAnnotatedSuperInfo(ImageHeapScanner heapScanner, AnalysisType type, DynamicHub hub) { @@ -333,7 +351,7 @@ private void fillAnnotatedSuperInfo(ImageHeapScanner heapScanner, AnalysisType t AnnotatedType[] cachedAnnotatedInterfaces = annotatedInterfacesMap.computeIfAbsent( new AnnotatedInterfacesEncodingKey(annotatedInterfaces), k -> annotatedInterfaces); hub.setAnnotatedSuperInfo(AnnotatedSuperInfo.factory(annotatedSuperclass, cachedAnnotatedInterfaces)); - heapScanner.rescanField(hub, DynamicHub.class, "annotatedSuperInfo"); + heapScanner.rescanField(hub, dynamicHubAnnotatedSuperInfoField); } private boolean isTypeAllowed(Type t) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java index 14a246fe3308..b480ce7f63fa 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/heap/SVMImageHeapScanner.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.hosted.analysis.heap; +import java.lang.reflect.Field; + import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -44,6 +46,7 @@ import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.ameta.AnalysisConstantReflectionProvider; import com.oracle.svm.hosted.meta.HostedMetaAccess; +import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; @@ -53,12 +56,16 @@ public class SVMImageHeapScanner extends ImageHeapScanner { private final ImageClassLoader loader; protected HostedMetaAccess hostedMetaAccess; private final Class economicMapImpl; + private final Field economicMapImplEntriesField; + private final Field economicMapImplHashArrayField; public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { super(imageHeap, metaAccess, snippetReflection, aConstantReflection, aScanningObserver); this.loader = loader; - this.economicMapImpl = this.loader.findClassOrFail("org.graalvm.collections.EconomicMapImpl"); + economicMapImpl = getClass("org.graalvm.collections.EconomicMapImpl"); + economicMapImplEntriesField = ReflectionUtil.lookupField(economicMapImpl, "entries"); + economicMapImplHashArrayField = ReflectionUtil.lookupField(economicMapImpl, "hashArray"); } public void setHostedMetaAccess(HostedMetaAccess hostedMetaAccess) { @@ -144,8 +151,8 @@ protected void rescanEconomicMap(EconomicMap map) { super.rescanEconomicMap(map); /* Make sure any EconomicMapImpl$CollisionLink objects are scanned. */ if (map.getClass() == economicMapImpl) { - rescanField(map, economicMapImpl, "entries"); - rescanField(map, economicMapImpl, "hashArray"); + rescanField(map, economicMapImplEntriesField); + rescanField(map, economicMapImplHashArrayField); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java index 21375fd1b6a8..e7d689e34876 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java @@ -27,6 +27,7 @@ import java.lang.annotation.Annotation; import java.lang.annotation.Repeatable; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; import java.util.Objects; import java.util.stream.Stream; @@ -40,6 +41,7 @@ import com.oracle.svm.core.hub.AnnotationTypeSupport; import com.oracle.svm.hosted.FeatureImpl.AfterRegistrationAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; @AutomaticFeature public class AnnotationTypeFeature implements Feature { @@ -47,6 +49,8 @@ public class AnnotationTypeFeature implements Feature { private EconomicSet repeatableAnnotationClasses = EconomicSet.create(); private EconomicSet visitedElements = EconomicSet.create(); + private Field annotationTypeMapField; + @Override public void afterRegistration(AfterRegistrationAccess access) { ImageSingletons.add(AnnotationTypeSupport.class, new AnnotationTypeSupport()); @@ -57,6 +61,11 @@ public void afterRegistration(AfterRegistrationAccess access) { .forEach(repeatableAnnotationClasses::add); } + @Override + public void duringSetup(DuringSetupAccess access) { + annotationTypeMapField = ((DuringSetupAccessImpl) access).findField(AnnotationTypeSupport.class, "annotationTypeMap"); + } + @Override public void duringAnalysis(DuringAnalysisAccess access) { DuringAnalysisAccessImpl accessImpl = (DuringAnalysisAccessImpl) access; @@ -88,7 +97,7 @@ private void reportAnnotation(DuringAnalysisAccess a, AnnotatedElement element) if (repeatableAnnotationClasses.contains(annotationClass)) { AnnotationTypeSupport annotationTypeSupport = ImageSingletons.lookup(AnnotationTypeSupport.class); if (annotationTypeSupport.createInstance(annotationClass)) { - access.rescanField(annotationTypeSupport, AnnotationTypeSupport.class, "annotationTypeMap"); + access.rescanField(annotationTypeSupport, annotationTypeMapField); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java index a408d0f518d0..a86cf54ce9dd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java @@ -29,6 +29,7 @@ import static com.oracle.svm.hosted.classinitialization.InitKind.RUN_TIME; import java.io.PrintWriter; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -75,6 +76,7 @@ public class ClassInitializationFeature implements GraalFeature { private ClassInitializationSupport classInitializationSupport; private AnalysisUniverse universe; private AnalysisMetaAccess metaAccess; + private Field dynamicHubClassInitializationInfoField; public static void processClassInitializationOptions(ClassInitializationSupport initializationSupport) { initializeNativeImagePackagesAtBuildTime(initializationSupport); @@ -115,6 +117,7 @@ public void duringSetup(DuringSetupAccess a) { access.registerObjectReplacer(this::checkImageHeapInstance); universe = ((FeatureImpl.DuringSetupAccessImpl) a).getBigBang().getUniverse(); metaAccess = ((FeatureImpl.DuringSetupAccessImpl) a).getBigBang().getMetaAccess(); + dynamicHubClassInitializationInfoField = access.findField(DynamicHub.class, "classInitializationInfo"); } private Object checkImageHeapInstance(Object obj) { @@ -301,7 +304,7 @@ private Set> initializeSafeDelayedClasses(TypeInitializerGraph initGrap : ClassInitializationInfo.INITIALIZED_INFO_SINGLETON; DynamicHub hub = ((SVMHost) universe.hostVM()).dynamicHub(type); hub.setClassInitializationInfo(initializationInfo); - universe.getHeapScanner().rescanField(hub, DynamicHub.class, "classInitializationInfo"); + universe.getHeapScanner().rescanField(hub, dynamicHubClassInitializationInfoField); } } }); @@ -328,7 +331,7 @@ private void buildClassInitializationInfo(FeatureImpl.DuringAnalysisAccessImpl a hub.setClassInitializationInfo(info); // TODO rescanning the hub here should not be necessary universe.getHeapScanner().scanHub(type); - universe.getHeapScanner().rescanField(hub, DynamicHub.class, "classInitializationInfo"); + universe.getHeapScanner().rescanField(hub, dynamicHubClassInitializationInfoField); } private static ClassInitializationInfo buildRuntimeInitializationInfo(FeatureImpl.DuringAnalysisAccessImpl access, AnalysisType type) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index a0f74091648c..8d2d021cf859 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -83,6 +83,7 @@ import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.util.ReflectionUtil; @@ -147,6 +148,11 @@ public abstract class LocalizationFeature implements Feature { private Function> findClassByName; + private Field baseLocaleCacheField; + private Field localeCacheField; + private Field candidatesCacheField; + private Field localeObjectCacheMapField; + public static class Options { @Option(help = "Comma separated list of bundles to be included into the image.", type = OptionType.User)// public static final HostedOptionKey IncludeResourceBundles = new HostedOptionKey<>(new LocatableMultiOptionValue.Strings()); @@ -263,10 +269,20 @@ public void afterRegistration(AfterRegistrationAccess access) { } @Override - public void duringSetup(DuringSetupAccess access) { + public void duringSetup(DuringSetupAccess a) { + DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; if (optimizedMode) { access.registerObjectReplacer(this::eagerlyInitializeBundles); } + if (JavaVersionUtil.JAVA_SPEC >= 17) { + baseLocaleCacheField = access.findField("sun.util.locale.BaseLocale$Cache", "CACHE"); + localeCacheField = access.findField("java.util.Locale$Cache", "LOCALECACHE"); + } else { + baseLocaleCacheField = access.findField("sun.util.locale.BaseLocale", "CACHE"); + localeCacheField = access.findField("java.util.Locale", "LOCALECACHE"); + } + candidatesCacheField = access.findField("java.util.ResourceBundle$Control", "CANDIDATES_CACHE"); + localeObjectCacheMapField = access.findField(LocaleObjectCache.class, "map"); } /** @@ -315,20 +331,15 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { @Override public void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; - if (JavaVersionUtil.JAVA_SPEC >= 17) { - scanLocaleCache(access, "sun.util.locale.BaseLocale$Cache", "CACHE"); - scanLocaleCache(access, "java.util.Locale$Cache", "LOCALECACHE"); - } else { - scanLocaleCache(access, "sun.util.locale.BaseLocale", "CACHE"); - scanLocaleCache(access, "java.util.Locale", "LOCALECACHE"); - } - scanLocaleCache(access, "java.util.ResourceBundle$Control", "CANDIDATES_CACHE"); + scanLocaleCache(access, baseLocaleCacheField); + scanLocaleCache(access, localeCacheField); + scanLocaleCache(access, candidatesCacheField); } - private static void scanLocaleCache(DuringAnalysisAccessImpl access, String localeClassName, String cacheFieldName) { - Object localeCache = access.rescanRoot(localeClassName, cacheFieldName); + private void scanLocaleCache(DuringAnalysisAccessImpl access, Field cacheFieldField) { + Object localeCache = access.rescanRoot(cacheFieldField); if (localeCache != null) { - access.rescanField(localeCache, LocaleObjectCache.class, "map"); + access.rescanField(localeCache, localeObjectCacheMapField); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadMTFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadMTFeature.java index 425fc112a312..ddc1121c1f38 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadMTFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadMTFeature.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.hosted.thread; +import java.lang.reflect.Field; import java.util.List; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -61,6 +62,7 @@ import com.oracle.svm.core.threadlocal.VMThreadLocalMTSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import jdk.vm.ci.code.MemoryBarriers; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -75,6 +77,8 @@ public class VMThreadMTFeature implements GraalFeature { private final VMThreadLocalCollector threadLocalCollector = new VMThreadLocalCollector(); private final VMThreadLocalMTSupport threadLocalSupport = new VMThreadLocalMTSupport(); + private Field infosField; + public int getVMThreadSize() { assert threadLocalSupport.vmThreadSize != -1 : "not yet initialized"; return threadLocalSupport.vmThreadSize; @@ -89,6 +93,8 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { public void duringSetup(DuringSetupAccess config) { ImageSingletons.add(VMThreadLocalMTSupport.class, threadLocalSupport); config.registerObjectReplacer(threadLocalCollector); + + infosField = ((DuringSetupAccessImpl) config).findField(VMThreadLocalInfos.class, "infos"); } /** @@ -235,7 +241,7 @@ public void duringAnalysis(DuringAnalysisAccess a) { if (VMThreadLocalInfos.setInfos(threadLocalCollector.threadLocals.values())) { a.requireAnalysisIteration(); DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; - access.rescanField(ImageSingletons.lookup(VMThreadLocalInfos.class), VMThreadLocalInfos.class, "infos"); + access.rescanField(ImageSingletons.lookup(VMThreadLocalInfos.class), infosField); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadSTFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadSTFeature.java index dd0be7c34406..4544f3e3eeb9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadSTFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadSTFeature.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.hosted.thread; +import java.lang.reflect.Field; import java.util.List; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -56,6 +57,7 @@ import com.oracle.svm.core.threadlocal.VMThreadLocalInfo; import com.oracle.svm.core.threadlocal.VMThreadLocalInfos; import com.oracle.svm.core.threadlocal.VMThreadLocalSTSupport; +import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import jdk.vm.ci.meta.JavaKind; @@ -69,6 +71,7 @@ public class VMThreadSTFeature implements GraalFeature { private final VMThreadLocalCollector threadLocalCollector = new VMThreadLocalCollector(); + private Field infosField; @Override public boolean isInConfiguration(IsInConfigurationAccess access) { @@ -79,6 +82,7 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { public void duringSetup(DuringSetupAccess config) { ImageSingletons.add(VMThreadLocalSTSupport.class, new VMThreadLocalSTSupport()); config.registerObjectReplacer(threadLocalCollector); + infosField = ((FeatureImpl.DuringSetupAccessImpl) config).findField(VMThreadLocalInfos.class, "infos"); } /** @@ -214,7 +218,7 @@ public void duringAnalysis(DuringAnalysisAccess a) { if (VMThreadLocalInfos.setInfos(threadLocalCollector.threadLocals.values())) { a.requireAnalysisIteration(); DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; - access.rescanField(ImageSingletons.lookup(VMThreadLocalInfos.class), VMThreadLocalInfos.class, "infos"); + access.rescanField(ImageSingletons.lookup(VMThreadLocalInfos.class), infosField); } } diff --git a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java index 807769f10948..e2be25ec57f8 100644 --- a/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java +++ b/substratevm/src/com.oracle.svm.jfr/src/com/oracle/svm/jfr/JfrFeature.java @@ -189,7 +189,7 @@ public void duringAnalysis(DuringAnalysisAccess a) { try { Field f = c.getDeclaredField("eventHandler"); RuntimeReflection.register(f); - access.rescanRoot(c, "eventHandler"); + access.rescanRoot(f); } catch (Exception e) { throw VMError.shouldNotReachHere("Unable to register eventHandler for: " + c.getCanonicalName(), e); } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleClass.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleClass.java index 13b18b1d6c24..4054d775010b 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleClass.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessibleClass.java @@ -34,6 +34,7 @@ import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.MetaUtil; @@ -72,7 +73,7 @@ void addFieldIfAbsent(FeatureImpl.DuringAnalysisAccessImpl access, String name, } if (!fields.containsKey(name)) { fields.put(name, mappingFunction.apply(name)); - access.rescanField(this, JNIAccessibleClass.class, "fields"); + access.rescanField(this, ReflectionUtil.lookupField(JNIAccessibleClass.class, "fields")); } } @@ -83,7 +84,7 @@ void addMethodIfAbsent(FeatureImpl.DuringAnalysisAccessImpl access, JNIAccessibl } if (!methods.containsKey(descriptor)) { methods.put(descriptor, mappingFunction.apply(descriptor)); - access.rescanField(this, JNIAccessibleClass.class, "methods"); + access.rescanField(this, ReflectionUtil.lookupField(JNIAccessibleClass.class, "methods")); } } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNINativeLinkage.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNINativeLinkage.java index 8af345c2574a..e4d1134ebefa 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNINativeLinkage.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNINativeLinkage.java @@ -24,6 +24,10 @@ */ package com.oracle.svm.jni.access; +//Checkstyle: allow reflection + +import java.lang.reflect.Field; + import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.word.PointerBase; import org.graalvm.word.WordFactory; @@ -35,6 +39,7 @@ import com.oracle.svm.core.jdk.NativeLibrarySupport; import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport; import com.oracle.svm.hosted.c.CGlobalDataFeature; +import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.MetaUtil; @@ -91,7 +96,8 @@ public CGlobalDataInfo getBuiltInAddress(BigBang bb) { if (builtInAddress == null) { CGlobalData linkage = CGlobalDataFactory.forSymbol(this.getShortName()); builtInAddress = CGlobalDataFeature.singleton().registerAsAccessedOrGet(linkage); - bb.getUniverse().getHeapScanner().rescanField(this, JNINativeLinkage.class, "builtInAddress"); + Field jniNativeLinkageBuiltInAddressField = ReflectionUtil.lookupField(JNINativeLinkage.class, "builtInAddress"); + bb.getUniverse().getHeapScanner().rescanField(this, jniNativeLinkageBuiltInAddressField); } return builtInAddress; } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java index a7c1b681906d..caf5b5e9ed1a 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java @@ -51,10 +51,10 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import com.oracle.svm.core.jdk.SealedClassSupport; import org.graalvm.compiler.debug.GraalError; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; +import org.graalvm.nativeimage.hosted.Feature.DuringSetupAccess; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.util.GuardedAnnotationAccess; @@ -67,11 +67,13 @@ import com.oracle.svm.core.hub.ClassForNameSupport; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.RecordSupport; +import com.oracle.svm.core.jdk.SealedClassSupport; import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ConditionalConfigurationRegistry; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.FeatureImpl.FeatureAccessImpl; import com.oracle.svm.hosted.annotation.AnnotationSubstitutionType; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; @@ -108,6 +110,9 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl private final ReflectionDataAccessors accessors; + private static Field annotationTypeMapField; + private static Field dynamicHubReflectionDataField; + public ReflectionDataBuilder(FeatureAccessImpl access) { arrayReflectionData = getArrayReflectionData(); accessors = new ReflectionDataAccessors(access); @@ -198,6 +203,12 @@ private void checkNotSealed() { } } + protected void duringSetup(DuringSetupAccess a) { + DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; + annotationTypeMapField = access.findField(AnnotationTypeSupport.class, "annotationTypeMap"); + dynamicHubReflectionDataField = access.findField(DynamicHub.class, "rd"); + } + protected void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; processReachableTypes(access); @@ -446,7 +457,7 @@ private static void registerTypesForAnnotationValue(DuringAnalysisAccessImpl acc */ AnnotationTypeSupport annotationTypeSupport = ImageSingletons.lookup(AnnotationTypeSupport.class); if (annotationTypeSupport.createInstance((Class) type)) { - access.rescanField(annotationTypeSupport, AnnotationTypeSupport.class, "annotationTypeMap"); + access.rescanField(annotationTypeSupport, annotationTypeMapField); } ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(type); @@ -565,7 +576,7 @@ private void processClass(DuringAnalysisAccessImpl access, Class clazz) { buildRecordComponents(clazz, access)); } hub.setReflectionData(reflectionData); - access.rescanField(hub, DynamicHub.class, "rd"); + access.rescanField(hub, dynamicHubReflectionDataField); if (type.isAnnotation()) { /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java index 151d1681cbf1..898948251cc0 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java @@ -188,6 +188,7 @@ public void duringSetup(DuringSetupAccess a) { loader = access.getImageClassLoader(); annotationSubstitutions = ((Inflation) access.getBigBang()).getAnnotationSubstitutionProcessor(); + reflectionData.duringSetup(access); } @Override diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java index 2ce30b961e1a..a26e6e5fb0fd 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java @@ -24,6 +24,9 @@ */ package com.oracle.svm.reflect.proxy.hosted; +// Checkstyle: allow reflection + +import java.lang.reflect.Field; import java.util.Collections; import java.util.List; @@ -48,6 +51,7 @@ @AutomaticFeature public final class DynamicProxyFeature implements Feature { private int loadedConfigurations; + private Field proxyCacheField; @Override public List> getRequiredFeatures() { @@ -68,6 +72,8 @@ public void duringSetup(DuringSetupAccess a) { loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, imageClassLoader, "dynamic proxy", ConfigurationFiles.Options.DynamicProxyConfigurationFiles, ConfigurationFiles.Options.DynamicProxyConfigurationResources, ConfigurationFile.DYNAMIC_PROXY.getFileName()); + + proxyCacheField = access.findField(DynamicProxySupport.class, "proxyCache"); } private static ProxyRegistry proxyRegistry() { @@ -82,7 +88,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { @Override public void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; - access.rescanField(ImageSingletons.lookup(DynamicProxyRegistry.class), DynamicProxySupport.class, "proxyCache"); + access.rescanField(ImageSingletons.lookup(DynamicProxyRegistry.class), proxyCacheField); proxyRegistry().flushConditionalConfiguration(a); } diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index d75a6c4d2448..f5c3777e4044 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -155,6 +155,10 @@ public boolean getAsBoolean() { private boolean profilingEnabled; // Checkstyle: resume + private Field layoutInfoMapField; + private Field layoutMapField; + private Field uncachedDispatchField; + private static void initializeTruffleReflectively(ClassLoader imageClassLoader) { invokeStaticMethod("com.oracle.truffle.api.impl.Accessor", "getTVMCI", Collections.emptyList()); invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "initializeNativeImageState", @@ -291,8 +295,12 @@ public void duringSetup(DuringSetupAccess access) { } GraalProviderObjectReplacements providerReplacements = ImageSingletons.lookup(RuntimeGraalSetup.class) .getProviderObjectReplacements(metaAccess); - graalObjectReplacer = new GraalObjectReplacer(config.getUniverse(), metaAccess, providerReplacements); + graalObjectReplacer = new GraalObjectReplacer(config, config.getUniverse(), metaAccess, providerReplacements); access.registerObjectReplacer(this::replaceNodeFieldAccessor); + + layoutInfoMapField = config.findField("com.oracle.truffle.object.DefaultLayout$LayoutInfo", "LAYOUT_INFO_MAP"); + layoutMapField = config.findField("com.oracle.truffle.object.DefaultLayout", "LAYOUT_MAP"); + uncachedDispatchField = config.findField(LibraryFactory.class, "uncachedDispatch"); } @SuppressWarnings("deprecation") @@ -374,8 +382,8 @@ public void duringAnalysis(DuringAnalysisAccess a) { // "REGISTRY"); // access.rescanRoot("com.oracle.truffle.api.library.LibraryFactory$ResolvedDispatch", // "CACHE"); - access.rescanRoot("com.oracle.truffle.object.DefaultLayout$LayoutInfo", "LAYOUT_INFO_MAP"); - access.rescanRoot("com.oracle.truffle.object.DefaultLayout", "LAYOUT_MAP"); + access.rescanRoot(layoutInfoMapField); + access.rescanRoot(layoutMapField); // access.rescanRoot("com.oracle.truffle.polyglot.PolyglotEngineImpl", "ENGINES"); // access.rescanRoot("com.oracle.truffle.api.TruffleLogger$LoggerCache", "INSTANCE"); @@ -443,13 +451,13 @@ private void registerUnsafeAccess(DuringAnalysisAccess access, * * @see #registerTruffleLibrariesAsInHeap */ - private static void initializeTruffleLibrariesAtBuildTime(DuringAnalysisAccessImpl access, AnalysisType type) { + private void initializeTruffleLibrariesAtBuildTime(DuringAnalysisAccessImpl access, AnalysisType type) { if (type.isAnnotationPresent(GenerateLibrary.class)) { /* Eagerly resolve library type. */ LibraryFactory factory = LibraryFactory.resolve(type.getJavaClass().asSubclass(Library.class)); /* Trigger computation, then rescan uncachedDispatch. */ factory.getUncached(); - access.rescanField(factory, LibraryFactory.class, "uncachedDispatch"); + access.rescanField(factory, uncachedDispatchField); } if (type.getDeclaredAnnotationsByType(ExportLibrary.class).length != 0) { /* Eagerly resolve receiver type. */ diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java index 668aafb067a6..c5d5c8765099 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java @@ -373,12 +373,6 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { } } - @Override - public void duringAnalysis(DuringAnalysisAccess a) { - FeatureImpl.DuringAnalysisAccessImpl access = (FeatureImpl.DuringAnalysisAccessImpl) a; - access.rescanRoot("com.oracle.truffle.polyglot.PolyglotEngineImpl", "ENGINES"); - } - static class TruffleParsingInlineInvokePlugin implements InlineInvokePlugin { private final SVMHost hostVM; From 21d1aa36c860c215c0530c7a83fc34f8c332c670 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Mon, 13 Dec 2021 17:46:59 -0800 Subject: [PATCH 17/23] Update points-to loop. --- .../oracle/graal/pointsto/ObjectScanner.java | 4 ++++ .../graal/pointsto/PointsToAnalysis.java | 22 ++----------------- .../pointsto/heap/HeapSnapshotVerifier.java | 2 +- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index 7577993c908f..3e047c1d8d4e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -71,6 +71,10 @@ public ObjectScanner(BigBang bb, ObjectScanningObserver scanningObserver) { this(bb, null, new ObjectScanner.ReusableSet(), scanningObserver); } + public ObjectScanner(BigBang bb, ReusableSet scannedObjects, ObjectScanningObserver scanningObserver) { + this(bb, null, scannedObjects, scanningObserver); + } + public ObjectScanner(BigBang bb, CompletionExecutor executor, ReusableSet scannedObjects, ObjectScanningObserver scanningObserver) { this.bb = bb; this.scanningObserver = scanningObserver; 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 63d1963477c3..4b79d40b46f8 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 @@ -572,10 +572,6 @@ public ConstantFieldProvider getConstantFieldProvider() { return providers.getConstantFieldProvider(); } - public CompletionExecutor getExecutor() { - return executor; - } - @Override public void checkUserLimitations() { } @@ -636,22 +632,9 @@ public void postTask(final DebugContextRunnable task) { public boolean finish() throws InterruptedException { try (Indent indent = debug.logAndIndent("starting analysis in BigBang.finish")) { universe.setAnalysisDataValid(false); - boolean didSomeWork = false; - - int numTypes; - do { - didSomeWork |= doTypeflow(); - - /* - * Check if the object graph introduces any new types, which leads to new operations - * being posted. - */ - assert executor.getPostedOperations() == 0; - numTypes = universe.getTypes().size(); - } while (executor.getPostedOperations() != 0 || numTypes != universe.getTypes().size()); - + boolean didSomeWork = doTypeflow(); + assert executor.getPostedOperations() == 0; universe.setAnalysisDataValid(true); - return didSomeWork; } } @@ -718,7 +701,6 @@ public void runAnalysis(DebugContext debugContext, Function Date: Mon, 13 Dec 2021 17:05:28 -0800 Subject: [PATCH 18/23] Increase gate time. --- substratevm/ci_includes/gate.hocon | 1 + 1 file changed, 1 insertion(+) diff --git a/substratevm/ci_includes/gate.hocon b/substratevm/ci_includes/gate.hocon index aba5f15fa7e4..00fcd70818c0 100644 --- a/substratevm/ci_includes/gate.hocon +++ b/substratevm/ci_includes/gate.hocon @@ -69,6 +69,7 @@ builds += [ } ${labsjdk-ce-17} ${svm-common-gate} ${svm-common-windows-jdk17} ${svmUnittest} { name: "gate-svm-windows-basics" + timelimit: "1:30:00" run: [ ${svm-cmd-gate} ["build,helloworld,test,svmjunit"] ] From 3865f439f6868ffce8728f0cf414058154d41c12 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 14 Dec 2021 14:24:14 -0800 Subject: [PATCH 19/23] Optimize heap storage. --- .../oracle/graal/pointsto/heap/ImageHeap.java | 45 +++++++------------ .../graal/pointsto/heap/ImageHeapScanner.java | 33 +++++++------- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java index 25667883cf62..1a5356fbf181 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java @@ -35,7 +35,6 @@ import com.oracle.graal.pointsto.util.AnalysisFuture; import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.ResolvedJavaField; public class ImageHeap { @@ -53,8 +52,8 @@ public AnalysisFuture getTask(JavaConstant constant) { return heapObjects.get(constant); } - public ImageHeapObject get(JavaConstant constant) { - return getTask(constant).ensureDone(); + public AnalysisFuture addTask(JavaConstant constant, AnalysisFuture object) { + return heapObjects.putIfAbsent(constant, object); } public Set getObjects(AnalysisType type) { @@ -62,11 +61,8 @@ public Set getObjects(AnalysisType type) { } public boolean add(AnalysisType type, ImageHeapObject heapObj) { - return heapObjects(type).add(heapObj); - } - - private Set heapObjects(AnalysisType type) { - return typesToObjects.computeIfAbsent(type, t -> Collections.newSetFromMap(new ConcurrentHashMap<>())); + Set objectSet = typesToObjects.computeIfAbsent(type, t -> ConcurrentHashMap.newKeySet()); + return objectSet.add(heapObj); } /** @@ -76,27 +72,18 @@ private Set heapObjects(AnalysisType type) { * created only after an object is processed through the object replacers. */ public static class ImageHeapObject { - final AnalysisType type; /** Store the object, already processed by the object transformers. */ - final JavaConstant object; + private final JavaConstant object; - ImageHeapObject(JavaConstant object, AnalysisType type) { + ImageHeapObject(JavaConstant object) { this.object = object; - this.type = type; } public JavaConstant getObject() { return object; } - /* - * Equals and hascode just compare the replaced constant. Thus two HeapObject insantaces are - * considered equal if they snapshot the same constant, even if the instanceFieldValues are - * different, i.e., they were snapshotted at different times during analysis and one of them - * mutated. This is necessary for the removal of the old snapshotted value on forced - * updates. Alternativelly each AnalysisType needs a mapping from JavaConstant to HeapObject - * instead of just a set of HeapObject. - */ + /* Equals and hashCode just compare the replaced constant. */ @Override public boolean equals(Object o) { @@ -117,15 +104,15 @@ public int hashCode() { public static final class ImageHeapInstance extends ImageHeapObject { /** Store original field values of the object. */ - final Map> objectFieldValues; + private final AnalysisFuture[] objectFieldValues; - ImageHeapInstance(JavaConstant object, AnalysisType type, Map> objectFieldValues) { - super(object, type); + ImageHeapInstance(JavaConstant object, AnalysisFuture[] objectFieldValues) { + super(object); this.objectFieldValues = objectFieldValues; } public JavaConstant readFieldValue(AnalysisField field) { - return objectFieldValues.get(field).ensureDone(); + return objectFieldValues[field.getPosition()].ensureDone(); } /** @@ -133,18 +120,18 @@ public JavaConstant readFieldValue(AnalysisField field) { * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, JavaConstant, ObjectScanner.ScanReason)}. */ public AnalysisFuture getFieldTask(AnalysisField field) { - return objectFieldValues.get(field); + return objectFieldValues[field.getPosition()]; } /** * Read the field value, executing the field task in this thread if not already executed. */ public JavaConstant readField(AnalysisField field) { - return objectFieldValues.get(field).ensureDone(); + return objectFieldValues[field.getPosition()].ensureDone(); } public void setFieldTask(AnalysisField field, AnalysisFuture task) { - objectFieldValues.put(field, task); + objectFieldValues[field.getPosition()] = task; } } @@ -152,8 +139,8 @@ static final class ImageHeapArray extends ImageHeapObject { /** Contains the already scanned array elements. */ private final JavaConstant[] arrayElementValues; - ImageHeapArray(JavaConstant object, AnalysisType type, JavaConstant[] arrayElementValues) { - super(object, type); + ImageHeapArray(JavaConstant object, JavaConstant[] arrayElementValues) { + super(object); this.arrayElementValues = arrayElementValues; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index a0ea32288d03..8a633c9b43c4 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -225,7 +225,7 @@ public void scan(JavaConstant javaConstant, ScanReason reason) { protected AnalysisFuture getOrCreateConstantReachableTask(JavaConstant javaConstant, ScanReason reason) { ScanReason nonNullReason = Objects.requireNonNull(reason); - AnalysisFuture existingTask = imageHeap.heapObjects.get(javaConstant); + AnalysisFuture existingTask = imageHeap.getTask(javaConstant); if (existingTask == null) { /* * TODO - this still true? is it ok to seal the heap? @@ -238,7 +238,7 @@ protected AnalysisFuture getOrCreateConstantReachableTask(JavaC throw AnalysisError.shouldNotReachHere("Universe is sealed. New constant reachable: " + javaConstant.toValueString()); } AnalysisFuture newTask = new AnalysisFuture<>(() -> createImageHeapObject(javaConstant, nonNullReason)); - existingTask = imageHeap.heapObjects.putIfAbsent(javaConstant, newTask); + existingTask = imageHeap.addTask(javaConstant, newTask); if (existingTask == null) { /* * Immediately schedule the new task. There is no need to have not-yet-reachable @@ -251,6 +251,7 @@ protected AnalysisFuture getOrCreateConstantReachableTask(JavaC return existingTask; } + @SuppressWarnings({"unchecked", "rawtypes"}) protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReason reason) { assert constant.getJavaKind() == JavaKind.Object && !constant.isNull(); @@ -283,19 +284,19 @@ protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReaso final JavaConstant rawElementValue = constantReflection.readArrayElement(constant, idx); arrayElements[idx] = onArrayElementReachable(constant, type, rawElementValue, idx, arrayReason); } - newImageHeapObject = new ImageHeapArray(constant, type, arrayElements); + newImageHeapObject = new ImageHeapArray(constant, arrayElements); markTypeInstantiated(type); } else { - Map> instanceFieldValues = new HashMap<>(); /* * We need to have the new ImageHeapInstance early so that we can reference it in the * lambda when the field value gets reachable. But it must not be published to any other * thread before all instanceFieldValues are filled in. */ - newImageHeapObject = new ImageHeapInstance(constant, type, instanceFieldValues); /* We are about to query the type's fields, the type must be marked as reachable. */ markTypeInstantiated(type); - for (AnalysisField field : type.getInstanceFields(true)) { + AnalysisField[] instanceFields = type.getInstanceFields(true); + AnalysisFuture[] instanceFieldValues = new AnalysisFuture[instanceFields.length]; + for (AnalysisField field : instanceFields) { ScanReason fieldReason = new FieldScan(field, constant, reason); ValueSupplier rawFieldValue; try { @@ -304,8 +305,9 @@ protected ImageHeapObject createImageHeapObject(JavaConstant constant, ScanReaso /* Ignore missing type errors. */ continue; } - instanceFieldValues.put(field, new AnalysisFuture<>(() -> onFieldValueReachable(field, constant, rawFieldValue, fieldReason))); + instanceFieldValues[field.getPosition()] = new AnalysisFuture<>(() -> onFieldValueReachable(field, constant, rawFieldValue, fieldReason)); } + newImageHeapObject = new ImageHeapInstance(constant, instanceFieldValues); } /* @@ -369,7 +371,7 @@ JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, V /* Notify the points-to analysis of the scan. */ if (objectFuture != null) { /* Add the transformed value to the image heap. */ - scanningObserver.forNonNullFieldValue(receiver, field, objectFuture.ensureDone().object, reason); + scanningObserver.forNonNullFieldValue(receiver, field, objectFuture.ensureDone().getObject(), reason); } } } @@ -389,24 +391,25 @@ protected JavaConstant onArrayElementReachable(JavaConstant array, AnalysisType scanningObserver.forNullArrayElement(array, arrayType, elementIndex, reason); } else { ImageHeapObject element = objectFuture.ensureDone(); - AnalysisType elementType = constantType(element.object); + AnalysisType elementType = constantType(element.getObject()); markTypeInstantiated(elementType); /* Process the array element. */ - scanningObserver.forNonNullArrayElement(array, arrayType, element.object, elementType, elementIndex, reason); - return element.object; + scanningObserver.forNonNullArrayElement(array, arrayType, element.getObject(), elementType, elementIndex, reason); + return element.getObject(); } } return null; } void onObjectReachable(ImageHeapObject imageHeapObject) { - imageHeap.add(imageHeapObject.type, imageHeapObject); + AnalysisType objectType = metaAccess.lookupJavaType(imageHeapObject.getObject()); + imageHeap.add(objectType, imageHeapObject); - markTypeInstantiated(imageHeapObject.type); + markTypeInstantiated(objectType); if (imageHeapObject instanceof ImageHeapInstance) { ImageHeapInstance imageHeapInstance = (ImageHeapInstance) imageHeapObject; - for (AnalysisField field : imageHeapObject.type.getInstanceFields(true)) { + for (AnalysisField field : objectType.getInstanceFields(true)) { if (field.isReachable() && field.isRead() && isValueAvailable(field)) { postTask(imageHeapInstance.getFieldTask(field)); } @@ -489,7 +492,7 @@ protected AnalysisFuture patchStaticField(TypeData typeData, Analy } protected AnalysisFuture patchInstanceField(ImageHeapInstance receiverObject, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, receiverObject.object, fieldValue, reason)); + AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, receiverObject.getObject(), fieldValue, reason)); receiverObject.setFieldTask(field, task); return task; } From c17ebfee3b20aedb5164f083dc0889301e2b65ff Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 14 Dec 2021 18:02:33 -0800 Subject: [PATCH 20/23] Use the ForkJoinPool for the heap verifier. --- .../oracle/graal/pointsto/ObjectScanner.java | 23 ++++++++++++------- .../pointsto/heap/HeapSnapshotVerifier.java | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index 3e047c1d8d4e..2e46d07b6e97 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -137,8 +137,8 @@ default DebugContext getDebug(OptionValues options, List f private void scanEmbeddedRoot(JavaConstant root, BytecodePosition position) { try { EmbeddedRootScan reason = new EmbeddedRootScan(position, root); - scanConstant(root, reason); scanningObserver.forEmbeddedRoot(root, reason); + scanConstant(root, reason); } catch (UnsupportedFeatureException ex) { AnalysisMethod method = (AnalysisMethod) position.getMethod(); bb.getUnsupportedFeatures().addMessage(method.format("%H.%n(%p)"), method, ex.getMessage(), null, ex); @@ -178,10 +178,14 @@ protected final void scanField(AnalysisField field, JavaConstant receiver, ScanR } else if (fieldValue.isNull()) { scanningObserver.forNullFieldValue(receiver, field, reason); } else if (fieldValue.getJavaKind() == JavaKind.Object) { - /* Scan the field value. */ - scanConstant(fieldValue, reason); - /* Process the field value. */ + /* First notify the observer about the field value... */ scanningObserver.forNonNullFieldValue(receiver, field, fieldValue, reason); + /* + * ... and only then scan the new value, i.e., follow its references. The order is + * important for observers that expect to see the receiver before any of its + * referenced elements are being scanned. + */ + scanConstant(fieldValue, reason); } } catch (UnsupportedFeatureException ex) { @@ -211,11 +215,14 @@ protected final void scanArray(JavaConstant array, ScanReason prevReason) { Object element = bb.getUniverse().replaceObject(e); JavaConstant elementConstant = bb.getSnippetReflectionProvider().forObject(element); AnalysisType elementType = analysisType(bb, element); - - /* Scan the array element. */ - scanConstant(elementConstant, reason); - /* Process the array element. */ + /* First notify the observer about the array element value... */ scanningObserver.forNonNullArrayElement(array, arrayType, elementConstant, elementType, idx, reason); + /* + * ... and only then scan the new value, i.e., follow its references. The order + * is important for observers that expect to see the receiver before any of its + * referenced elements are being scanned. + */ + scanConstant(elementConstant, reason); } } catch (UnsupportedFeatureException ex) { unsupportedFeature(arrayType.toJavaName(true), ex.getMessage(), reason); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java index 149a8aa8f429..d735cc5d7a03 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java @@ -119,7 +119,7 @@ public HeapSnapshotVerifier(BigBang bb, ImageHeap imageHeap, ImageHeapScanner sc public boolean verifyHeapSnapshot(CompletionExecutor executor) throws InterruptedException { foundMismatch = false; scannedObjects.reset(); - ObjectScanner objectScanner = new ObjectScanner(bb, scannedObjects, new ScanningObserver()); + ObjectScanner objectScanner = new ObjectScanner(bb, executor, scannedObjects, new ScanningObserver()); executor.start(); scanTypes(objectScanner); objectScanner.scanBootImageHeapRoots(); From c74d0c735ac9bc4c38840294d7afef43d5adc4ee Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 14 Dec 2021 23:05:28 -0800 Subject: [PATCH 21/23] Notify veifier when analysis is modified. --- .../AnalysisObjectScanningObserver.java | 10 +-- .../pointsto/ObjectScanningObserver.java | 10 ++- .../pointsto/heap/HeapSnapshotVerifier.java | 18 ++--- .../oracle/graal/pointsto/heap/ImageHeap.java | 3 +- .../graal/pointsto/heap/ImageHeapScanner.java | 65 ++++++++++--------- .../oracle/graal/pointsto/heap/TypeData.java | 5 +- .../reports/AnalysisHeapHistogramPrinter.java | 6 +- .../pointsto/reports/ObjectTreePrinter.java | 9 ++- 8 files changed, 73 insertions(+), 53 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java index f986dd65a845..b4060dd3a302 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AnalysisObjectScanningObserver.java @@ -61,7 +61,7 @@ public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sca } @Override - public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { PointsToAnalysis analysis = getAnalysis(); AnalysisType fieldType = analysis.getMetaAccess().lookupJavaType(analysis.getSnippetReflectionProvider().asObject(Object.class, fieldValue).getClass()); assert fieldType.isInstantiated() : fieldType; @@ -72,8 +72,9 @@ public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, Jav if (!fieldTypeFlow.getState().containsObject(constantObject)) { /* Add the new constant to the field's flow state. */ TypeState constantTypeState = TypeState.forNonNullObject(analysis, constantObject); - fieldTypeFlow.addState(analysis, constantTypeState); + return fieldTypeFlow.addState(analysis, constantTypeState); } + return false; } /** @@ -107,7 +108,7 @@ public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, i } @Override - public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex, ScanReason reason) { + public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex, ScanReason reason) { assert elementType.isInstantiated() : elementType; ArrayElementsTypeFlow arrayObjElementsFlow = getArrayElementsFlow(array, arrayType); PointsToAnalysis analysis = getAnalysis(); @@ -115,8 +116,9 @@ public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, J if (!arrayObjElementsFlow.getState().containsObject(constantObject)) { /* Add the constant element to the constant's array type flow. */ TypeState elementTypeState = TypeState.forNonNullObject(analysis, constantObject); - arrayObjElementsFlow.addState(analysis, elementTypeState); + return arrayObjElementsFlow.addState(analysis, elementTypeState); } + return false; } /** diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java index 544899ba4024..d208dbbd5e21 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanningObserver.java @@ -60,8 +60,11 @@ default boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sc /** * Hook for scanned non-null field value. + * + * @return true if value is consumed, false otherwise */ - default void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + default boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + return false; } /** @@ -73,8 +76,11 @@ default boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, /** * Hook for scanned non-null element value. + * + * @return true if value is consumed, false otherwise */ - default void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex, ScanReason reason) { + default boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex, ScanReason reason) { + return false; } /** diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java index d735cc5d7a03..96c9fb1cf404 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/HeapSnapshotVerifier.java @@ -156,7 +156,7 @@ public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sca } @Override - public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { if (field.isStatic()) { TypeData typeData = field.getDeclaringClass().getOrComputeData(); AnalysisFuture fieldValueTask = typeData.getFieldTask(field); @@ -166,8 +166,8 @@ public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, Jav } JavaConstant fieldSnapshot = fieldValueTask.guardedGet(); if (!Objects.equals(fieldSnapshot, fieldValue)) { - warnStaticFieldMismatch(field, fieldSnapshot, fieldValue, reason); - scanner.patchStaticField(typeData, field, fieldValue, reason).ensureDone(); + Runnable onAnalysisModified = () -> warnStaticFieldMismatch(field, fieldSnapshot, fieldValue, reason); + scanner.patchStaticField(typeData, field, fieldValue, reason, onAnalysisModified).ensureDone(); } } else { ImageHeapInstance receiverObject = (ImageHeapInstance) getReceiverObject(receiver, reason); @@ -178,10 +178,11 @@ public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, Jav } JavaConstant fieldSnapshot = fieldValueTask.guardedGet(); if (!Objects.equals(fieldSnapshot, fieldValue)) { - warnInstanceFieldMismatch(field, fieldSnapshot, fieldValue, reason); - scanner.patchInstanceField(receiverObject, field, fieldValue, reason).ensureDone(); + Runnable onAnalysisModified = () -> warnInstanceFieldMismatch(field, fieldSnapshot, fieldValue, reason); + scanner.patchInstanceField(receiverObject, field, fieldValue, reason, onAnalysisModified).ensureDone(); } } + return false; } @Override @@ -194,13 +195,14 @@ public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, i } @Override - public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementValue, AnalysisType elementType, int index, ScanReason reason) { + public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementValue, AnalysisType elementType, int index, ScanReason reason) { ImageHeapArray arrayObject = (ImageHeapArray) getReceiverObject(array, reason); JavaConstant elementSnapshot = arrayObject.getElement(index); if (!Objects.equals(elementSnapshot, elementValue)) { - warnArrayElementMismatch(arrayType, elementSnapshot, elementValue, reason); - arrayObject.setElement(index, scanner.onArrayElementReachable(array, arrayType, elementValue, index, reason)); + Runnable onAnalysisModified = () -> warnArrayElementMismatch(arrayType, elementSnapshot, elementValue, reason); + arrayObject.setElement(index, scanner.onArrayElementReachable(array, arrayType, elementValue, index, reason, onAnalysisModified)); } + return false; } private ImageHeapObject getReceiverObject(JavaConstant constant, ScanReason reason) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java index 1a5356fbf181..8f759046c761 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeap.java @@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap; import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.heap.value.ValueSupplier; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.AnalysisFuture; @@ -117,7 +118,7 @@ public JavaConstant readFieldValue(AnalysisField field) { /** * Return a task for transforming and snapshotting the field value, effectively a future for - * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, JavaConstant, ObjectScanner.ScanReason)}. + * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, ValueSupplier, ObjectScanner.ScanReason)}. */ public AnalysisFuture getFieldTask(AnalysisField field) { return objectFieldValues[field.getPosition()]; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index 8a633c9b43c4..eba21805247b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -61,7 +61,6 @@ import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.code.BytecodePosition; -import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -187,23 +186,9 @@ void markTypeInstantiated(AnalysisType type) { type.registerAsInHeap(); } - AnalysisFuture markConstantReachable(Constant constant, ScanReason reason) { - if (!(constant instanceof JavaConstant)) { - /* - * The bytecode parser sometimes embeds low-level VM constants for types into the - * high-level graph. Since these constants are the result of type lookups, these types - * are already marked as reachable. Eventually, the bytecode parser should be changed to - * only use JavaConstant. - */ - return null; - } - - JavaConstant javaConstant = (JavaConstant) constant; - if (javaConstant.getJavaKind() == JavaKind.Object && javaConstant.isNonNull()) { - if (!hostVM.platformSupported(asObject(javaConstant).getClass())) { - return null; - } - return getOrCreateConstantReachableTask(javaConstant, reason); + AnalysisFuture markConstantReachable(JavaConstant constant, ScanReason reason) { + if (constant.getJavaKind() == JavaKind.Object && constant.isNonNull()) { + return getOrCreateConstantReachableTask(constant, reason); } return null; @@ -340,11 +325,15 @@ protected Object unwrapObject(JavaConstant constant) { return snippetReflection.asObject(Object.class, constant); } - JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, JavaConstant fieldValue, ScanReason reason) { - return onFieldValueReachable(field, receiver, ValueSupplier.eagerValue(fieldValue), reason); + JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, JavaConstant fieldValue, ScanReason reason, Runnable onAnalysisModified) { + return onFieldValueReachable(field, receiver, ValueSupplier.eagerValue(fieldValue), reason, onAnalysisModified); } JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, ValueSupplier rawValue, ScanReason reason) { + return onFieldValueReachable(field, receiver, rawValue, reason, null); + } + + JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, ValueSupplier rawValue, ScanReason reason, Runnable onAnalysisModified) { AnalysisError.guarantee(field.isReachable(), "Field value is only reachable when field is reachable " + field.format("%H.%n")); /* @@ -359,10 +348,11 @@ JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, V JavaConstant transformedValue = transformFieldValue(field, receiver, rawValue.get()); if (scanningObserver != null) { + boolean analysisModified = false; if (transformedValue.getJavaKind() == JavaKind.Object && hostVM.isRelocatedPointer(asObject(transformedValue))) { - scanningObserver.forRelocatedPointerFieldValue(receiver, field, transformedValue, reason); + analysisModified = scanningObserver.forRelocatedPointerFieldValue(receiver, field, transformedValue, reason); } else if (transformedValue.isNull()) { - scanningObserver.forNullFieldValue(receiver, field, reason); + analysisModified = scanningObserver.forNullFieldValue(receiver, field, reason); } else { // TODO this adds the transformedValue in the heap, should we do this here? // This will also run the replacer again; transformFieldValue already run the @@ -371,9 +361,12 @@ JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, V /* Notify the points-to analysis of the scan. */ if (objectFuture != null) { /* Add the transformed value to the image heap. */ - scanningObserver.forNonNullFieldValue(receiver, field, objectFuture.ensureDone().getObject(), reason); + analysisModified = scanningObserver.forNonNullFieldValue(receiver, field, objectFuture.ensureDone().getObject(), reason); } } + if (analysisModified && onAnalysisModified != null) { + onAnalysisModified.run(); + } } /* Return the transformed value, but NOT the image heap object. */ return transformedValue; @@ -385,18 +378,28 @@ protected JavaConstant transformFieldValue(AnalysisField field, JavaConstant rec } protected JavaConstant onArrayElementReachable(JavaConstant array, AnalysisType arrayType, JavaConstant rawElementValue, int elementIndex, ScanReason reason) { + return onArrayElementReachable(array, arrayType, rawElementValue, elementIndex, reason, null); + } + + protected JavaConstant onArrayElementReachable(JavaConstant array, AnalysisType arrayType, JavaConstant rawElementValue, int elementIndex, ScanReason reason, Runnable onAnalysisModified) { AnalysisFuture objectFuture = markConstantReachable(rawElementValue, reason); if (scanningObserver != null && arrayType.getComponentType().getJavaKind() == JavaKind.Object) { + boolean analysisModified; if (objectFuture == null) { - scanningObserver.forNullArrayElement(array, arrayType, elementIndex, reason); + analysisModified = scanningObserver.forNullArrayElement(array, arrayType, elementIndex, reason); } else { ImageHeapObject element = objectFuture.ensureDone(); AnalysisType elementType = constantType(element.getObject()); markTypeInstantiated(elementType); /* Process the array element. */ - scanningObserver.forNonNullArrayElement(array, arrayType, element.getObject(), elementType, elementIndex, reason); + // TODO + analysisModified = scanningObserver.forNonNullArrayElement(array, arrayType, element.getObject(), elementType, elementIndex, reason); return element.getObject(); } + if (analysisModified && onAnalysisModified != null) { + onAnalysisModified.run(); + } + } return null; } @@ -448,7 +451,7 @@ public Object rescanRoot(Field reflectionField) { AnalysisField field = metaAccess.lookupJavaField(reflectionField); JavaConstant fieldValue = readHostedFieldValue(field, null).get(); TypeData typeData = field.getDeclaringClass().getOrComputeData(); - AnalysisFuture fieldTask = patchStaticField(typeData, field, fieldValue, OtherReason.RESCAN); + AnalysisFuture fieldTask = patchStaticField(typeData, field, fieldValue, OtherReason.RESCAN, null); if (field.isRead()) { Object root = asObject(fieldTask.ensureDone()); rescanCollectionElements(root); @@ -477,7 +480,7 @@ public void rescanField(Object receiver, Field reflectionField) { JavaConstant fieldValue = readHostedFieldValue(field, universe.toHosted(receiverConstant)).get(); if (fieldValue != null) { ImageHeapInstance receiverObject = (ImageHeapInstance) toImageHeapObject(receiverConstant); - AnalysisFuture fieldTask = patchInstanceField(receiverObject, field, fieldValue, OtherReason.RESCAN); + AnalysisFuture fieldTask = patchInstanceField(receiverObject, field, fieldValue, OtherReason.RESCAN, null); if (field.isRead()) { rescanCollectionElements(asObject(fieldTask.ensureDone())); } @@ -485,14 +488,14 @@ public void rescanField(Object receiver, Field reflectionField) { } } - protected AnalysisFuture patchStaticField(TypeData typeData, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, null, fieldValue, reason)); + protected AnalysisFuture patchStaticField(TypeData typeData, AnalysisField field, JavaConstant fieldValue, ScanReason reason, Runnable onAnalysisModified) { + AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, null, fieldValue, reason, onAnalysisModified)); typeData.setFieldTask(field, task); return task; } - protected AnalysisFuture patchInstanceField(ImageHeapInstance receiverObject, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, receiverObject.getObject(), fieldValue, reason)); + protected AnalysisFuture patchInstanceField(ImageHeapInstance receiverObject, AnalysisField field, JavaConstant fieldValue, ScanReason reason, Runnable onAnalysisModified) { + AnalysisFuture task = new AnalysisFuture<>(() -> onFieldValueReachable(field, receiverObject.getObject(), fieldValue, reason, onAnalysisModified)); receiverObject.setFieldTask(field, task); return task; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/TypeData.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/TypeData.java index 27afd60e5ae0..7297070a44b9 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/TypeData.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/TypeData.java @@ -27,6 +27,7 @@ import java.util.Map; import com.oracle.graal.pointsto.ObjectScanner; +import com.oracle.graal.pointsto.heap.value.ValueSupplier; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.AnalysisFuture; @@ -46,7 +47,7 @@ public final class TypeData { /** * The raw values of all static fields, regardless of field reachability status. Evaluating the * {@link AnalysisFuture} runs - * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, JavaConstant, ObjectScanner.ScanReason)} + * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, ValueSupplier, ObjectScanner.ScanReason)} * adds the result to the image heap}. */ final Map> staticFieldValues; @@ -62,7 +63,7 @@ public boolean shouldInitializeAtRunTime() { /** * Return a task for transforming and snapshotting the field value, effectively a future for - * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, JavaConstant, ObjectScanner.ScanReason)}. + * {@link ImageHeapScanner#onFieldValueReachable(AnalysisField, JavaConstant, ValueSupplier, ObjectScanner.ScanReason)}. */ public AnalysisFuture getFieldTask(AnalysisField field) { return staticFieldValues.get(field); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisHeapHistogramPrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisHeapHistogramPrinter.java index 6a3517c818e2..66d7b3ce6d3c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisHeapHistogramPrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisHeapHistogramPrinter.java @@ -101,7 +101,8 @@ public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sca } @Override - public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + return false; } @Override @@ -110,7 +111,8 @@ public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, i } @Override - public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int index, ScanReason reason) { + public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int index, ScanReason reason) { + return false; } } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java index c27fd224c1e2..3d2580cb9ac7 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/ObjectTreePrinter.java @@ -339,11 +339,11 @@ public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, Sca } @Override - public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { + public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { if (receiver == null) { // static field - return; + return false; } if (constantToNode.containsKey(receiver) && constantToNode.containsKey(fieldValue)) { @@ -351,6 +351,7 @@ public void forNonNullFieldValue(JavaConstant receiver, AnalysisField field, Jav ObjectNodeBase valueNode = constantToNode.get(fieldValue); receiverNode.addField(field, valueNode); } + return true; } @Override @@ -363,12 +364,14 @@ public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, i } @Override - public void forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int index, ScanReason reason) { + public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int index, ScanReason reason) { if (constantToNode.containsKey(array) && constantToNode.containsKey(elementConstant)) { ArrayObjectNode arrayNode = (ArrayObjectNode) constantToNode.get(array); ObjectNodeBase valueNode = constantToNode.get(elementConstant); arrayNode.addElement(index, valueNode); + return true; } + return false; } @Override From e8ad1bede9a011d7bab93867bfbaa893f7ab6e96 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 14 Dec 2021 22:41:18 -0800 Subject: [PATCH 22/23] Refactor heap scanner. --- .../graal/pointsto/heap/ImageHeapScanner.java | 71 +++++++++---------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index eba21805247b..ec4c4333614a 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -186,12 +186,12 @@ void markTypeInstantiated(AnalysisType type) { type.registerAsInHeap(); } - AnalysisFuture markConstantReachable(JavaConstant constant, ScanReason reason) { + JavaConstant markConstantReachable(JavaConstant constant, ScanReason reason) { if (constant.getJavaKind() == JavaKind.Object && constant.isNonNull()) { - return getOrCreateConstantReachableTask(constant, reason); + return getOrCreateConstantReachableTask(constant, reason).ensureDone().getObject(); } - return null; + return constant; } public ImageHeapObject toImageHeapObject(JavaConstant constant) { @@ -343,33 +343,32 @@ JavaConstant onFieldValueReachable(AnalysisField field, JavaConstant receiver, V */ AnalysisError.guarantee(rawValue.isAvailable(), "Value not yet available for " + field.format("%H.%n")); - /* Attempting to materialize the value before it is available may result in an error. */ - // TODO why run the transformer here, it is run by markConstantReachable anyway + // TODO the transformer here and markConstantReachable both run the object replacers JavaConstant transformedValue = transformFieldValue(field, receiver, rawValue.get()); + /* Add the transformed value to the image heap. */ + JavaConstant fieldValue = markConstantReachable(transformedValue, reason); if (scanningObserver != null) { - boolean analysisModified = false; - if (transformedValue.getJavaKind() == JavaKind.Object && hostVM.isRelocatedPointer(asObject(transformedValue))) { - analysisModified = scanningObserver.forRelocatedPointerFieldValue(receiver, field, transformedValue, reason); - } else if (transformedValue.isNull()) { - analysisModified = scanningObserver.forNullFieldValue(receiver, field, reason); - } else { - // TODO this adds the transformedValue in the heap, should we do this here? - // This will also run the replacer again; transformFieldValue already run the - // replacer - AnalysisFuture objectFuture = markConstantReachable(transformedValue, reason); - /* Notify the points-to analysis of the scan. */ - if (objectFuture != null) { - /* Add the transformed value to the image heap. */ - analysisModified = scanningObserver.forNonNullFieldValue(receiver, field, objectFuture.ensureDone().getObject(), reason); - } - } + /* Notify the points-to analysis of the scan. */ + boolean analysisModified = notifyAnalysis(field, receiver, fieldValue, reason); if (analysisModified && onAnalysisModified != null) { onAnalysisModified.run(); } } /* Return the transformed value, but NOT the image heap object. */ - return transformedValue; + return fieldValue; + } + + private boolean notifyAnalysis(AnalysisField field, JavaConstant receiver, JavaConstant fieldValue, ScanReason reason) { + boolean analysisModified = false; + if (fieldValue.getJavaKind() == JavaKind.Object && hostVM.isRelocatedPointer(asObject(fieldValue))) { + analysisModified = scanningObserver.forRelocatedPointerFieldValue(receiver, field, fieldValue, reason); + } else if (fieldValue.isNull()) { + analysisModified = scanningObserver.forNullFieldValue(receiver, field, reason); + } else if (fieldValue.getJavaKind() == JavaKind.Object) { + analysisModified = scanningObserver.forNonNullFieldValue(receiver, field, fieldValue, reason); + } + return analysisModified; } @SuppressWarnings("unused") @@ -382,26 +381,26 @@ protected JavaConstant onArrayElementReachable(JavaConstant array, AnalysisType } protected JavaConstant onArrayElementReachable(JavaConstant array, AnalysisType arrayType, JavaConstant rawElementValue, int elementIndex, ScanReason reason, Runnable onAnalysisModified) { - AnalysisFuture objectFuture = markConstantReachable(rawElementValue, reason); + JavaConstant elementValue = markConstantReachable(rawElementValue, reason); if (scanningObserver != null && arrayType.getComponentType().getJavaKind() == JavaKind.Object) { - boolean analysisModified; - if (objectFuture == null) { - analysisModified = scanningObserver.forNullArrayElement(array, arrayType, elementIndex, reason); - } else { - ImageHeapObject element = objectFuture.ensureDone(); - AnalysisType elementType = constantType(element.getObject()); - markTypeInstantiated(elementType); - /* Process the array element. */ - // TODO - analysisModified = scanningObserver.forNonNullArrayElement(array, arrayType, element.getObject(), elementType, elementIndex, reason); - return element.getObject(); - } + /* Notify the points-to analysis of the scan. */ + boolean analysisModified = notifyAnalysis(array, arrayType, elementValue, elementIndex, reason); if (analysisModified && onAnalysisModified != null) { onAnalysisModified.run(); } + } + return elementValue; + } + private boolean notifyAnalysis(JavaConstant array, AnalysisType arrayType, JavaConstant elementValue, int elementIndex, ScanReason reason) { + boolean analysisModified; + if (elementValue.isNull()) { + analysisModified = scanningObserver.forNullArrayElement(array, arrayType, elementIndex, reason); + } else { + AnalysisType elementType = metaAccess.lookupJavaType(elementValue); + analysisModified = scanningObserver.forNonNullArrayElement(array, arrayType, elementValue, elementType, elementIndex, reason); } - return null; + return analysisModified; } void onObjectReachable(ImageHeapObject imageHeapObject) { From 1ac236bfc877180e8cbbe7ca51d277a673766781 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Wed, 15 Dec 2021 00:13:24 -0800 Subject: [PATCH 23/23] Refactor heap verification. --- .../graal/pointsto/PointsToAnalysis.java | 14 +++---- .../pointsto/heap/HeapSnapshotVerifier.java | 39 +++++++++++-------- 2 files changed, 30 insertions(+), 23 deletions(-) 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 4b79d40b46f8..9f72be1fe3bc 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 @@ -721,8 +721,8 @@ public void runAnalysis(DebugContext debugContext, Function HeapVerifierVerbosity = new OptionKey<>(0); + public static final OptionKey HeapVerifierVerbosity = new OptionKey<>(2); } protected final BigBang bb; @@ -67,7 +67,8 @@ static class Options { protected final ImageHeap imageHeap; private ReusableSet scannedObjects; - private boolean foundMismatch; + private boolean heapPatched; + private boolean analysisModified; private final int verbosity; @@ -117,7 +118,8 @@ public HeapSnapshotVerifier(BigBang bb, ImageHeap imageHeap, ImageHeapScanner sc } public boolean verifyHeapSnapshot(CompletionExecutor executor) throws InterruptedException { - foundMismatch = false; + heapPatched = false; + analysisModified = false; scannedObjects.reset(); ObjectScanner objectScanner = new ObjectScanner(bb, executor, scannedObjects, new ScanningObserver()); executor.start(); @@ -125,7 +127,7 @@ public boolean verifyHeapSnapshot(CompletionExecutor executor) throws Interrupte objectScanner.scanBootImageHeapRoots(); executor.complete(); executor.shutdown(); - return foundMismatch; + return analysisModified || heapPatched; } protected void scanTypes(@SuppressWarnings("unused") ObjectScanner objectScanner) { @@ -141,7 +143,7 @@ private final class ScanningObserver implements ObjectScanningObserver { public boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisField field, JavaConstant fieldValue, ScanReason reason) { boolean result = scanner.getScanningObserver().forRelocatedPointerFieldValue(receiver, field, fieldValue, reason); if (result) { - foundMismatch = true; + analysisModified = true; } return result; } @@ -150,7 +152,7 @@ public boolean forRelocatedPointerFieldValue(JavaConstant receiver, AnalysisFiel public boolean forNullFieldValue(JavaConstant receiver, AnalysisField field, ScanReason reason) { boolean result = scanner.getScanningObserver().forNullFieldValue(receiver, field, reason); if (result) { - foundMismatch = true; + analysisModified = true; } return result; } @@ -161,25 +163,27 @@ public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, TypeData typeData = field.getDeclaringClass().getOrComputeData(); AnalysisFuture fieldValueTask = typeData.getFieldTask(field); if (!fieldValueTask.isDone()) { - warnStaticFieldNotComputed(field, fieldValue, reason); + // warnStaticFieldNotComputed(field, fieldValue, reason); fieldValueTask.ensureDone(); } JavaConstant fieldSnapshot = fieldValueTask.guardedGet(); if (!Objects.equals(fieldSnapshot, fieldValue)) { Runnable onAnalysisModified = () -> warnStaticFieldMismatch(field, fieldSnapshot, fieldValue, reason); scanner.patchStaticField(typeData, field, fieldValue, reason, onAnalysisModified).ensureDone(); + heapPatched = true; } } else { ImageHeapInstance receiverObject = (ImageHeapInstance) getReceiverObject(receiver, reason); AnalysisFuture fieldValueTask = receiverObject.getFieldTask(field); if (!fieldValueTask.isDone()) { - warnInstanceFieldNotComputed(field, fieldValue, reason); + // warnInstanceFieldNotComputed(field, fieldValue, reason); fieldValueTask.ensureDone(); } JavaConstant fieldSnapshot = fieldValueTask.guardedGet(); if (!Objects.equals(fieldSnapshot, fieldValue)) { Runnable onAnalysisModified = () -> warnInstanceFieldMismatch(field, fieldSnapshot, fieldValue, reason); scanner.patchInstanceField(receiverObject, field, fieldValue, reason, onAnalysisModified).ensureDone(); + heapPatched = true; } } return false; @@ -189,7 +193,7 @@ public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field, public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, int elementIndex, ScanReason reason) { boolean result = scanner.getScanningObserver().forNullArrayElement(array, arrayType, elementIndex, reason); if (result) { - foundMismatch = true; + analysisModified = true; } return result; } @@ -201,6 +205,7 @@ public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType if (!Objects.equals(elementSnapshot, elementValue)) { Runnable onAnalysisModified = () -> warnArrayElementMismatch(arrayType, elementSnapshot, elementValue, reason); arrayObject.setElement(index, scanner.onArrayElementReachable(array, arrayType, elementValue, index, reason, onAnalysisModified)); + heapPatched = true; } return false; } @@ -236,11 +241,13 @@ public void forScannedConstant(JavaConstant value, ScanReason reason) { if (task == null) { warnNoTaskForHub(value, reason); scanner.toImageHeapObject(value, reason); + heapPatched = true; } else { if (!task.isDone()) { /* If there is a task for the hub it should have been triggered. */ warnNoTaskComputedForHub(value, reason); task.ensureDone(); + heapPatched = true; } JavaConstant snapshot = task.guardedGet().getObject(); if (!Objects.equals(snapshot, value)) { @@ -252,21 +259,21 @@ public void forScannedConstant(JavaConstant value, ScanReason reason) { } private void warnNoTaskForHub(JavaConstant value, ScanReason reason) { - foundMismatch = true; + analysisModified = true; if (!skipWarning()) { warning(reason, "No snapshot task found for hub %s %n", value); } } private void warnNoTaskComputedForHub(JavaConstant value, ScanReason reason) { - foundMismatch = true; + analysisModified = true; if (!skipWarning()) { warning(reason, "Snapshot not yet computed for hub %n new value: %s %n", value); } } private void warnArrayElementMismatch(AnalysisType arrayType, JavaConstant elementSnapshot, JavaConstant elementValue, ScanReason reason) { - foundMismatch = true; + analysisModified = true; if (skipWarning(() -> skipArrayTypes.contains(arrayType))) { return; } @@ -274,28 +281,28 @@ private void warnArrayElementMismatch(AnalysisType arrayType, JavaConstant eleme } private void warnStaticFieldMismatch(AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { - foundMismatch = true; + analysisModified = true; if (!skipWarning(() -> internalFields.contains(field))) { warning(reason, "Value mismatch for static field %s %n snapshot: %s %n new value: %s %n", field, fieldSnapshot, fieldValue); } } + @SuppressWarnings("unused") private void warnStaticFieldNotComputed(AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - foundMismatch = true; if (!skipWarning(() -> internalFields.contains(field))) { warning(reason, "Snapshot not yet computed for static field %s %n new value: %s %n", field, fieldValue); } } private void warnInstanceFieldMismatch(AnalysisField field, JavaConstant fieldSnapshot, JavaConstant fieldValue, ScanReason reason) { - foundMismatch = true; + analysisModified = true; if (!skipWarning(() -> internalFields.contains(field))) { warning(reason, "Value mismatch for instance field %s %n snapshot: %s %n new value: %s %n", field, fieldSnapshot, fieldValue); } } + @SuppressWarnings("unused") private void warnInstanceFieldNotComputed(AnalysisField field, JavaConstant fieldValue, ScanReason reason) { - foundMismatch = true; if (!skipWarning(() -> internalFields.contains(field) || externalFields.contains(field))) { warning(reason, "Snapshot not yet computed for instance field %s %n new value: %s %n", field, fieldValue); }