diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java index 29281d1bf142..68caf179e27e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java @@ -417,7 +417,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec // Primitive Class case returns null PiNode klassNonNull = helper.emitNullReturnGuard(klass, nullValue, GraalDirectives.UNLIKELY_PROBABILITY); - // if ((Klass::_access_flags & Modifer.INTERCAE) != 0) return null + // if ((Klass::_access_flags & Modifer.INTERFACE) != 0) return null ValueNode accessFlags = helper.readKlassAccessFlags(klassNonNull); LogicNode test = IntegerTestNode.create(accessFlags, ConstantNode.forInt(Modifier.INTERFACE), NodeView.DEFAULT); helper.emitReturnIfNot(test, nullValue, GraalDirectives.UNLIKELY_PROBABILITY); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java index 1370d7f40382..975975f3087a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java @@ -143,6 +143,10 @@ public Field originalField(ResolvedJavaField field) { Objects.requireNonNull(field); GraalError.guarantee(field instanceof HotSpotResolvedJavaField, "Unexpected implementation class: %s", field.getClass()); + if (field.isInternal()) { + /* internal fields never have a corresponding java.lang.reflect.Field. */ + return null; + } return runtime().getMirror(field); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java index e4a6f3ed1741..c0a35aa86b8b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java @@ -25,18 +25,13 @@ package com.oracle.graal.pointsto; import java.io.PrintWriter; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Function; -import java.util.stream.Stream; import org.graalvm.nativeimage.hosted.Feature; -import com.oracle.graal.pointsto.ClassInclusionPolicy.LayeredBaseImageInclusionPolicy; +import com.oracle.graal.pointsto.ClassInclusionPolicy.SharedLayerImageInclusionPolicy; import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; @@ -68,7 +63,9 @@ import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.BytecodePosition; import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; /** * This abstract class is shared between Reachability and Points-to. It contains generic methods @@ -103,6 +100,8 @@ public abstract class AbstractAnalysisEngine implements BigBang { protected final Timer analysisTimer; protected final Timer verifyHeapTimer; protected final ClassInclusionPolicy classInclusionPolicy; + private static final ResolvedJavaMethod[] NO_METHODS = new ResolvedJavaMethod[]{}; + private static final ResolvedJavaField[] NO_FIELDS = new ResolvedJavaField[]{}; @SuppressWarnings("this-escape") public AbstractAnalysisEngine(OptionValues options, AnalysisUniverse universe, HostVM hostVM, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflectionProvider, @@ -208,7 +207,7 @@ private boolean analysisModified() { * After the analysis reaches a stable state check if the shadow heap contains all * objects reachable from roots. If this leads to analysis state changes, an additional * analysis iteration will be run. - * + * * We reuse the analysis executor, which at this point should be in before-start state: * the analysis finished and it re-initialized the executor for the next iteration. The * verifier controls the life cycle of the executor: it starts it and then waits until @@ -259,7 +258,7 @@ public void profileConstantObject(AnalysisType type) { } public boolean isBaseLayerAnalysisEnabled() { - return classInclusionPolicy instanceof LayeredBaseImageInclusionPolicy; + return classInclusionPolicy instanceof SharedLayerImageInclusionPolicy; } @Override @@ -352,30 +351,58 @@ public final boolean executorIsStarted() { } @Override - public void registerTypeForBaseImage(Class cls) { - if (getOrDefault(cls, classInclusionPolicy::isClassIncluded, false)) { - classInclusionPolicy.includeClass(cls); - Stream.concat(Arrays.stream(getOrDefault(cls, Class::getDeclaredConstructors, new Constructor[0])), Arrays.stream(getOrDefault(cls, Class::getDeclaredMethods, new Method[0]))) - .filter(classInclusionPolicy::isMethodIncluded) - .forEach(classInclusionPolicy::includeMethod); - Arrays.stream(getOrDefault(cls, Class::getDeclaredFields, new Field[0])) - .filter(classInclusionPolicy::isFieldIncluded) - .forEach(classInclusionPolicy::includeField); + public void tryRegisterTypeForBaseImage(ResolvedJavaType type) { + if (tryApply(type, classInclusionPolicy::isOriginalTypeIncluded, false)) { + classInclusionPolicy.includeType(type); + ResolvedJavaMethod[] constructors = tryApply(type, t -> t.getDeclaredConstructors(false), NO_METHODS); + ResolvedJavaMethod[] methods = tryApply(type, t -> t.getDeclaredMethods(false), NO_METHODS); + for (ResolvedJavaMethod[] executables : List.of(constructors, methods)) { + for (ResolvedJavaMethod executable : executables) { + if (classInclusionPolicy.isOriginalMethodIncluded(executable)) { + classInclusionPolicy.includeMethod(executable); + } + } + } + ResolvedJavaField[] instanceFields = tryApply(type, t -> t.getInstanceFields(false), NO_FIELDS); + ResolvedJavaField[] staticFields = tryApply(type, ResolvedJavaType::getStaticFields, NO_FIELDS); + for (ResolvedJavaField[] fields : List.of(instanceFields, staticFields)) { + for (ResolvedJavaField field : fields) { + if (classInclusionPolicy.isOriginalFieldIncluded(field)) { + classInclusionPolicy.includeField(field); + } + } + } } } @Override - public void registerMethodForBaseImage(AnalysisMethod method) { - if (classInclusionPolicy.isMethodIncluded(method)) { + public void tryRegisterMethodForBaseImage(AnalysisMethod method) { + if (classInclusionPolicy.isAnalysisMethodIncluded(method)) { classInclusionPolicy.includeMethod(method); } } - public static U getOrDefault(T cls, Function getMembers, U backup) { + @Override + public void tryRegisterFieldForBaseImage(AnalysisField field) { + if (classInclusionPolicy.isAnalysisFieldIncluded(field)) { + classInclusionPolicy.includeField(field); + } + } + + /** + * Applies {@code function} to {@code type} and returns the result or, if + * {@link NoClassDefFoundError} or {@link IncompatibleClassChangeError} thrown when applying the + * function, returns {@code fallback}. + *

+ * The error handling allows for querying members (fields, methods or constructors) of a class + * where a member signature refers to a type that is unresolvable due to an incomplete class + * path. Such resolution errors need to be ignored when building a shared layer. + */ + private static U tryApply(ResolvedJavaType type, Function function, U fallback) { try { - return getMembers.apply(cls); + return function.apply(type); } catch (NoClassDefFoundError | IncompatibleClassChangeError e) { - return backup; + return fallback; } } 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 7ebe3be86051..cfd4ef3ba6e6 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 @@ -45,6 +45,7 @@ import jdk.graal.compiler.word.WordTypes; import jdk.vm.ci.code.BytecodePosition; import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.ResolvedJavaType; /** * Central static analysis interface that groups together the functionality of reachability analysis @@ -139,12 +140,17 @@ default AnalysisMethod fallbackResolveConcreteMethod(AnalysisType resolvingType, } @SuppressWarnings("unused") - default void registerTypeForBaseImage(Class cls) { + default void tryRegisterTypeForBaseImage(ResolvedJavaType type) { } @SuppressWarnings("unused") - default void registerMethodForBaseImage(AnalysisMethod method) { + default void tryRegisterMethodForBaseImage(AnalysisMethod method) { + + } + + @SuppressWarnings("unused") + default void tryRegisterFieldForBaseImage(AnalysisField field) { } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ClassInclusionPolicy.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ClassInclusionPolicy.java index 9cd7db9945e0..909df697454b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ClassInclusionPolicy.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ClassInclusionPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -24,27 +24,26 @@ */ package com.oracle.graal.pointsto; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Modifier; - -import org.graalvm.nativeimage.AnnotationAccess; -import org.graalvm.nativeimage.hosted.Feature; - +import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; -import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.util.AnalysisError; -import jdk.graal.compiler.api.replacements.Fold; +import jdk.vm.ci.meta.ModifiersProvider; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; /** - * Policy used to determine which classes, methods and fields need to be included in the image when - * {@code LayerCreate} sub-options {@code module}, {@code package} or {@code path} are specified - * depending on the configuration. + * Policy used to determine which types, methods and fields need to be included in an image. The + * decisions made by a policy are based on {@link ModifiersProvider#getModifiers()} which follows + * the type, method and field flags in the JVM spec. In the case of an inner class, the + * {@code InnerClasses} attribute ({@jvms 4.7.9}) is ignored. In contrast, + * {@link Class#getModifiers()} reflects the flags from the {@code InnerClasses} attribute. */ public abstract class ClassInclusionPolicy { + protected BigBang bb; protected final Object reason; @@ -56,204 +55,224 @@ public void setBigBang(BigBang bb) { this.bb = bb; } - public static boolean isClassIncludedBase(Class cls) { - if (Feature.class.isAssignableFrom(cls)) { - return false; - } - - if (AnnotationAccess.isAnnotationPresent(cls, TargetClass.class)) { - return false; - } - try { - Class enclosingClass = cls.getEnclosingClass(); - return enclosingClass == null || isClassIncludedBase(enclosingClass); - } catch (LinkageError e) { - return true; - } - } - /** - * Determine if the given class needs to be included in the image according to the policy. + * Determines if {@code type} needs to be included in the image according to this policy. */ - public boolean isClassIncluded(Class cls) { - Class enclosingClass = cls.getEnclosingClass(); - return isClassIncludedBase(cls) && (enclosingClass == null || isClassIncluded(enclosingClass)); + public boolean isOriginalTypeIncluded(ResolvedJavaType type) { + /* Delegate to host VM for additional checks. */ + return bb.getHostVM().isSupportedOriginalType(bb, type); } /** - * Determine if the given method needs to be included in the image according to the policy. + * Includes the given {@code type} in the image. */ - public boolean isMethodIncluded(Executable method) { - return isMethodIncluded(bb.getMetaAccess().lookupJavaMethod(method)); - } - - public boolean isMethodIncluded(AnalysisMethod method) { - /* - * Methods annotated with @Fold should not be included in the base image as they must be - * inlined. An extension image would inline the method as well and would not use the method - * from the base image. - */ - return !AnnotationAccess.isAnnotationPresent(method, Fold.class); + public void includeType(ResolvedJavaType type) { + AnalysisType aType = asAnalysisType(type); + if (type.isAbstract() || type.isInterface() || type.isPrimitive()) { + /* + * Those types cannot be instantiated. They are instead registered as reachable as they + * can still have methods or fields that could be used by an extension image. + */ + aType.registerAsReachable(reason); + } else { + aType.registerAsInstantiated(reason); + } } /** - * Determine if the given field needs to be included in the image according to the policy. + * Determines if {@code method} needs to be included in the image according to this policy. */ - public boolean isFieldIncluded(Field field) { - if (!bb.getHostVM().platformSupported(field)) { - return false; - } - return bb.getHostVM().isFieldIncluded(bb, field); + public boolean isAnalysisMethodIncluded(AnalysisMethod method) { + /* Delegate to host VM for additional checks. */ + return bb.getHostVM().isSupportedAnalysisMethod(bb, method); } /** - * Determine if the given field needs to be included in the image according to the policy. + * Determines if {@code method} needs to be included in the image according to this policy. */ - public boolean isFieldIncluded(AnalysisField field) { - if (!bb.getHostVM().platformSupported(field)) { - return false; - } - return bb.getHostVM().isFieldIncluded(bb, field); + public boolean isOriginalMethodIncluded(ResolvedJavaMethod method) { + /* Delegate to host VM for additional checks. */ + return bb.getHostVM().isSupportedOriginalMethod(bb, method); } /** - * Includes the given class in the image. + * Includes the given {@code method} in the image. */ - public void includeClass(Class cls) { - /* - * Those classes cannot be registered as allocated as they cannot be instantiated. They are - * instead registered as reachable as they can still have methods or fields that could be - * used by an extension image. - */ - if (Modifier.isAbstract(cls.getModifiers()) || cls.isInterface() || cls.isPrimitive()) { - bb.getMetaAccess().lookupJavaType(cls).registerAsReachable(reason); - } else { - bb.getMetaAccess().lookupJavaType(cls).registerAsInstantiated(reason); - } + public void includeMethod(ResolvedJavaMethod method) { + bb.postTask(debug -> bb.addRootMethod(asAnalysisMethod(method), method.isConstructor(), reason)); } /** - * Includes the given method in the image. + * Determines if {@code field} needs to be included in the image according to this policy. */ - public abstract void includeMethod(Executable method); + public boolean isAnalysisFieldIncluded(AnalysisField field) { + /* Delegate to host VM for additional checks. */ + return bb.getHostVM().isSupportedAnalysisField(bb, field); + } /** - * Includes the given method in the image. + * Determines if {@code field} needs to be included in the image according to this policy. */ - public abstract void includeMethod(AnalysisMethod method); + public boolean isOriginalFieldIncluded(ResolvedJavaField field) { + /* Delegate to host VM for additional checks. */ + return bb.getHostVM().isSupportedOriginalField(bb, field); + } /** - * Includes the given field in the image. + * Includes the given {@code field} in the image. */ - public void includeField(Field field) { - bb.postTask(debug -> bb.addRootField(field)); + public void includeField(ResolvedJavaField field) { + bb.postTask(debug -> bb.addRootField(asAnalysisField(field))); } - /** - * Includes the given field in the image. - */ - public void includeField(AnalysisField field) { - bb.postTask(debug -> bb.addRootField(field)); + AnalysisType asAnalysisType(ResolvedJavaType type) { + return type instanceof AnalysisType aType ? aType : bb.getUniverse().lookup(type); + } + + AnalysisField asAnalysisField(ResolvedJavaField field) { + return field instanceof AnalysisField aField ? aField : bb.getUniverse().lookup(field); + } + + AnalysisMethod asAnalysisMethod(ResolvedJavaMethod method) { + return method instanceof AnalysisMethod aMethod ? aMethod : bb.getUniverse().lookup(method); } /** - * The analysis for the base layer of a layered image assumes that any method that is reachable - * using the base java access rules can be an entry point. An upper layer does not have access - * to the packages from a lower layer. Thus, only the public classes with their public and - * protected inner classes and methods can be accessed by an upper layer. + * The inclusion policy is queried during the class path scanning to determine which code should + * be included in a shared layer. The policy tries to limit the code that we eagerly include by + * eliminating code that wouldn't be directly accessible from an extension layer. *

- * Protected elements from a final or sealed class cannot be accessed as an upper layer cannot - * create a new class that extends the final or sealed class. + * For types and methods it follows the Java language access rules to determine accessibility. + * For example, it includes all {@code public} methods and types, but it doesn't include + * {@code private} methods, although a {@code private} method will still be included if + * reachable from a root. *

- * All the fields are included disregarding access rules as a missing field would cause issues - * in the object layout. + * All methods directly accessible from extension layers must be marked as root to + * ensure the correctness of the analysis. For the other methods, the inclusion policy is only a + * heuristic and the layer in which a method is included will not affect correctness. + *

+ * Contrastingly, all the fields of included types are also included disregarding access rules + * since missing a field would cause inconsistencies in the object layout across layers. + *

+ * Note: For a description of layers structure see {@code ImageLayerBuildingSupport}. */ - public static class LayeredBaseImageInclusionPolicy extends ClassInclusionPolicy { - public LayeredBaseImageInclusionPolicy(Object reason) { + public static class SharedLayerImageInclusionPolicy extends ClassInclusionPolicy { + public SharedLayerImageInclusionPolicy(Object reason) { super(reason); } + /** + * A type from a shared layer is included iff it is public, which at the Java source level + * corresponds to {@code public} for top level types and either {@code public} or + * {@code protected} for inner types. + *

+ * The VM access semantics for inner classes and interfaces differ from the Java language + * semantics. An inner type declared as {@code protected} in the source code has the + * ACC_PUBLIC access flag in the class file. Similarly, an inner type declared as + * {@code private} is treated as a package-private inner type and has no access modifiers + * set in the class file. We follow the VM access semantics for inner types. + */ @Override - public boolean isClassIncluded(Class cls) { - if (!super.isClassIncluded(cls)) { + public boolean isOriginalTypeIncluded(ResolvedJavaType type) { + if (!super.isOriginalTypeIncluded(type)) { return false; } - Class enclosingClass = cls.getEnclosingClass(); - int classModifiers = cls.getModifiers(); - if (enclosingClass != null) { - return isAccessible(enclosingClass, classModifiers) && isClassIncluded(enclosingClass); - } else { - return Modifier.isPublic(classModifiers); + if (!canLinkType(type)) { + return false; } + ResolvedJavaType enclosingType = type.getEnclosingType(); + if (enclosingType != null && !isOriginalTypeIncluded(enclosingType)) { + return false; + } + return type.isPublic(); } - @Override - public boolean isMethodIncluded(Executable method) { - return !Modifier.isAbstract(method.getModifiers()) && isAccessible(method) && super.isMethodIncluded(method); + /** + * Try to link the type. Linking can fail for example if the class path is incomplete. + * Methods of unlinked types cannot be parsed, so we exclude the type early. Linking would + * be triggered later when {@link AnalysisType} is created anyway. + */ + private static boolean canLinkType(ResolvedJavaType type) { + try { + type.link(); + } catch (LinkageError ex) { + return false; + } + return true; } @Override - public void includeMethod(Executable method) { - bb.postTask(debug -> { - Class declaringClass = method.getDeclaringClass(); - AnalysisMethod analysisMethod = bb.getMetaAccess().lookupJavaMethod(method); - registerMethod(method.getModifiers(), declaringClass, analysisMethod); - bb.forcedAddRootMethod(analysisMethod, analysisMethod.isConstructor(), reason); - }); + public boolean isAnalysisMethodIncluded(AnalysisMethod method) { + return super.isAnalysisMethodIncluded(method) && isMethodAccessible(method); } @Override - public void includeMethod(AnalysisMethod method) { - bb.postTask(debug -> { - Class declaringClass = method.getDeclaringClass().getJavaClass(); - registerMethod(method.getModifiers(), declaringClass, method); - bb.forcedAddRootMethod(method, method.isConstructor(), reason); - }); + public boolean isOriginalMethodIncluded(ResolvedJavaMethod method) { + return super.isOriginalMethodIncluded(method) && isMethodAccessible(method); } - private void registerMethod(int methodModifiers, Class declaringClass, AnalysisMethod analysisMethod) { - /* - * Non-abstract methods from an abstract class or default methods from an interface are - * not registered as implementation invoked by the analysis because their declaring - * class cannot be marked as instantiated and AnalysisType.getTypeFlow only includes - * instantiated types (see TypeFlow.addObserver). For now, to ensure those methods are - * included in the image, they are manually registered as implementation invoked. - */ - if (!Modifier.isAbstract(methodModifiers) && (declaringClass.isInterface() || Modifier.isAbstract(declaringClass.getModifiers()))) { - analysisMethod.registerAsDirectRootMethod(reason); - analysisMethod.registerAsImplementationInvoked(reason); + /** + * Determines if {@code method} can be called (without reflection) from an extension layer. + * A public method can always be accessed. A private or package-private method can never be + * accessed as packages are never split between layers. A protected method can only be + * accessed from classes in the same package or subclasses. Since a final class cannot be + * subclassed and the complete closed hierarchy of a sealed class must be in the same layer, + * a protected method in a final or sealed class cannot be accessed from an extension layer. + * In addition, abstract methods cannot be included in the image as they cannot be analyzed + * or compiled. + */ + private static boolean isMethodAccessible(ResolvedJavaMethod method) { + if (method.isAbstract()) { + return false; + } + if (method.isPublic()) { + return true; + } + if (method.isPrivate() || method.isPackagePrivate()) { + return false; } + /* Protected methods from non-final non-sealed classes should be accessible. */ + AnalysisError.guarantee(method.isProtected()); + ResolvedJavaType declaringClass = method.getDeclaringClass(); + return !declaringClass.isFinalFlagSet() && !OriginalClassProvider.getJavaClass(declaringClass).isSealed(); } - } - /** - * The default inclusion policy. Includes all classes and methods. Including all fields causes - * issues at the moment, so the same rules as for the {@link LayeredBaseImageInclusionPolicy} - * are used. - */ - public static class DefaultAllInclusionPolicy extends ClassInclusionPolicy { - public DefaultAllInclusionPolicy(Object reason) { - super(reason); + @Override + public void includeMethod(ResolvedJavaMethod method) { + bb.postTask(debug -> bb.forcedAddRootMethod(asAnalysisMethod(method), method.isConstructor(), reason)); } @Override - public void includeMethod(Executable method) { - bb.postTask(debug -> bb.addRootMethod(method, method instanceof Constructor, reason)); + public boolean isAnalysisFieldIncluded(AnalysisField field) { + return super.isAnalysisFieldIncluded(field) && bb.getHostVM().isFieldIncludedInSharedLayer(field); } @Override - public void includeMethod(AnalysisMethod method) { - bb.postTask(debug -> bb.addRootMethod(method, method.isConstructor(), reason)); + public boolean isOriginalFieldIncluded(ResolvedJavaField field) { + return super.isOriginalFieldIncluded(field) && bb.getHostVM().isFieldIncludedInSharedLayer(field); } } - protected boolean isAccessible(Member member) { - Class cls = member.getDeclaringClass(); - int modifiers = member.getModifiers(); - return isAccessible(cls, modifiers); - } + /** + * The default inclusion policy. Includes all types, methods and fields. + */ + public static class DefaultAllInclusionPolicy extends ClassInclusionPolicy { + public DefaultAllInclusionPolicy(Object reason) { + super(reason); + } - protected boolean isAccessible(Class cls, int modifiers) { - return Modifier.isPublic(modifiers) || (!Modifier.isFinal(cls.getModifiers()) && !cls.isSealed() && Modifier.isProtected(modifiers)); + @Override + public boolean isOriginalTypeIncluded(ResolvedJavaType type) { + if (!super.isOriginalTypeIncluded(type)) { + return false; + } + try { + ResolvedJavaType enclosingType = type.getEnclosingType(); + return enclosingType == null || isOriginalTypeIncluded(enclosingType); + } catch (LinkageError e) { + /* Ignore missing type errors. */ + return true; + } + } } } 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 feaec2e3c99b..e1e70df80547 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 @@ -42,6 +42,8 @@ import java.util.function.Consumer; import java.util.stream.StreamSupport; +import org.graalvm.nativeimage.AnnotationAccess; + import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.constraints.UnsupportedFeatures; @@ -77,6 +79,7 @@ import com.oracle.svm.common.meta.MultiMethod; import com.oracle.svm.util.ClassUtil; +import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.debug.Indent; @@ -347,6 +350,7 @@ public AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial, Ob @Override public AnalysisMethod forcedAddRootMethod(AnalysisMethod method, boolean invokeSpecial, Object reason, MultiMethod.MultiMethodKey... otherRoots) { AnalysisError.guarantee(isBaseLayerAnalysisEnabled()); + registerDefaultMethod(method, reason); PointsToAnalysisMethod analysisMethod = assertPointsToAnalysisMethod(method); postTask(ignore -> { MethodTypeFlow typeFlow = analysisMethod.getTypeFlow(); @@ -359,6 +363,21 @@ public AnalysisMethod forcedAddRootMethod(AnalysisMethod method, boolean invokeS return addRootMethod(analysisMethod, invokeSpecial, reason, otherRoots); } + /** + * Non-abstract methods from an abstract class or default methods from an interface are not + * registered as implementation invoked by the analysis because their declaring class cannot be + * marked as instantiated and {@link AnalysisType#getTypeFlow(BigBang, boolean)} only includes + * instantiated types (see {@link TypeFlow#addObserver(PointsToAnalysis, TypeFlow)}). To ensure + * these methods are included in the image they are manually registered as implementation + * invoked. + */ + private static void registerDefaultMethod(AnalysisMethod method, Object reason) { + if (!method.isAbstract() && (method.getDeclaringClass().isInterface() || method.getDeclaringClass().isAbstract())) { + method.registerAsDirectRootMethod(reason); + method.registerAsImplementationInvoked(reason); + } + } + protected void validateRootMethodRegistration(AnalysisMethod aMethod, boolean invokeSpecial) { if (invokeSpecial && aMethod.isAbstract()) { throw AnalysisError.userError("Abstract methods cannot be registered as special invoke entry points."); @@ -371,6 +390,7 @@ public AnalysisMethod addRootMethod(AnalysisMethod aMethod, boolean invokeSpecia assert !universe.sealed() : "Cannot register root methods after analysis universe is sealed."; validateRootMethodRegistration(aMethod, invokeSpecial); AnalysisError.guarantee(aMethod.isOriginalMethod()); + AnalysisError.guarantee(!AnnotationAccess.isAnnotationPresent(aMethod, Fold.class), "@Fold annotated method cannot be a root method."); boolean isStatic = aMethod.isStatic(); int paramCount = aMethod.getSignature().getParameterCount(!isStatic); PointsToAnalysisMethod originalPTAMethod = assertPointsToAnalysisMethod(aMethod); @@ -517,11 +537,6 @@ public AnalysisType addRootField(Class clazz, String fieldName) { throw shouldNotReachHere("field not found: " + fieldName); } - @Override - public AnalysisType addRootField(Field field) { - return addRootField(getMetaAccess().lookupJavaField(field)); - } - @Override public AnalysisType addRootField(AnalysisField field) { if (field.isStatic()) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java index 61eb2fa1810c..773d59eddf68 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ReachabilityAnalysis.java @@ -25,7 +25,6 @@ package com.oracle.graal.pointsto; import java.lang.reflect.Executable; -import java.lang.reflect.Field; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; @@ -58,8 +57,6 @@ public interface ReachabilityAnalysis { */ AnalysisType addRootField(Class clazz, String fieldName); - AnalysisType addRootField(Field field); - AnalysisType addRootField(AnalysisField field); /** @@ -92,9 +89,7 @@ public interface ReachabilityAnalysis { AnalysisMethod addRootMethod(Executable method, boolean invokeSpecial, Object reason, MultiMethod.MultiMethodKey... otherRoots); /** - * In addition to register the method as a root, saturate all the parameters. Meant to be used - * under the {@code UseBaseLayerInclusionPolicy} option to ensure the invocation is replaced by - * the context-insensitive invoke. + * In addition to registering the method as a root, saturate all the parameters. * * @see ReachabilityAnalysis#addRootMethod(AnalysisMethod, boolean, Object, * MultiMethod.MultiMethodKey...) 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 4b4d4d161abe..5b46d74892db 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 @@ -27,7 +27,6 @@ package com.oracle.graal.pointsto.api; import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -63,6 +62,7 @@ import jdk.graal.compiler.options.OptionValues; import jdk.graal.compiler.phases.OptimisticOptimizations; import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -371,19 +371,48 @@ public HostedProviders getProviders(MultiMethod.MultiMethodKey key) { } /** - * This method should only be used by the {@code ClassInclusionPolicy} to determine which fields - * should be included in the shared layer. + * Determine if the type is supported by the host VM and should be processed by the analysis. */ @SuppressWarnings("unused") - public boolean isFieldIncluded(BigBang bb, Field field) { + public boolean isSupportedOriginalType(BigBang bb, ResolvedJavaType type) { return true; } /** - * See {@link HostVM#isFieldIncluded(BigBang, Field)}. + * Determine if the method is supported by the host VM and should be processed by the analysis. */ @SuppressWarnings("unused") - public boolean isFieldIncluded(BigBang bb, AnalysisField field) { + public boolean isSupportedAnalysisMethod(BigBang bb, AnalysisMethod method) { + return true; + } + + /** + * Determine if the method is supported by the host VM and should be processed by the analysis. + */ + @SuppressWarnings("unused") + public boolean isSupportedOriginalMethod(BigBang bb, ResolvedJavaMethod method) { + return true; + } + + /** + * Determine if the field is supported by the host VM and should be processed by the analysis. + */ + @SuppressWarnings("unused") + public boolean isSupportedAnalysisField(BigBang bb, AnalysisField field) { + return true; + } + + /** + * Determine if the field is supported by the host VM and should be processed by the analysis. + */ + @SuppressWarnings("unused") + public boolean isSupportedOriginalField(BigBang bb, ResolvedJavaField field) { + return true; + } + + /** Determine if field should be included in the shared layer. */ + @SuppressWarnings("unused") + public boolean isFieldIncludedInSharedLayer(ResolvedJavaField field) { return true; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalClassProvider.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalClassProvider.java index 9bc69a2f9162..121be3fd55a8 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalClassProvider.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalClassProvider.java @@ -31,6 +31,9 @@ import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.ResolvedJavaType; +/** + * A wrapper type that can be unwrapped to an original host VM type. + */ public interface OriginalClassProvider { /** diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalFieldProvider.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalFieldProvider.java index 63e394ab8778..aa990bb7e401 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalFieldProvider.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalFieldProvider.java @@ -30,6 +30,9 @@ import jdk.vm.ci.meta.ResolvedJavaField; +/** + * A wrapper field that can be unwrapped to an original host VM field. + */ public interface OriginalFieldProvider { /** diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalMethodProvider.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalMethodProvider.java index 059d9317dd7d..949a22601cc3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalMethodProvider.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalMethodProvider.java @@ -31,6 +31,9 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; +/** + * A wrapper method that can be unwrapped to an original host VM method. + */ public interface OriginalMethodProvider { /** diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java index e7c22c5d102f..015a07852c36 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java @@ -397,8 +397,8 @@ private boolean processReason(Object current, String prefix) { reasonStr = ((ResolvedJavaMethod) current).format("%f method %H.%n"); } else if (current instanceof ResolvedJavaField field) { - /** - * In {@link AnalysisUniverse#lookupAllowUnresolved(JavaField)} we may register a + /* + * In {@code AnalysisUniverse#lookupAllowUnresolved(JavaField)} we may register a * ResolvedJavaField as reason. * * We convert it to AnalysisField to print more information about why the field is 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 850a631db0a3..85927f9144fe 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 @@ -83,10 +83,6 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav private static final AtomicReferenceFieldUpdater UNIQUE_CONSTANT_UPDATER = // AtomicReferenceFieldUpdater.newUpdater(AnalysisType.class, AnalysisObject.class, "uniqueConstant"); - @SuppressWarnings("rawtypes")// - private static final AtomicReferenceFieldUpdater INTERCEPTORS_UPDATER = // - AtomicReferenceFieldUpdater.newUpdater(AnalysisType.class, Object.class, "interceptors"); - private static final AtomicReferenceFieldUpdater subtypeReachableNotificationsUpdater = AtomicReferenceFieldUpdater .newUpdater(AnalysisType.class, Object.class, "subtypeReachableNotifications"); @@ -1168,18 +1164,14 @@ public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expec * * Although all elements are of type AnalysisField, we set this array to be of type * ResolvedJavaField so that runtime compilation does not need to convert the array type. + * Resolving GR-66575 will allow these arrays to be of type {@code AnalysisField[]}. */ private volatile ResolvedJavaField[] instanceFieldsWithSuper; private volatile ResolvedJavaField[] instanceFieldsWithoutSuper; - public void clearInstanceFieldsCache() { - instanceFieldsWithSuper = null; - instanceFieldsWithoutSuper = null; - } - /** * Note that although this returns a ResolvedJavaField[], all instance fields are of type - * AnalysisField and can be casted to AnalysisField without problem. + * AnalysisField and can be cast to AnalysisField without problem. */ @Override public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) { @@ -1196,7 +1188,7 @@ private ResolvedJavaField[] initializeInstanceFields(boolean includeSuperclasses if (includeSuperclasses && getSuperclass() != null) { list.addAll(Arrays.asList(getSuperclass().getInstanceFields(true))); } - ResolvedJavaField[] result = convertFields(interceptInstanceFields(wrapped.getInstanceFields(false)), list, includeSuperclasses); + ResolvedJavaField[] result = convertFields(wrapped.getInstanceFields(false), list, includeSuperclasses); if (includeSuperclasses) { instanceFieldsWithSuper = result; } else { @@ -1288,13 +1280,13 @@ public AnalysisType getEnclosingType() { } @Override - public ResolvedJavaMethod[] getDeclaredMethods() { + public AnalysisMethod[] getDeclaredMethods() { return getDeclaredMethods(true); } @Override public AnalysisMethod[] getDeclaredMethods(boolean forceLink) { - GraalError.guarantee(forceLink == false, "only use getDeclaredMethods without forcing to link, because linking can throw LinkageError"); + GraalError.guarantee(!forceLink, "only use getDeclaredMethods without forcing to link, because linking can throw LinkageError"); AnalysisMethod[] result = declaredMethods; if (result == null) { result = universe.lookup(wrapped.getDeclaredMethods(forceLink)); @@ -1314,7 +1306,7 @@ public List getAllMethods(boolean forceLink) { } @Override - public ResolvedJavaMethod[] getDeclaredConstructors() { + public AnalysisMethod[] getDeclaredConstructors() { return getDeclaredConstructors(true); } @@ -1442,38 +1434,13 @@ public int compareTo(AnalysisType other) { } @Override - public int hashCode() { + public final int hashCode() { assert id != 0 || isJavaLangObject() : "Type id not set yet"; return id; } @Override - public boolean equals(Object obj) { + public final boolean equals(Object obj) { return this == obj; } - - /* Value copied from java.lang.Class. */ - private static final int ANNOTATION = 0x00002000; - - /* Method copied from java.lang.Class. */ - @Override - public boolean isAnnotation() { - return (getModifiers() & ANNOTATION) != 0; - } - - public void addInstanceFieldsInterceptor(InstanceFieldsInterceptor interceptor) { - ConcurrentLightHashSet.addElement(this, INTERCEPTORS_UPDATER, interceptor); - } - - private ResolvedJavaField[] interceptInstanceFields(ResolvedJavaField[] fields) { - ResolvedJavaField[] result = fields; - for (Object interceptor : ConcurrentLightHashSet.getElements(this, INTERCEPTORS_UPDATER)) { - result = ((InstanceFieldsInterceptor) interceptor).interceptInstanceFields(universe, result, this); - } - return result; - } - - public interface InstanceFieldsInterceptor { - ResolvedJavaField[] interceptInstanceFields(AnalysisUniverse universe, ResolvedJavaField[] fields, AnalysisType type); - } } diff --git a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java index 93d48c0162b9..909d65b3dd51 100644 --- a/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java +++ b/substratevm/src/com.oracle.graal.reachability/src/com/oracle/graal/reachability/ReachabilityAnalysisEngine.java @@ -25,7 +25,6 @@ package com.oracle.graal.reachability; import java.lang.reflect.Executable; -import java.lang.reflect.Field; import java.util.ArrayDeque; import java.util.Collections; import java.util.Deque; @@ -149,12 +148,6 @@ public AnalysisType addRootField(Class clazz, String fieldName) { throw AnalysisError.userError("Field not found: " + fieldName); } - @Override - public AnalysisType addRootField(Field field) { - AnalysisField analysisField = getMetaAccess().lookupJavaField(field); - return addRootField(analysisField); - } - @Override public AnalysisType addRootField(AnalysisField analysisField) { analysisField.registerAsAccessed("root field"); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 934259f4c1c2..e3f905578b91 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -168,7 +168,7 @@ protected void onValueUpdate(EconomicMap, Object> values, Boolean o public static final String LAYER_OPTION_PREFIX = "-H:Layer"; // "--layer" public static final String LAYER_CREATE_OPTION = LAYER_OPTION_PREFIX + "Create"; // "-create" // @APIOption(name = LAYER_CREATE_OPTION) // use when non-experimental - @Option(help = "Experimental: Build a Native Image layer.")// + @Option(help = "Experimental: Build a Native Image layer. See NativeImageLayers.md for more info.")// public static final HostedOptionKey LayerCreate = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.build()); // public static final String LAYER_USE_OPTION = LAYER_OPTION_PREFIX + "-use"; @@ -1455,8 +1455,6 @@ public enum ReportingMode { @Option(help = "Ignore classes or packages (comma separated) from the ones included with '-H:Preserve'. This can be used to workaround potential issues related to '-H:Preserve'.", type = OptionType.Debug) // public static final HostedOptionKey IgnorePreserveForClasses = new HostedOptionKey<>( AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); - @Option(help = "Force include include all public types and methods that can be reached using normal Java access rules.")// - public static final HostedOptionKey UseBaseLayerInclusionPolicy = new HostedOptionKey<>(false); @Option(help = "Support for calls via the Java Foreign Function and Memory API", type = Expert) // public static final HostedOptionKey ForeignAPISupport = new HostedOptionKey<>(true); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java index 3093b1972932..f23022ef515f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java @@ -47,7 +47,7 @@ /** * Substitutions in this class are required to adapt the JDK encoding for annotations to our - * modified version of it. See {@code ReflectionMetadataEncoderImpl.encodeAnnotations()} for a + * modified version of it. See {@code RuntimeMetadataEncoderImpl.encodeAnnotations()} for a * description of the changes and the rationale behind them. */ @TargetClass(AnnotationParser.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 4156a15ebb4e..89bc68ab4521 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 @@ -25,6 +25,7 @@ package com.oracle.svm.hosted; import static com.oracle.graal.pointsto.api.PointstoOptions.UseExperimentalReachabilityAnalysis; +import static com.oracle.svm.core.SubstrateOptions.LayerCreate; import static com.oracle.svm.hosted.NativeImageOptions.DiagnosticsDir; import static com.oracle.svm.hosted.NativeImageOptions.DiagnosticsMode; import static jdk.graal.compiler.hotspot.JVMCIVersionCheck.OPEN_LABSJDK_RELEASE_URL_PATTERN; @@ -32,7 +33,6 @@ import java.io.IOException; import java.lang.annotation.Annotation; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.file.Files; @@ -321,6 +321,7 @@ import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.riscv64.RISCV64; @@ -1108,9 +1109,9 @@ protected void setupNativeImage(OptionValues options, Map bb.registerTypeForBaseImage(cls)); + loader.classLoaderSupport.getClassesToIncludeUnconditionally().forEach(clazz -> bb.tryRegisterTypeForBaseImage(originalMetaAccess.lookupJavaType(clazz))); if (loader.classLoaderSupport.isPreserveMode()) { - PreserveOptionsSupport.registerPreservedClasses(loader.classLoaderSupport); + PreserveOptionsSupport.registerPreservedClasses(bb, originalMetaAccess, loader.classLoaderSupport); } registerEntryPointStubs(entryPoints); @@ -1342,8 +1343,9 @@ private static Inflation createBigBang(DebugContext debug, OptionValues options, SnippetReflectionProvider snippetReflectionProvider = aProviders.getSnippetReflection(); ConstantReflectionProvider constantReflectionProvider = aProviders.getConstantReflection(); WordTypes wordTypes = aProviders.getWordTypes(); - String reason = "Included in the base image"; - ClassInclusionPolicy classInclusionPolicy = SubstrateOptions.UseBaseLayerInclusionPolicy.getValue(options) ? new ClassInclusionPolicy.LayeredBaseImageInclusionPolicy(reason) + String reason = "included by " + SubstrateOptionsParser.commandArgument(LayerCreate, ""); + ClassInclusionPolicy classInclusionPolicy = ImageLayerBuildingSupport.buildingSharedLayer() + ? new ClassInclusionPolicy.SharedLayerImageInclusionPolicy(reason) : new ClassInclusionPolicy.DefaultAllInclusionPolicy(reason); if (PointstoOptions.UseExperimentalReachabilityAnalysis.getValue(options)) { ReachabilityMethodProcessingHandler reachabilityMethodProcessingHandler; @@ -1758,17 +1760,12 @@ public static void checkName(BigBang bb, AnalysisMethod method) { checkName(bb, method, format); } - public static void checkName(BigBang bb, AnalysisField field) { + public static void checkName(BigBang bb, ResolvedJavaField field) { String format = field.format("%H.%n"); checkName(bb, null, format); } - public static void checkName(BigBang bb, Field field) { - String format = field.getType().getName() + "." + field.getName(); - checkName(bb, null, format); - } - - public static void checkName(BigBang bb, AnalysisType type) { + public static void checkName(BigBang bb, ResolvedJavaType type) { String format = type.toJavaName(true); checkName(bb, null, format); } @@ -1782,19 +1779,19 @@ public static void checkName(BigBang bb, AnalysisType type) { "sun.lwawt.macosx.CCustomCursor.getHotSpot()", "sun.awt.shell.Win32ShellFolder2.ATTRIB_GHOSTED"); - private static void checkName(BigBang bb, AnalysisMethod method, String format) { + private static void checkName(BigBang bb, AnalysisMethod method, String name) { /* * We do not want any parts of the native image generator in the generated image. Therefore, * no element whose name contains "hosted" must be seen as reachable by the static analysis. * The same holds for "host VM" elements, which come from the hosting VM, unless they are * JDK internal types. */ - String lformat = format.toLowerCase(Locale.ROOT); - if (!CHECK_NAMING_EXCEPTIONS.contains(format)) { - if (lformat.contains("hosted")) { - report(bb, format, method, "Hosted element used at run time: " + format + namingConventionsErrorMessageSuffix("hosted")); - } else if (!lformat.startsWith("jdk.internal") && lformat.contains("hotspot")) { - report(bb, format, method, "Element with HotSpot in its name used at run time: " + format + namingConventionsErrorMessageSuffix("HotSpot")); + String lcName = name.toLowerCase(Locale.ROOT); + if (!CHECK_NAMING_EXCEPTIONS.contains(name)) { + if (lcName.contains("hosted")) { + report(bb, name, method, "Hosted element used at run time: " + name + namingConventionsErrorMessageSuffix("hosted")); + } else if (!lcName.startsWith("jdk.internal") && lcName.contains("hotspot")) { + report(bb, name, method, "Element with HotSpot in its name used at run time: " + name + namingConventionsErrorMessageSuffix("HotSpot")); } } } 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 afca15548259..77d30152be52 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 @@ -26,7 +26,6 @@ import java.lang.ref.Reference; import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -51,6 +50,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.BigBang; @@ -60,6 +60,7 @@ import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; +import com.oracle.graal.pointsto.infrastructure.OriginalMethodProvider; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; @@ -69,6 +70,7 @@ import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisGraphDecoder; import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisPolicy; +import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.common.meta.GuaranteeFolded; import com.oracle.svm.common.meta.MultiMethod; @@ -81,7 +83,7 @@ import com.oracle.svm.core.SubstrateOptions.OptimizationLevel; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.InjectAccessors; -import com.oracle.svm.core.c.CGlobalData; +import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.encoder.SymbolEncoder; import com.oracle.svm.core.graal.meta.SubstrateForeignCallLinkage; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; @@ -109,7 +111,6 @@ import com.oracle.svm.core.util.HostedStringDeduplication; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.analysis.NativeImagePointsToAnalysis; import com.oracle.svm.hosted.analysis.SVMParsingSupport; import com.oracle.svm.hosted.classinitialization.ClassInitializationFeature; import com.oracle.svm.hosted.classinitialization.ClassInitializationOptions; @@ -139,6 +140,7 @@ import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor; import jdk.graal.compiler.core.common.spi.ForeignCallsProvider; import jdk.graal.compiler.debug.DebugContext; @@ -162,6 +164,7 @@ import jdk.internal.vm.annotation.DontInline; import jdk.vm.ci.meta.DeoptimizationReason; import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -169,10 +172,11 @@ public class SVMHost extends HostVM { private final ConcurrentHashMap typeToHub = new ConcurrentHashMap<>(); private final ConcurrentHashMap hubToType = new ConcurrentHashMap<>(); + private final boolean verifyNamingConventions; public enum UsageKind { Instantiated, - Reachable; + Reachable } private final Map> forbiddenTypes; @@ -201,15 +205,18 @@ public enum UsageKind { private final SVMParsingSupport parsingSupport; private final InlineBeforeAnalysisPolicy inlineBeforeAnalysisPolicy; + private final AnnotationSubstitutionProcessor annotationSubstitutions; private final MissingRegistrationSupport missingRegistrationSupport; private final SymbolEncoder encoder = SymbolEncoder.singleton(); private final int layerId; private final boolean useBaseLayer; - private Set excludedFields; - private AnalysisType cGlobalData; - private AnalysisType optionKey; + + // All elements below are from the host VM universe, not the analysis universe + private Set sharedLayerExcludedFields; + private final ResolvedJavaType optionKeyType; + private final ResolvedJavaType featureType; private final Boolean optionAllowUnsafeAllocationOfAllInstantiatedTypes = SubstrateOptions.AllowUnsafeAllocationOfAllInstantiatedTypes.getValue(); private final boolean isClosedTypeWorld = SubstrateOptions.useClosedTypeWorld(); @@ -217,6 +224,7 @@ public enum UsageKind { private final boolean enableReachableInCurrentLayer; private final boolean buildingImageLayer = ImageLayerBuildingSupport.buildingImageLayer(); private final LayeredStaticFieldSupport layeredStaticFieldSupport; + private final MetaAccessProvider originalMetaAccess; @SuppressWarnings("this-escape") public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializationSupport classInitializationSupport, AnnotationSubstitutionProcessor annotationSubstitutions, @@ -224,7 +232,9 @@ public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializatio super(options, loader.getClassLoader()); this.loader = loader; this.classInitializationSupport = classInitializationSupport; + this.annotationSubstitutions = annotationSubstitutions; this.missingRegistrationSupport = missingRegistrationSupport; + this.originalMetaAccess = GraalAccess.getOriginalProviders().getMetaAccess(); this.stringTable = HostedStringDeduplication.singleton(); this.forbiddenTypes = setupForbiddenTypes(options); this.automaticUnsafeTransformations = new AutomaticUnsafeTransformationSupport(options, annotationSubstitutions, loader); @@ -248,12 +258,17 @@ public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializatio layerId = ImageLayerBuildingSupport.buildingImageLayer() ? DynamicImageLayerInfo.getCurrentLayerNumber() : 0; useBaseLayer = ImageLayerBuildingSupport.buildingExtensionLayer(); if (ImageLayerBuildingSupport.buildingSharedLayer()) { - initializeExcludedFields(); + initializeSharedLayerExcludedFields(); } enableTrackAcrossLayers = ImageLayerBuildingSupport.buildingSharedLayer(); enableReachableInCurrentLayer = ImageLayerBuildingSupport.buildingExtensionLayer(); layeredStaticFieldSupport = ImageLayerBuildingSupport.buildingImageLayer() ? LayeredStaticFieldSupport.singleton() : null; + + optionKeyType = lookupOriginalType(OptionKey.class); + featureType = lookupOriginalType(Feature.class); + + verifyNamingConventions = SubstrateOptions.VerifyNamingConventions.getValue(); } /** @@ -414,7 +429,7 @@ public void onTypeReachable(BigBang bb, AnalysisType analysisType) { classInitializationSupport.maybeInitializeAtBuildTime(analysisType); /* Compute the automatic substitutions. */ - automaticUnsafeTransformations.computeTransformations(bb, this, GraalAccess.getOriginalProviders().getMetaAccess().lookupJavaType(analysisType.getJavaClass())); + automaticUnsafeTransformations.computeTransformations(bb, this, lookupOriginalType(analysisType.getJavaClass())); } @Override @@ -915,37 +930,53 @@ public boolean platformSupported(AnnotatedElement element) { return false; } - private void initializeExcludedFields() { - excludedFields = new HashSet<>(); + /** + * Gets a {@link ResolvedJavaType} for {@code clazz} from the host VM universe. + */ + private ResolvedJavaType lookupOriginalType(Class clazz) { + return originalMetaAccess.lookupJavaType(clazz); + } + + /** + * Gets a {@link ResolvedJavaField} declared in {@code declaringClass} from the host VM + * universe. + */ + private ResolvedJavaField lookupOriginalDeclaredField(Class declaringClass, String fieldName) { + return originalMetaAccess.lookupJavaField(ReflectionUtil.lookupField(declaringClass, fieldName)); + } + + private void initializeSharedLayerExcludedFields() { + sharedLayerExcludedFields = new HashSet<>(); /* * These fields need to be folded as they are used in snippets, and they must be accessed * without producing reads with side effects. */ - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "layoutEncoding")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "numClassTypes")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "numInterfaceTypes")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "openTypeWorldTypeCheckSlots")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "typeIDDepth")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "typeID")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "monitorOffset")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "hubType")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "companion")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHubCompanion.class, "arrayHub")); - excludedFields.add(ReflectionUtil.lookupField(DynamicHubCompanion.class, "additionalFlags")); + + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "layoutEncoding")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "numClassTypes")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "numInterfaceTypes")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "openTypeWorldTypeCheckSlots")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "typeIDDepth")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "typeID")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "monitorOffset")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "hubType")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "companion")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHubCompanion.class, "arrayHub")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHubCompanion.class, "additionalFlags")); /* Needs to be immutable for correct lowering of SubstrateIdentityHashCodeNode. */ - excludedFields.add(ReflectionUtil.lookupField(DynamicHub.class, "identityHashOffset")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(DynamicHub.class, "identityHashOffset")); /* * Including this field makes ThreadLocalAllocation.getTlabDescriptorSize reachable through * ThreadLocalAllocation.regularTLAB which is accessed with * FastThreadLocalBytes.getSizeSupplier */ - excludedFields.add(ReflectionUtil.lookupField(VMThreadLocalInfo.class, "sizeSupplier")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(VMThreadLocalInfo.class, "sizeSupplier")); /* This field cannot be written to (see documentation) */ - excludedFields.add(ReflectionUtil.lookupField(Counter.Group.class, "enabled")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(Counter.Group.class, "enabled")); /* This field can contain a reference to a Thread, which is not allowed in the heap */ - excludedFields.add(ReflectionUtil.lookupField(NativeLibraries.class, "nativeLibraryLockMap")); + sharedLayerExcludedFields.add(lookupOriginalDeclaredField(NativeLibraries.class, "nativeLibraryLockMap")); } @Override @@ -957,103 +988,180 @@ public boolean sortFields() { return ImageLayerBuildingSupport.buildingImageLayer(); } + /** If it's not one of the known builder types it must be an original VM type. */ + private static boolean isOriginalType(ResolvedJavaType type) { + return !(type instanceof OriginalClassProvider); + } + + /** If it's not one of the known builder methods it must be an original VM method. */ + private static boolean isOriginalMethod(ResolvedJavaMethod method) { + return !(method instanceof OriginalMethodProvider); + } + + /** If it's not one of the known builder fields it must be an original VM fields. */ + private static boolean isOriginalField(ResolvedJavaField field) { + return !(field instanceof OriginalFieldProvider); + } + + /** + * Check if an original {@link ResolvedJavaType} should be included in the image. This method + * should not be called with an {@link AnalysisType} or any other SVM specific types. + */ + @Override + public boolean isSupportedOriginalType(BigBang bb, ResolvedJavaType type) { + AnalysisError.guarantee(isOriginalType(type), "Expected an original VM type, found %s", type.getClass()); + + if (!platformSupported(type)) { + return false; + } + + if (featureType.isAssignableFrom(type)) { + return false; + } + + /* Substitution types should never be reachable directly. */ + if (AnnotationAccess.isAnnotationPresent(type, TargetClass.class)) { + return false; + } + + return super.isSupportedOriginalType(bb, type); + } + /** - * This method cannot use an {@link AnalysisField} because it is used before the analysis is set - * up. + * Check if an {@link AnalysisMethod} should be included in the image. For checking its + * annotations we rely on the {@link AnnotationAccess} unwrapping mechanism to include any + * annotations injected in the substitution layer. */ @Override - public boolean isFieldIncluded(BigBang bb, Field field) { + public boolean isSupportedAnalysisMethod(BigBang bb, AnalysisMethod method) { + if (!platformSupported(method)) { + return false; + } /* - * Fields of type CGlobalData can use a CGlobalDataFactory which must not be reachable at - * run time + * Methods annotated with @Fold should not be included in the base image as they are + * replaced by the invocation plugin with a constant. If reachable in an extension image, + * the plugin will replace it again. */ - if (field.getType().equals(CGlobalData.class)) { + if (AnnotationAccess.isAnnotationPresent(method, Fold.class)) { + return false; + } + return super.isSupportedAnalysisMethod(bb, method); + } + + /** + * Check if an original host VM method should be included in the image. This allows us to create + * the corresponding {@link AnalysisMethod} lazily, only if it needs to be analyzed. + *

+ * There is some redundancy with {@link #isSupportedAnalysisMethod(BigBang, AnalysisMethod)}, + * however we keep them separated due to the particularities of the substitution layer. + * Annotations of {@link AnalysisMethod}s can be queried directly, but for original host VM + * methods we need to go via the corresponding substitution method, if any, since the + * substitution mechanism can inject annotations in the original methods, like @{@link Fold}. + */ + @Override + public boolean isSupportedOriginalMethod(BigBang bb, ResolvedJavaMethod method) { + AnalysisError.guarantee(isOriginalMethod(method), "Expected an original VM method, found %s", method.getClass()); + + if (!platformSupported(method)) { + return false; + } + + /* If the method is substituted we need to check the substitution layer for @Fold. */ + ResolvedJavaMethod substitutionMethod = bb.getUniverse().getSubstitutions().lookup(method); + if (AnnotationAccess.isAnnotationPresent(substitutionMethod, Fold.class)) { return false; } + return super.isSupportedOriginalMethod(bb, method); + } - if (excludedFields.contains(field)) { + /** + * Check if an {@link AnalysisField} should be included in the image. For checking its + * annotations we rely on the {@link AnnotationAccess} unwrapping mechanism to include any + * annotations injected in the substitution layer. + */ + @Override + public boolean isSupportedAnalysisField(BigBang bb, AnalysisField field) { + if (!platformSupported(field)) { return false; } + /* Options should not be in the image */ - if (OptionKey.class.isAssignableFrom(field.getType())) { + if (optionKeyType.isAssignableFrom(OriginalClassProvider.getOriginalType(field.getType()))) { return false; } /* Fields that are always folded don't need to be included. */ - if (field.getAnnotation(GuaranteeFolded.class) != null) { + if (field.isGuaranteeFolded()) { return false; } - /* Fields from this package should not be in the image */ - if (field.getDeclaringClass().getName().startsWith("jdk.graal.compiler")) { + /* Fields that are deleted or substituted should not be in the image. */ + if (field.getAnnotation(Delete.class) != null || field.getAnnotation(InjectAccessors.class) != null) { return false; } - /* Fields that are deleted or substituted should not be in the image */ - if (bb instanceof NativeImagePointsToAnalysis nativeImagePointsToAnalysis) { - AnnotationSubstitutionProcessor annotationSubstitutionProcessor = nativeImagePointsToAnalysis.getAnnotationSubstitutionProcessor(); - boolean included = !annotationSubstitutionProcessor.isDeleted(field) && !annotationSubstitutionProcessor.isAnnotationPresent(field, InjectAccessors.class); - if (!included) { - return false; - } - } /* Remaining fields should match the naming conventions. */ - if (SubstrateOptions.VerifyNamingConventions.getValue()) { + if (verifyNamingConventions) { NativeImageGenerator.checkName(bb, field); } - return super.isFieldIncluded(bb, field); + return super.isSupportedAnalysisField(bb, field); } /** - * This method needs to be kept in sync with {@link SVMHost#isFieldIncluded(BigBang, Field)}. + * Check if an original host VM field should be included in the image. This allows us to create + * the corresponding {@link AnalysisField} lazily, only if it needs to be analyzed + *

+ * There is some redundancy with {@link #isSupportedAnalysisField(BigBang, AnalysisField)}, + * however we keep them separated due to the particularities of the substitution layer. + * Annotations of {@link AnalysisField}s can be queried directly, but for original host VM + * fields we need to go via the corresponding substitution field, if any, since the substitution + * mechanism can inject annotations in the original fields, like @{@link InjectAccessors}. */ @Override - public boolean isFieldIncluded(BigBang bb, AnalysisField field) { - /* - * Fields of type CGlobalData can use a CGlobalDataFactory which must not be reachable at - * run time - */ - if (cGlobalData == null) { - cGlobalData = bb.getMetaAccess().lookupJavaType(CGlobalData.class); - } - if (field.getType().equals(cGlobalData)) { - return false; - } + public boolean isSupportedOriginalField(BigBang bb, ResolvedJavaField field) { + AnalysisError.guarantee(isOriginalField(field), "Expected an original VM field, found %s", field.getClass()); - if (excludedFields.contains(OriginalFieldProvider.getJavaField(field))) { + if (!platformSupported(field) || field.isInternal()) { return false; } /* Options should not be in the image */ - if (optionKey == null) { - optionKey = bb.getMetaAccess().lookupJavaType(OptionKey.class); - } - if (optionKey.isAssignableFrom(field.getType())) { + if (field.getType() instanceof ResolvedJavaType fieldType && optionKeyType.isAssignableFrom(fieldType)) { return false; } /* Fields that are always folded don't need to be included. */ - if (field.isGuaranteeFolded()) { + if (AnnotationAccess.isAnnotationPresent(field, GuaranteeFolded.class)) { return false; } - /* Fields from this package should not be in the image */ - if (field.getDeclaringClass().toJavaName().startsWith("jdk.graal.compiler")) { - return false; - } - /* Fields that are deleted or substituted should not be in the image */ - boolean included = field.getAnnotation(Delete.class) == null && field.getAnnotation(InjectAccessors.class) == null; - if (!included) { + /* Fields that are deleted or substituted should not be in the image. */ + if (annotationSubstitutions.isDeleted(field) || annotationSubstitutions.hasInjectAccessors(field)) { return false; } - /* Remaining fields should match the naming conventions. */ - if (SubstrateOptions.VerifyNamingConventions.getValue()) { + if (verifyNamingConventions) { NativeImageGenerator.checkName(bb, field); } - return super.isFieldIncluded(bb, field); + return super.isSupportedOriginalField(bb, field); + } + + /** + * Checks the exclusion list to determine if field should be included in the shared layer. + */ + @Override + public boolean isFieldIncludedInSharedLayer(ResolvedJavaField field) { + if (sharedLayerExcludedFields.contains(OriginalFieldProvider.getOriginalField(field))) { + return false; + } + + /* Fields from the Graal compiler should not be in the shared layer unconditionally. */ + if (field.getDeclaringClass().toJavaName().startsWith("jdk.graal.compiler")) { + return false; + } + return true; } @Override 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 c08ebd6e0b04..3838247295b0 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 @@ -27,10 +27,8 @@ import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Arrays; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Stream; import com.oracle.graal.pointsto.ClassInclusionPolicy; import com.oracle.graal.pointsto.PointsToAnalysis; @@ -51,7 +49,6 @@ import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.code.IncompatibleClassChangeFallbackMethod; import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; -import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.util.LogUtils; @@ -61,7 +58,7 @@ import jdk.graal.compiler.word.WordTypes; import jdk.vm.ci.code.BytecodePosition; import jdk.vm.ci.meta.ConstantReflectionProvider; -import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.Signature; public class NativeImagePointsToAnalysis extends PointsToAnalysis implements Inflation { @@ -154,17 +151,14 @@ public void onTypeReachable(AnalysisType type) { } if (ImageLayerBuildingSupport.buildingSharedLayer()) { /* - * Using getInstanceFields and getStaticFields allows to include the fields from the - * substitution class. + * Register open-world fields as roots to prevent premature optimizations, i.e., + * like constant-folding their values in the shared layer. */ - Stream.concat(Arrays.stream(getOrDefault(type, t -> t.getInstanceFields(true), new AnalysisField[0])), - Arrays.stream(getOrDefault(type, AnalysisType::getStaticFields, new AnalysisField[0]))) - .filter(field -> field != null && classInclusionPolicy.isFieldIncluded((AnalysisField) field)) - .forEach(field -> classInclusionPolicy.includeField((AnalysisField) field)); + tryRegisterFieldsInBaseImage(type.getInstanceFields(true)); + tryRegisterFieldsInBaseImage(type.getStaticFields()); /* - * Only the class initializers that are executed at run time should be included in - * the base layer. + * Register run time executed class initializers as roots in the base layer. */ AnalysisMethod classInitializer = type.getClassInitializer(); if (classInitializer != null && !ClassInitializationSupport.singleton().maybeInitializeAtBuildTime(type) && classInitializer.getCode() != null) { @@ -174,21 +168,17 @@ public void onTypeReachable(AnalysisType type) { }); } + private void tryRegisterFieldsInBaseImage(ResolvedJavaField[] fields) { + for (ResolvedJavaField resolvedJavaField : fields) { + tryRegisterFieldForBaseImage((AnalysisField) resolvedJavaField); + } + } + @Override public void initializeMetaData(AnalysisType type) { dynamicHubInitializer.initializeMetaData(universe.getHeapScanner(), type); } - public static ResolvedJavaType toWrappedType(ResolvedJavaType type) { - if (type instanceof AnalysisType) { - return ((AnalysisType) type).getWrapped(); - } else if (type instanceof HostedType) { - return ((HostedType) type).getWrapped().getWrapped(); - } else { - return type; - } - } - @Override protected void validateRootMethodRegistration(AnalysisMethod aMethod, boolean invokeSpecial) { super.validateRootMethodRegistration(aMethod, invokeSpecial); 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 a86970b78f29..8b21752bd516 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 @@ -100,7 +100,7 @@ public AnalysisMethod lookup(AnalysisMetaAccess aMetaAccess, AnalysisMethod aCon AnalysisMethod aMethod = aMetaAccess.getUniverse().lookup(factoryMethod); if (HostedImageLayerBuildingSupport.buildingSharedLayer()) { - aMetaAccess.getUniverse().getBigbang().registerMethodForBaseImage(aMethod); + aMetaAccess.getUniverse().getBigbang().tryRegisterMethodForBaseImage(aMethod); } return aMethod; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java index fac075df54c5..d5eb2283e67a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java @@ -55,7 +55,9 @@ import org.graalvm.nativeimage.impl.RuntimeResourceSupport; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; +import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.ClassInclusionPolicy; +import com.oracle.graal.pointsto.ClassInclusionPolicy.DefaultAllInclusionPolicy; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.jdk.localization.BundleContentSubstitutedLocalizationSupport; import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; @@ -68,6 +70,7 @@ import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionValues; +import jdk.vm.ci.meta.MetaAccessProvider; public class PreserveOptionsSupport extends IncludeOptionsSupport { @@ -181,10 +184,12 @@ private static String getSecurityProvidersCSV() { return joiner.toString(); } - public static void registerPreservedClasses(NativeImageClassLoaderSupport classLoaderSupport) { + public static void registerPreservedClasses(BigBang bb, MetaAccessProvider originalMetaAccess, NativeImageClassLoaderSupport classLoaderSupport) { var classesOrPackagesToIgnore = SubstrateOptions.IgnorePreserveForClasses.getValue().valuesAsSet(); + ClassInclusionPolicy classInclusionPolicy = new DefaultAllInclusionPolicy("included by " + SubstrateOptionsParser.commandArgument(Preserve, "")); + classInclusionPolicy.setBigBang(bb); var classesToPreserve = classLoaderSupport.getClassesToPreserve() - .filter(ClassInclusionPolicy::isClassIncludedBase) + .filter(clazz -> classInclusionPolicy.isOriginalTypeIncluded(originalMetaAccess.lookupJavaType(clazz))) .filter(c -> !(classesOrPackagesToIgnore.contains(c.getPackageName()) || classesOrPackagesToIgnore.contains(c.getName()))) .sorted(Comparator.comparing(ReflectionUtil::getClassHierarchyDepth).reversed()) .toList(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java index 8dc326ed4790..920ea612c138 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java @@ -188,7 +188,6 @@ public static void processLayerOptions(EconomicMap, Object> values, IncludeOptionsSupport.parseIncludeSelector(layerCreateArg, valueWithOrigin, layerSelectors, option, layerCreatePossibleOptions()); } - SubstrateOptions.UseBaseLayerInclusionPolicy.update(values, true); SubstrateOptions.ClosedTypeWorld.update(values, false); if (SubstrateOptions.imageLayerEnabledHandler != null) { SubstrateOptions.imageLayerEnabledHandler.onOptionEnabled(values); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java index c15f034b48ec..efdad6654825 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java @@ -107,6 +107,40 @@ *

  • The "hosted universe": elements that the AOT compilation operates on.
  • * * + * Layered Model for Static Analysis Universes + * + *
    + * +--------------------------------------------------------------+
    + * |                        Hosted Universe                       |
    + * |--------------------------------------------------------------|
    + * |        Elements that we need to create code or data for      |
    + * +------------------------------+-------------------------------+
    + *                                | wraps elements of
    + *                                V
    + * +--------------------------------------------------------------+
    + * |                        Analysis Universe                     |
    + * |--------------------------------------------------------------|
    + * |        Elements that the static analysis operates on         |
    + * +------+----------------------------+--------------------------+
    + *        | wraps elements of          | wraps elements of
    + *        |                            |
    + *        |                            V
    + *        |  +----------------------------------------------------+
    + *        |  |               Substitution Layer                   |
    + *        |  |----------------------------------------------------|
    + *        |  |  Allows modification of some elements coming from  |
    + *        |  | the layer below without modifying the layer below  |
    + *        |  +--------------------+-------------------------------+
    + *        |                       | wraps elements of
    + *        V                       V
    + * +--------------------------------------------------------------+
    + * |                         Host VM Universe                     |
    + * |--------------------------------------------------------------|
    + * |         Original source of elements, as parsed from          |
    + * |      class files found on image class and module path        |
    + * +--------------------------------------------------------------+
    + * 
    + * * Not covered in this documentation is the "substrate universe", i.e., elements that are used for * JIT compilation at image run time when a native image contains the GraalVM compiler itself. JIT * compilation is only used when a native image contains a Truffle language, e.g., the JavaScript 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 4931879e60b4..35f036cc28bd 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 @@ -78,6 +78,7 @@ import com.oracle.svm.core.fieldvaluetransformer.FieldOffsetFieldValueTransformer; import com.oracle.svm.core.fieldvaluetransformer.NewInstanceOfFixedClassFieldValueTransformer; import com.oracle.svm.core.fieldvaluetransformer.StaticFieldBaseFieldValueTransformer; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; @@ -91,6 +92,7 @@ import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; +import jdk.internal.reflect.Reflection; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -229,14 +231,37 @@ public boolean isDeleted(ResolvedJavaField field) { if (deleteAnnotations.get(field) != null) { return true; } - return isAnnotationPresent(field, Delete.class); + if (isAnnotationPresent(field, Delete.class)) { + return true; + } + if (isImplicitlyDeleted(field)) { + return true; + } + return false; + } + + /** + * When an entire type is fully substituted, for example when {@link Class} is replaced with + * {@link DynamicHub}, we replace all fields of the original type. All the fields that are + * not @{@link Substitute} are implicitly considered as @{@link Delete}. However, not all + * implicitly deleted fields are present in the {@link #deleteAnnotations} map because when the + * original class fields are iterated {@link Class#getDeclaredFields()} applies + * {@link Reflection#filterFields(Class, Field[])} and excludes several fields from reflection + * access. + */ + private boolean isImplicitlyDeleted(ResolvedJavaField field) { + /* + * If a field's type is fully substituted but the field was not substituted, then it is + * considered implicitly deleted. + */ + return typeSubstitutions.get(field.getDeclaringClass()) instanceof SubstitutionType && !fieldSubstitutions.containsKey(field); } - public boolean isAnnotationPresent(Field field, Class annotationClass) { - return isAnnotationPresent(metaAccess.lookupJavaField(field), annotationClass); + public boolean hasInjectAccessors(ResolvedJavaField field) { + return isAnnotationPresent(field, InjectAccessors.class); } - public boolean isAnnotationPresent(ResolvedJavaField field, Class annotationClass) { + private boolean isAnnotationPresent(ResolvedJavaField field, Class annotationClass) { ResolvedJavaField substitutionField = fieldSubstitutions.get(field); if (substitutionField != null) { return AnnotationAccess.isAnnotationPresent(substitutionField, annotationClass);