From 4f9263d0e8d3415f0c6ba8277cadbc9f73bacdcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Mon, 11 Nov 2024 19:04:54 +0100 Subject: [PATCH] Futher refine CustomGraalClassLoader support for libgraal building --- .../hotspot/libgraal/LibGraalClassLoader.java | 80 +++++++------------ .../libgraal/LibGraalClassLoaderBase.java | 42 ++++++---- substratevm/mx.substratevm/mx_substratevm.py | 1 + .../svm/core/hub/ClassForNameSupport.java | 14 ++-- .../hotspot/libgraal/LibGraalFeature.java | 15 ++-- .../oracle/svm/hosted/ClassLoaderFeature.java | 26 +++--- .../com/oracle/svm/hosted/FeatureHandler.java | 9 +-- .../oracle/svm/hosted/ImageClassLoader.java | 4 +- .../hosted/NativeImageClassLoaderSupport.java | 80 ++++++++++++------- .../hosted/NativeImageGeneratorRunner.java | 3 +- .../oracle/svm/hosted/NativeImageOptions.java | 5 +- .../hosted/doc-files/LibGraalClassLoader.txt | 12 +++ .../svm/hosted/reflect/ReflectionFeature.java | 2 +- .../AnnotationSubstitutionProcessor.java | 30 ++++--- 14 files changed, 174 insertions(+), 149 deletions(-) rename substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassForNameSupportFeature.java => compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoaderBase.java (53%) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LibGraalClassLoader.txt diff --git a/compiler/src/jdk.graal.compiler.libgraal.loader/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java b/compiler/src/jdk.graal.compiler.libgraal.loader/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java index 8fc181c67e7e..aaf39c51dd3b 100644 --- a/compiler/src/jdk.graal.compiler.libgraal.loader/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java +++ b/compiler/src/jdk.graal.compiler.libgraal.loader/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java @@ -38,14 +38,13 @@ import java.nio.file.Path; import java.security.ProtectionDomain; import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; -import java.util.function.Consumer; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -59,8 +58,9 @@ /** * A classloader, that reads class files and resources from a jimage file at image build time. */ +@SuppressWarnings("unused") @Platforms(Platform.HOSTED_ONLY.class) -final class HostedLibGraalClassLoader extends ClassLoader { +final class HostedLibGraalClassLoader extends ClassLoader implements LibGraalClassLoaderBase { private static final String JAVA_HOME_PROPERTY_KEY = "jdk.graal.internal.libgraal.javahome"; private static final String JAVA_HOME_PROPERTY_VALUE = System.getProperty(JAVA_HOME_PROPERTY_KEY, System.getProperty("java.home")); @@ -79,7 +79,7 @@ final class HostedLibGraalClassLoader extends ClassLoader { * Map from the {@linkplain Class#forName(String) name} of a class to the image path of its * class file. */ - private final Map classes = new HashMap<>(); + private final Map classes; /** * Map from a service name to a list of providers. @@ -113,7 +113,6 @@ public HostedLibGraalClassLoader() { super(LibGraalClassLoader.LOADER_NAME, Feature.class.getClassLoader()); libGraalJavaHome = Path.of(JAVA_HOME_PROPERTY_VALUE); - Map modulesMap = new HashMap<>(); try { /* * Access to jdk.internal.jimage classes is needed by this Classloader implementation. @@ -129,6 +128,9 @@ public HostedLibGraalClassLoader() { Modules.addExports(javaBaseModule, "jdk.internal.vm", unnamedModuleOfThisLoader); Modules.addExports(javaBaseModule, "jdk.internal.misc", unnamedModuleOfThisLoader); + Map modulesMap = new HashMap<>(); + Map classesMap = new HashMap<>(); + Path imagePath = libGraalJavaHome.resolve(Path.of("lib", "modules")); this.imageReader = BasicImageReader.open(imagePath); for (var entry : imageReader.getEntryNames()) { @@ -140,13 +142,13 @@ public HostedLibGraalClassLoader() { resources.put(resource, entry); if (resource.endsWith(".class")) { String className = resource.substring(0, resource.length() - ".class".length()).replace('/', '.'); - classes.put(className, entry); if (resource.equals("module-info.class")) { ModuleDescriptor md = ModuleDescriptor.read(imageReader.getResourceBuffer(imageReader.findLocation(entry))); for (var p : md.provides()) { services.computeIfAbsent(p.service(), k -> new ArrayList<>()).addAll(p.providers()); } } else { + classesMap.put(className, entry); modulesMap.put(className, module); } } @@ -154,36 +156,24 @@ public HostedLibGraalClassLoader() { } } + modules = Map.copyOf(modulesMap); + classes = Map.copyOf(classesMap); + } catch (IOException e) { - throw new RuntimeException(e); + throw GraalError.shouldNotReachHere(e); } - this.modules = Map.copyOf(modulesMap); } - /** - * Gets an unmodifiable map from the {@linkplain Class#forName(String) name} of a class to the - * name of its enclosing module. Reflectively accessed by - * {@code LibGraalFeature.OptionCollector#afterAnalysis(AfterAnalysisAccess)}. - */ - @SuppressWarnings("unused") + @Override public Map getModules() { return modules; } /* Allow image builder to perform registration action on each class this loader provides. */ - @SuppressWarnings("unused") - public void forEachClass(Consumer> action) { - for (String className : classes.keySet()) { - if (className.equals("module-info")) { - continue; - } - try { - var clazz = loadClass(className); - action.accept(clazz); - } catch (ClassNotFoundException e) { - throw GraalError.shouldNotReachHere(e, LibGraalClassLoader.LOADER_NAME + " could not load class " + className); - } - } + + @Override + public Set getAllClassNames() { + return classes.keySet(); } @Override @@ -262,24 +252,11 @@ protected URL findResource(String name) { @Override protected Enumeration findResources(String name) throws IOException { - return new Enumeration<>() { - private URL next = findResource(name); - - @Override - public boolean hasMoreElements() { - return (next != null); - } - - @Override - public URL nextElement() { - if (next == null) { - throw new NoSuchElementException(); - } - URL u = next; - next = null; - return u; - } - }; + URL resource = findResource(name); + if (resource == null) { + return Collections.emptyEnumeration(); + } + return Collections.enumeration(List.of(resource)); } /** @@ -340,12 +317,13 @@ public String getContentType() { } } - /** - * @return instance of ClassLoader that should be seen at image-runtime if a class was loaded at - * image-buildtime by this classloader. - */ - @SuppressWarnings("unused") - public static ClassLoader getRuntimeClassLoader() { + @Override + public HostedLibGraalClassLoader getClassLoader() { + return this; + } + + @Override + public LibGraalClassLoader getRuntimeClassLoader() { return LibGraalClassLoader.singleton; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassForNameSupportFeature.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoaderBase.java similarity index 53% rename from substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassForNameSupportFeature.java rename to compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoaderBase.java index 78e7b41febc2..0fb49d052f7d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassForNameSupportFeature.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoaderBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -23,22 +23,32 @@ * questions. */ -package com.oracle.svm.hosted; +package jdk.graal.compiler.hotspot.libgraal; -import org.graalvm.nativeimage.ImageSingletons; +import java.util.Map; +import java.util.Set; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; -import com.oracle.svm.core.feature.InternalFeature; -import com.oracle.svm.core.hub.ClassForNameSupport; -import com.oracle.svm.core.layeredimagesingleton.FeatureSingleton; -import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; -import com.oracle.svm.hosted.FeatureImpl.AfterRegistrationAccessImpl; +public interface LibGraalClassLoaderBase { -@AutomaticallyRegisteredFeature -public final class ClassForNameSupportFeature implements InternalFeature, FeatureSingleton, UnsavedSingleton { - @Override - public void afterRegistration(AfterRegistrationAccess access) { - ClassLoader customLoader = ((AfterRegistrationAccessImpl) access).getImageClassLoader().classLoaderSupport.getCustomLoader(); - ImageSingletons.add(ClassForNameSupport.class, new ClassForNameSupport(customLoader)); - } + /** + * @return instance of ClassLoader that implements this interface. + */ + ClassLoader getClassLoader(); + + /** + * @return instance of ClassLoader that should be seen at image-runtime if a class was loaded at + * image-buildtime by this classloader. + */ + ClassLoader getRuntimeClassLoader(); + + /** + * Gets an unmodifiable map from the {@linkplain Class#forName(String) name} of a class to the + * name of its enclosing module. + */ + Map getModules(); + + /** + * Get unmodifiable set of fully qualified names of all classes this loader can load. + */ + Set getAllClassNames(); } diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 0b542e3a3a56..4c3295ead038 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1508,6 +1508,7 @@ def prevent_build_path_in_libgraal(): ## Pass via JVM args opening up of packages needed for image builder early on '-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.hotspot=ALL-UNNAMED', + '-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.hotspot.libgraal=ALL-UNNAMED', '-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.options=ALL-UNNAMED', '-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.truffle=ALL-UNNAMED', '-J--add-exports=jdk.graal.compiler/jdk.graal.compiler.truffle.hotspot=ALL-UNNAMED', diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java index 120fe5a356e9..b78b746918e3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java @@ -38,6 +38,7 @@ import com.oracle.svm.core.configure.ConditionalRuntimeValue; import com.oracle.svm.core.configure.RuntimeConditionSet; +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; @@ -45,16 +46,13 @@ import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; +@AutomaticallyRegisteredImageSingleton public final class ClassForNameSupport implements MultiLayeredImageSingleton, UnsavedSingleton { - private ClassLoader customLoader; + private ClassLoader libGraalLoader; - public ClassForNameSupport(ClassLoader customLoader) { - setCustomLoader(customLoader); - } - - public void setCustomLoader(ClassLoader customLoader) { - this.customLoader = customLoader; + public void setLibGraalLoader(ClassLoader libGraalLoader) { + this.libGraalLoader = libGraalLoader; } public static ClassForNameSupport singleton() { @@ -126,7 +124,7 @@ accessible through the builder class loader, and it was already registered by na @Platforms(HOSTED_ONLY.class) private boolean isLibGraalClass(Class clazz) { - return customLoader != null && clazz.getClassLoader() == customLoader; + return libGraalLoader != null && clazz.getClassLoader() == libGraalLoader; } public static ConditionalRuntimeValue updateConditionalValue(ConditionalRuntimeValue existingConditionalValue, Object newValue, diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java index 77fec49a581f..10fb29f0065c 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java @@ -62,7 +62,6 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.graal.hotspot.GetCompilerConfig; import com.oracle.svm.graal.hotspot.GetJNIConfig; -import com.oracle.svm.hosted.ClassLoaderFeature; import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; @@ -74,6 +73,7 @@ import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.hotspot.CompilerConfigurationFactory; import jdk.graal.compiler.hotspot.libgraal.BuildTime; +import jdk.graal.compiler.hotspot.libgraal.LibGraalClassLoaderBase; import jdk.graal.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin; import jdk.graal.compiler.options.OptionDescriptor; import jdk.graal.compiler.options.OptionKey; @@ -111,6 +111,8 @@ public List> getRequiredFeatures() { final MethodHandles.Lookup mhl = MethodHandles.lookup(); + LibGraalClassLoaderBase libGraalClassLoader; + /** * Loader used for loading classes from the guest GraalVM. */ @@ -185,8 +187,9 @@ public void afterRegistration(AfterRegistrationAccess access) { // org.graalvm.nativeimage.impl.IsolateSupport accessModulesToClass(ModuleSupport.Access.EXPORT, LibGraalFeature.class, "org.graalvm.nativeimage"); - loader = createHostedLibGraalClassLoader(access); - ImageSingletons.lookup(ClassForNameSupport.class).setCustomLoader(loader); + libGraalClassLoader = createHostedLibGraalClassLoader(access); + loader = libGraalClassLoader.getClassLoader(); + ImageSingletons.lookup(ClassForNameSupport.class).setLibGraalLoader(loader); buildTimeClass = loadClassOrFail("jdk.graal.compiler.hotspot.libgraal.BuildTime"); @@ -208,10 +211,10 @@ public void afterRegistration(AfterRegistrationAccess access) { } @SuppressWarnings("unchecked") - private static ClassLoader createHostedLibGraalClassLoader(AfterRegistrationAccess access) { + private static LibGraalClassLoaderBase createHostedLibGraalClassLoader(AfterRegistrationAccess access) { var hostedLibGraalClassLoaderClass = access.findClassByName("jdk.graal.compiler.hotspot.libgraal.HostedLibGraalClassLoader"); ModuleSupport.accessPackagesToClass(Access.EXPORT, hostedLibGraalClassLoaderClass, false, "java.base", "jdk.internal.module"); - return ReflectionUtil.newInstance((Class) hostedLibGraalClassLoaderClass); + return ReflectionUtil.newInstance((Class) hostedLibGraalClassLoaderClass); } private static void accessModulesToClass(ModuleSupport.Access access, Class accessingClass, String... moduleNames) { @@ -233,7 +236,7 @@ public void duringSetup(DuringSetupAccess access) { * HostedLibGraalClassLoader provides runtime-replacement loader instance. Make sure * HostedLibGraalClassLoader gets replaced by customRuntimeLoader instance in image. */ - ClassLoader customRuntimeLoader = ClassLoaderFeature.getCustomRuntimeClassLoader(loader); + ClassLoader customRuntimeLoader = libGraalClassLoader.getRuntimeClassLoader(); access.registerObjectReplacer(obj -> obj == loader ? customRuntimeLoader : obj); try { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java index 085c67d72731..78ae580dd4b4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java @@ -24,7 +24,6 @@ */ package com.oracle.svm.hosted; -import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -33,6 +32,7 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; +import com.oracle.svm.core.hub.ClassForNameSupport; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; @@ -41,6 +41,7 @@ import com.oracle.svm.hosted.jdk.HostedClassLoaderPackageManagement; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.hotspot.libgraal.LibGraalClassLoaderBase; import jdk.internal.loader.ClassLoaders; import jdk.vm.ci.meta.JavaConstant; @@ -120,15 +121,18 @@ public void duringSetup(DuringSetupAccess access) { var config = (FeatureImpl.DuringSetupAccessImpl) access; if (ImageLayerBuildingSupport.firstImageBuild()) { - ClassLoader customLoader = ((DuringSetupAccessImpl) access).imageClassLoader.classLoaderSupport.getCustomLoader(); - if (customLoader != null) { - ClassLoader customRuntimeLoader = getCustomRuntimeClassLoader(customLoader); - if (customRuntimeLoader != null) { + LibGraalClassLoaderBase libGraalLoader = ((DuringSetupAccessImpl) access).imageClassLoader.classLoaderSupport.getLibGraalLoader(); + if (libGraalLoader != null) { + ClassLoader libGraalClassLoader = libGraalLoader.getClassLoader(); + ClassForNameSupport.singleton().setLibGraalLoader(libGraalClassLoader); + + ClassLoader runtimeLibGraalClassLoader = libGraalLoader.getRuntimeClassLoader(); + if (runtimeLibGraalClassLoader != null) { /* - * CustomLoader provides runtime-replacement ClassLoader instance. Make sure - * customLoader gets replaced by customRuntimeLoader instance in image. + * LibGraalLoader provides runtime-replacement ClassLoader instance. Make sure + * LibGraalLoader gets replaced by runtimeLibGraalClassLoader instance in image. */ - access.registerObjectReplacer(obj -> obj == customLoader ? customRuntimeLoader : obj); + access.registerObjectReplacer(obj -> obj == libGraalClassLoader ? runtimeLibGraalClassLoader : obj); } } access.registerObjectReplacer(this::runtimeClassLoaderObjectReplacer); @@ -146,12 +150,6 @@ public void duringSetup(DuringSetupAccess access) { } } - public static ClassLoader getCustomRuntimeClassLoader(ClassLoader customLoader) { - Class customLoaderClass = customLoader.getClass(); - Method getRuntimeClassLoaderMethod = ReflectionUtil.lookupMethod(true, customLoaderClass, "getRuntimeClassLoader"); - return getRuntimeClassLoaderMethod != null ? ReflectionUtil.invokeMethod(getRuntimeClassLoaderMethod, null) : null; - } - @Override public void beforeAnalysis(BeforeAnalysisAccess access) { var packagesField = ReflectionUtil.lookupField(ClassLoader.class, "packages"); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java index 66ee578138c5..9309d6d8ce53 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java @@ -182,14 +182,7 @@ public void registerFeatures(ImageClassLoader loader, DebugContext debug) { registerFeature(featureClass, specificClassProvider, access); } - List featureClassLoaders; - ClassLoader customLoader = loader.classLoaderSupport.getCustomLoader(); - if (customLoader != null) { - featureClassLoaders = List.of(customLoader, loader.getClassLoader()); - } else { - featureClassLoaders = List.of(loader.getClassLoader()); - } - + List featureClassLoaders = loader.classLoaderSupport.getClassLoaders(); for (String featureName : Options.userEnabledFeatures()) { Class featureClass = null; for (ClassLoader featureClassLoader : featureClassLoaders) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java index c55efe36d594..7a93c04920f7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java @@ -408,8 +408,8 @@ public ClassLoader getClassLoader() { return classLoaderSupport.getClassLoader(); } - public Optional getMainClassFromModule(Object module) { - return classLoaderSupport.getMainClassFromModule(module); + public static Optional getMainClassFromModule(Object module) { + return NativeImageClassLoaderSupport.getMainClassFromModule(module); } public Optional findModule(String moduleName) { 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 6081a621f713..ca7bc6fdbe3c 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 @@ -72,7 +72,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; import java.util.function.BiConsumer; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -98,13 +97,16 @@ import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.ModuleSupport; +import com.oracle.svm.util.ModuleSupport.Access; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.hotspot.libgraal.LibGraalClassLoaderBase; import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionValues; import jdk.internal.module.Modules; -public class NativeImageClassLoaderSupport { +public final class NativeImageClassLoaderSupport { private final List imagecp; private final List buildcp; @@ -119,7 +121,6 @@ public class NativeImageClassLoaderSupport { private final ConcurrentHashMap> serviceProviders; private final NativeImageClassLoader classLoader; - private final ClassLoader customLoader; public final ModuleFinder upgradeAndSystemModuleFinder; public final ModuleLayer moduleLayerForImageBuild; @@ -131,6 +132,9 @@ public class NativeImageClassLoaderSupport { private Set javaPathsToInclude; private boolean includeAllFromClassPath; + private Optional libGraalLoader; + private List classLoaders; + private final Set> classesToIncludeUnconditionally = Collections.newSetFromMap(new ConcurrentHashMap<>()); @SuppressWarnings("this-escape") @@ -189,23 +193,6 @@ protected NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, St classLoader = new NativeImageClassLoader(imagecp, configuration, defaultSystemClassLoader); - String customLoaderPropertyKey = "org.graalvm.nativeimage.experimental.loader"; - String customLoaderPropertyValue = System.getProperty(customLoaderPropertyKey); - if (customLoaderPropertyValue != null) { - try { - Class customLoaderClass = Class.forName(customLoaderPropertyValue, true, classLoader); - customLoader = (ClassLoader) ReflectionUtil.newInstance(customLoaderClass); - } catch (ClassNotFoundException e) { - throw VMError.shouldNotReachHere("Custom ClassLoader " + customLoaderPropertyValue + - " set via system property " + customLoaderPropertyKey + " could not be found.", e); - } catch (ClassCastException e) { - throw VMError.shouldNotReachHere("Custom ClassLoader " + customLoaderPropertyValue + - " set via system property " + customLoaderPropertyKey + " does not extend class ClassLoader.", e); - } - } else { - customLoader = null; - } - ModuleLayer moduleLayer = ModuleLayer.defineModules(configuration, List.of(ModuleLayer.boot()), ignored -> classLoader).layer(); adjustBootLayerQualifiedExports(moduleLayer); moduleLayerForImageBuild = moduleLayer; @@ -238,8 +225,14 @@ public NativeImageClassLoader getClassLoader() { return classLoader; } - public ClassLoader getCustomLoader() { - return customLoader; + public LibGraalClassLoaderBase getLibGraalLoader() { + VMError.guarantee(libGraalLoader != null, "Invalid access to libGraalLoader before getting set up"); + return libGraalLoader.orElse(null); + } + + public List getClassLoaders() { + VMError.guarantee(classLoaders != null, "Invalid access to classLoaders before getting set up"); + return classLoaders; } private static Path stringToPath(String path) { @@ -270,13 +263,16 @@ public void loadAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoa new LoadClassHandler(executor, imageClassLoader).run(); - if (customLoader != null) { - /* If we have a customLoader, allow it to register its classes to the image builder */ - Class customLoaderClass = customLoader.getClass(); - Method customLoaderForEachClass = ReflectionUtil.lookupMethod(true, customLoaderClass, "forEachClass", Consumer.class); - if (customLoaderForEachClass != null) { - Consumer> handleClass = imageClassLoader::handleClass; - ReflectionUtil.invokeMethod(customLoaderForEachClass, customLoader, handleClass); + LibGraalClassLoaderBase graalLoader = getLibGraalLoader(); + if (graalLoader != null) { + /* If we have a customLoader, register its classes to the image builder */ + for (String fqn : graalLoader.getAllClassNames()) { + try { + var clazz = graalLoader.getClassLoader().loadClass(fqn); + imageClassLoader.handleClass(clazz); + } catch (ClassNotFoundException e) { + throw GraalError.shouldNotReachHere(e, graalLoader + " could not load class " + fqn); + } } } } @@ -569,6 +565,30 @@ private Stream processOption(OptionKey loaderClass = Class.forName(libGraalClassLoaderFQN, true, classLoader); + LibGraalClassLoaderBase loaderInstance = (LibGraalClassLoaderBase) ReflectionUtil.newInstance(loaderClass); + libGraalLoader = Optional.of(loaderInstance); + classLoaders = List.of(loaderInstance.getClassLoader(), getClassLoader()); + } catch (ClassNotFoundException e) { + throw VMError.shouldNotReachHere("LibGraalClassLoader " + libGraalClassLoaderFQN + + " set via " + SubstrateOptionsParser.commandArgument(NativeImageOptions.LibGraalClassLoader, libGraalClassLoaderFQN) + + " could not be found.", e); + } catch (ClassCastException e) { + throw VMError.shouldNotReachHere("LibGraalClassLoader " + libGraalClassLoaderFQN + + " set via " + SubstrateOptionsParser.commandArgument(NativeImageOptions.LibGraalClassLoader, libGraalClassLoaderFQN) + + " does not extend class " + LibGraalClassLoaderBase.class.getName() + '.', e); + } + } else { + libGraalLoader = Optional.empty(); + classLoaders = List.of(getClassLoader()); + } + } + private record AddExportsAndOpensAndReadsFormatValue(Module module, String packageName, List targetModules) { } @@ -667,7 +687,7 @@ private static boolean isModuleClassLoader(ClassLoader loader, ClassLoader modul } } - Optional getMainClassFromModule(Object module) { + static Optional getMainClassFromModule(Object module) { assert module instanceof Module : "Argument `module` is not an instance of java.lang.Module"; return ((Module) module).getDescriptor().mainClass(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java index 1eac07ed6f98..bf27c5ad5189 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java @@ -284,6 +284,7 @@ public static ImageClassLoader installNativeImageClassLoader(String[] classpath, NativeImageSystemClassLoader nativeImageSystemClassLoader = NativeImageSystemClassLoader.singleton(); NativeImageClassLoaderSupport nativeImageClassLoaderSupport = new NativeImageClassLoaderSupport(nativeImageSystemClassLoader.defaultSystemClassLoader, classpath, modulepath); nativeImageClassLoaderSupport.setupHostedOptionParser(arguments); + nativeImageClassLoaderSupport.setupLibGraalClassLoader(); /* Perform additional post-processing with the created nativeImageClassLoaderSupport */ for (NativeImageClassLoaderPostProcessing postProcessing : ServiceLoader.load(NativeImageClassLoaderPostProcessing.class)) { postProcessing.apply(nativeImageClassLoaderSupport); @@ -445,7 +446,7 @@ private int buildImage(ImageClassLoader classLoader) { .orElseThrow(() -> UserError.abort("Module " + moduleName + " for mainclass not found.")); } if (className.isEmpty()) { - className = classLoader.getMainClassFromModule(mainModule) + className = ImageClassLoader.getMainClassFromModule(mainModule) .orElseThrow(() -> UserError.abort("Module %s does not have a ModuleMainClass attribute, use -m /", moduleName)); } mainClass = classLoader.forName(className, mainModule); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java index fe3cb0f92f23..ad987a85ab9e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java @@ -57,8 +57,6 @@ public class NativeImageOptions { - public static final int DEFAULT_MAX_ANALYSIS_SCALING = 16; - @Option(help = "Comma separated list of CPU features that will be enabled while building the " + "target executable, irrespective of whether they are supported by the hosted " + "environment. Note that enabling features not present within the target environment " + @@ -307,4 +305,7 @@ protected void onValueUpdate(EconomicMap, Object> values, Boolean o } } }; + + @Option(help = "file:doc-files/LibGraalClassLoader.txt")// + public static final HostedOptionKey LibGraalClassLoader = new HostedOptionKey<>(""); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LibGraalClassLoader.txt b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LibGraalClassLoader.txt new file mode 100644 index 000000000000..d843a040f927 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/doc-files/LibGraalClassLoader.txt @@ -0,0 +1,12 @@ +Specify the fully qualified class name of the LibGraalClassLoader implementation that gets used for building libgraal. + +This option is only supported for building the libgraal shared library. +The given fully qualified class name has to be a subtype of +jdk.graal.compiler.hotspot.libgraal.LibGraalClassLoaderBase. + +When building the libgraal shared library, this option is used to specify a custom loader +the builder instantiates via default constructor, that affects image building as follows: + + 1. The custom loader is used to lookup Feature implementations passed via the --features option. + 2. All @CEntryPoint definitions from classes managed by the custom loader are processed. + 3. All @TargetClass substitutions in classes managed by the custom loader are processed. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index dc2864a9c6c0..1db397c00235 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -63,6 +63,7 @@ import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.hub.ClassForNameSupport; +import com.oracle.svm.core.hub.ClassForNameSupportFeature; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.meta.SharedMethod; @@ -72,7 +73,6 @@ import com.oracle.svm.core.reflect.SubstrateMethodAccessor; import com.oracle.svm.core.reflect.target.ReflectionSubstitutionSupport; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.ClassForNameSupportFeature; import com.oracle.svm.hosted.FallbackFeature; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeCompilationAccessImpl; 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 dde6975633d2..04a015d7c329 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 @@ -1091,15 +1091,18 @@ protected Class findTargetClass(Class annotatedBaseClass, TargetClass targ protected Class findTargetClass(Class targetClass, Class noClassNameProviderClass, Class annotatedBaseClass, T target, Class value, String targetClassName, Class> classNameProvider, String[] innerClasses, Class> classloaderSupplier, Class[] onlyWith) { - + Class holder; String className; - ClassLoader loader = imageClassLoader.getClassLoader(); + ClassLoader suppliedLoader = null; if (value != targetClass) { guarantee(targetClassName.isEmpty(), "Both class and class name specified for substitution"); guarantee(classNameProvider == noClassNameProviderClass, "Both class and classNameProvider specified for substitution"); guarantee(classloaderSupplier == TargetClass.NoClassLoaderProvider.class, "Annotation attribute 'classLoader' requires use of 'className' or 'classNameProvider'"); - className = value.getName(); + + holder = value; + className = holder.getName(); } else { + holder = null; if (classNameProvider != noClassNameProviderClass) { try { className = ReflectionUtil.newInstance(classNameProvider).apply(target); @@ -1112,7 +1115,7 @@ protected Class findTargetClass(Class targetClass, Class noClassNam } if (classloaderSupplier != TargetClass.NoClassLoaderProvider.class) { try { - loader = ReflectionUtil.newInstance(classloaderSupplier).get(); + suppliedLoader = ReflectionUtil.newInstance(classloaderSupplier).get(); } catch (ReflectionUtilError ex) { throw UserError.abort(ex.getCause(), "Cannot instantiate classloaderSupplier: %s. The class must have a parameterless constructor.", classloaderSupplier.getTypeName()); } @@ -1145,12 +1148,19 @@ protected Class findTargetClass(Class targetClass, Class noClassNam } } - Class holder; - try { - holder = Class.forName(className, false, loader); - } catch (ClassNotFoundException e) { - throw UserError.abort("Substitution target for %s is not loaded. Use field `onlyWith` in the `TargetClass` annotation to make substitution only active when needed.", - annotatedBaseClass.getName()); + if (holder == null) { + var substitutionsClassLoaders = suppliedLoader != null ? List.of(suppliedLoader) : imageClassLoader.classLoaderSupport.getClassLoaders(); + for (ClassLoader substitutionsClassLoader : substitutionsClassLoaders) { + try { + holder = Class.forName(className, false, substitutionsClassLoader); + break; + } catch (ClassNotFoundException e) { + if (substitutionsClassLoader == substitutionsClassLoaders.getLast()) { + throw UserError.abort("Substitution target for %s is not loaded. Use field `onlyWith` in the `TargetClass` annotation to make substitution only active when needed.", + annotatedBaseClass.getName()); + } + } + } } if (innerClasses.length > 0) { for (String innerClass : innerClasses) {