From ccd5aa34d35677ce0ebc6db84c50d38685ecfc3c Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Fri, 3 Nov 2023 16:45:22 +0100 Subject: [PATCH] Add options to include all from class path --- .../graal/pointsto/AbstractAnalysisEngine.java | 17 ++++++++++++++--- .../graal/pointsto/ClassInclusionPolicy.java | 15 +++++++++------ .../com/oracle/svm/core/SubstrateOptions.java | 5 ++++- .../svm/hosted/ClassLoaderSupportImpl.java | 2 +- .../hosted/NativeImageClassLoaderSupport.java | 14 +++++++++++--- .../analysis/NativeImagePointsToAnalysis.java | 5 +++-- 6 files changed, 42 insertions(+), 16 deletions(-) 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 190b3c97d339..48337db3e49f 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,6 +25,9 @@ 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; @@ -350,17 +353,25 @@ public final boolean executorIsStarted() { @Override public void registerTypeForBaseImage(Class cls) { - if (classInclusionPolicy.isClassIncluded(cls)) { + if (getOrDefault(cls, classInclusionPolicy::isClassIncluded, false)) { classInclusionPolicy.includeClass(cls); - Stream.concat(Arrays.stream(cls.getDeclaredConstructors()), Arrays.stream(cls.getDeclaredMethods())) + 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(cls.getDeclaredFields()) + Arrays.stream(getOrDefault(cls, Class::getDeclaredFields, new Field[0])) .filter(classInclusionPolicy::isFieldIncluded) .forEach(classInclusionPolicy::includeField); } } + public static U getOrDefault(T cls, Function getMembers, U backup) { + try { + return getMembers.apply(cls); + } catch (NoClassDefFoundError | IncompatibleClassChangeError e) { + return backup; + } + } + /** * Provide a non-null position. Some flows like newInstance and invoke require a non-null * position, for others is just better. The constructed position is best-effort, i.e., it 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 9ba4a7af994a..4e08c6243fe1 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 @@ -30,8 +30,10 @@ import java.lang.reflect.Modifier; import org.graalvm.nativeimage.AnnotationAccess; +import org.graalvm.nativeimage.hosted.Feature; import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.svm.core.annotate.TargetClass; import jdk.graal.compiler.api.replacements.Fold; @@ -55,7 +57,10 @@ public void setBigBang(BigBang bb) { /** * Determine if the given class needs to be included in the image according to the policy. */ - public abstract boolean isClassIncluded(Class cls); + public boolean isClassIncluded(Class cls) { + Class enclosingClass = cls.getEnclosingClass(); + return !Feature.class.isAssignableFrom(cls) && !AnnotationAccess.isAnnotationPresent(cls, TargetClass.class) && (enclosingClass == null || isClassIncluded(enclosingClass)); + } /** * Determine if the given method needs to be included in the image according to the policy. @@ -126,6 +131,9 @@ public LayeredBaseImageInclusionPolicy(Object reason) { @Override public boolean isClassIncluded(Class cls) { + if (!super.isClassIncluded(cls)) { + return false; + } Class enclosingClass = cls.getEnclosingClass(); int classModifiers = cls.getModifiers(); if (enclosingClass != null) { @@ -172,11 +180,6 @@ public DefaultAllInclusionPolicy(Object reason) { super(reason); } - @Override - public boolean isClassIncluded(Class cls) { - return true; - } - @Override public void includeMethod(Executable method) { bb.postTask(debug -> bb.addRootMethod(method, false, reason)); 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 6759646a916a..92d680df8989 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 @@ -1175,8 +1175,11 @@ public enum ReportingMode { @Option(help = "Include all classes, methods, fields, and resources from given paths", type = OptionType.Debug) // public static final HostedOptionKey IncludeAllFromPath = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.build()); + @Option(help = "Include all classes, methods, fields, and resources from the class path", type = OptionType.Debug) // + public static final HostedOptionKey IncludeAllFromClassPath = new HostedOptionKey<>(false); + public static boolean includeAll() { - return IncludeAllFromModule.hasBeenSet() || IncludeAllFromPath.hasBeenSet(); + return IncludeAllFromModule.hasBeenSet() || IncludeAllFromPath.hasBeenSet() || IncludeAllFromClassPath.hasBeenSet(); } @Option(help = "Run layered image base layer open-world analysis. Includes all public types and methods that can be reached using normal Java access rules.")// diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java index d25a48c71883..c77697474944 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java @@ -116,7 +116,7 @@ public void collectResources(ResourceCollector resourceCollector) { /* Collect remaining resources from classpath */ classLoaderSupport.classpath().stream().parallel().forEach(classpathFile -> { - boolean includeCurrent = classLoaderSupport.getJavaPathsToInclude().contains(classpathFile); + boolean includeCurrent = classLoaderSupport.getJavaPathsToInclude().contains(classpathFile) || classLoaderSupport.includeAllFromClassPath(); try { if (Files.isDirectory(classpathFile)) { scanDirectory(classpathFile, resourceCollector, includeCurrent); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index 9822516daf8c..2b5784c433a6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.hosted; +import static com.oracle.svm.core.SubstrateOptions.IncludeAllFromClassPath; import static com.oracle.svm.core.SubstrateOptions.IncludeAllFromModule; import static com.oracle.svm.core.SubstrateOptions.IncludeAllFromPath; import static com.oracle.svm.core.util.VMError.guarantee; @@ -77,8 +78,6 @@ import org.graalvm.collections.EconomicSet; import org.graalvm.collections.MapCursor; import org.graalvm.collections.Pair; -import jdk.graal.compiler.options.OptionKey; -import jdk.graal.compiler.options.OptionValues; import org.graalvm.nativeimage.impl.AnnotationExtractor; import com.oracle.svm.core.NativeImageClassLoaderOptions; @@ -98,6 +97,8 @@ import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.options.OptionKey; +import jdk.graal.compiler.options.OptionValues; import jdk.internal.module.Modules; public class NativeImageClassLoaderSupport { @@ -124,6 +125,7 @@ public class NativeImageClassLoaderSupport { private Set javaModuleNamesToInclude; private Set javaPathsToInclude; + private boolean includeAllFromClassPath; private final Set> classesToIncludeUnconditionally = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -239,6 +241,8 @@ public void loadAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoa .filter(p -> !classpath().contains(p)) .findAny().ifPresent(p -> missingFromSetOfEntriesError(p, classpath(), "classpath", IncludeAllFromPath)); + includeAllFromClassPath = IncludeAllFromClassPath.getValue(parsedHostedOptions); + new LoadClassHandler(executor, imageClassLoader).run(); } @@ -705,7 +709,7 @@ private void initModule(ModuleReference moduleReference) { } private void loadClassesFromPath(Path path) { - final boolean includeUnconditionally = javaPathsToInclude.contains(path); + final boolean includeUnconditionally = javaPathsToInclude.contains(path) || includeAllFromClassPath; if (ClasspathUtils.isJar(path)) { try { URI container = path.toAbsolutePath().toUri(); @@ -924,6 +928,10 @@ public Set getJavaPathsToInclude() { return javaPathsToInclude; } + public boolean includeAllFromClassPath() { + return includeAllFromClassPath; + } + public List> getClassesToIncludeUnconditionally() { return classesToIncludeUnconditionally.stream() .sorted(Comparator.comparing(Class::getTypeName)) 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 4b855824f34e..7da9ba746a54 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 @@ -141,9 +141,10 @@ public void onTypeReachable(AnalysisType type) { * Using getInstanceFields and getStaticFields allows to include the fields from the * substitution class. */ - Stream.concat(Arrays.stream(type.getInstanceFields(true)), Arrays.stream(type.getStaticFields())) + Stream.concat(Arrays.stream(getOrDefault(type, t -> t.getInstanceFields(true), new AnalysisField[0])), + Arrays.stream(getOrDefault(type, AnalysisType::getStaticFields, new AnalysisField[0]))) .map(OriginalFieldProvider::getJavaField) - .filter(classInclusionPolicy::isFieldIncluded) + .filter(field -> field != null && classInclusionPolicy.isFieldIncluded(field)) .forEach(classInclusionPolicy::includeField); } });