diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py index 6fa9c61e2f53..ff9a9aa85ae8 100644 --- a/compiler/mx.compiler/suite.py +++ b/compiler/mx.compiler/suite.py @@ -465,6 +465,22 @@ "workingSets" : "Graal,Test", "graalCompilerSourceEdition": "ignore", }, + + "jdk.graal.compiler.libgraal.loader" : { + "subDir" : "src", + "sourceDirs" : ["src"], + "workingSets" : "Graal", + "javaCompliance" : "21+", + "dependencies" : [ + "jdk.graal.compiler", + ], + "requiresConcealed" : { + "java.base" : [ + "jdk.internal.module", + "jdk.internal.jimage", + ], + }, + }, }, "distributions" : { @@ -691,6 +707,17 @@ "graalCompilerSourceEdition": "ignore", }, + "LIBGRAAL_LOADER" : { + "subDir": "src", + "dependencies" : [ + "jdk.graal.compiler.libgraal.loader", + ], + "distDependencies" : [ + "GRAAL", + ], + "maven": False, + }, + "GRAAL_PROFDIFF_TEST" : { "subDir" : "src", "dependencies" : [ diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalClassLoader.java b/compiler/src/jdk.graal.compiler.libgraal.loader/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java similarity index 66% rename from substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalClassLoader.java rename to compiler/src/jdk.graal.compiler.libgraal.loader/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java index aaa171c742ad..8fc181c67e7e 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalClassLoader.java +++ b/compiler/src/jdk.graal.compiler.libgraal.loader/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.graal.hotspot.libgraal; +package jdk.graal.compiler.hotspot.libgraal; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -38,7 +38,6 @@ 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; @@ -46,51 +45,51 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; +import java.util.function.Consumer; -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.util.VMError; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature; -import com.oracle.svm.util.ModuleSupport; +import jdk.graal.compiler.debug.GraalError; import jdk.internal.jimage.BasicImageReader; import jdk.internal.jimage.ImageLocation; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; +import jdk.internal.module.Modules; /** * A classloader, that reads class files and resources from a jimage file at image build time. */ -public class LibGraalClassLoader extends ClassLoader { +@Platforms(Platform.HOSTED_ONLY.class) +final class HostedLibGraalClassLoader extends ClassLoader { + + 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")); /** * Reader for the image. */ - @Platforms(Platform.HOSTED_ONLY.class) // private final BasicImageReader imageReader; /** * Map from the name of a resource (without module qualifier) to its path in the image. */ - @Platforms(Platform.HOSTED_ONLY.class) // private final Map resources = new HashMap<>(); /** * Map from the {@linkplain Class#forName(String) name} of a class to the image path of its * class file. */ - @Platforms(Platform.HOSTED_ONLY.class) // private final Map classes = new HashMap<>(); /** * Map from a service name to a list of providers. */ - @Platforms(Platform.HOSTED_ONLY.class) // private final Map> services = new HashMap<>(); /** * Map from the {@linkplain Class#forName(String) name} of a class to the name of its enclosing * module. */ - @Platforms(Platform.HOSTED_ONLY.class) // private final Map modules; /** @@ -100,7 +99,6 @@ public class LibGraalClassLoader extends ClassLoader { "jdk.internal.vm.ci", "org.graalvm.collections", "org.graalvm.word", - "org.graalvm.nativeimage", "jdk.graal.compiler", "org.graalvm.truffle.compiler", "com.oracle.graal.graal_enterprise"); @@ -109,17 +107,29 @@ public class LibGraalClassLoader extends ClassLoader { ClassLoader.registerAsParallelCapable(); } - /** - * @param imagePath path to the runtime image of a Java installation - */ - @Platforms(Platform.HOSTED_ONLY.class) - LibGraalClassLoader(Path imagePath) { - super("LibGraalClassLoader", null); + public final Path libGraalJavaHome; + + public HostedLibGraalClassLoader() { + super(LibGraalClassLoader.LOADER_NAME, Feature.class.getClassLoader()); + libGraalJavaHome = Path.of(JAVA_HOME_PROPERTY_VALUE); + Map modulesMap = new HashMap<>(); try { - // Need access to jdk.internal.jimage - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.EXPORT, getClass(), false, - "java.base", "jdk.internal.jimage"); + /* + * Access to jdk.internal.jimage classes is needed by this Classloader implementation. + */ + var javaBaseModule = Object.class.getModule(); + Modules.addExports(javaBaseModule, "jdk.internal.jimage", HostedLibGraalClassLoader.class.getModule()); + + /* + * The classes that will get loaded by this loader require access to several internal + * packages of java.base. Make sure packages will be accessible to those classes. + */ + Module unnamedModuleOfThisLoader = getUnnamedModule(); + Modules.addExports(javaBaseModule, "jdk.internal.vm", unnamedModuleOfThisLoader); + Modules.addExports(javaBaseModule, "jdk.internal.misc", unnamedModuleOfThisLoader); + + Path imagePath = libGraalJavaHome.resolve(Path.of("lib", "modules")); this.imageReader = BasicImageReader.open(imagePath); for (var entry : imageReader.getEntryNames()) { int secondSlash = entry.indexOf('/', 1); @@ -152,15 +162,33 @@ public class LibGraalClassLoader extends ClassLoader { /** * Gets an unmodifiable map from the {@linkplain Class#forName(String) name} of a class to the - * name of its enclosing module. + * name of its enclosing module. Reflectively accessed by + * {@code LibGraalFeature.OptionCollector#afterAnalysis(AfterAnalysisAccess)}. */ + @SuppressWarnings("unused") 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 protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - if (!SubstrateUtil.HOSTED || !classes.containsKey(name)) { + if (!classes.containsKey(name)) { return super.loadClass(name, resolve); } synchronized (getClassLoadingLock(name)) { @@ -172,40 +200,18 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundE } } - @Platforms(Platform.HOSTED_ONLY.class) - public Class loadClassOrFail(Class c) { - if (c.getClassLoader() == this) { - return c; - } - if (c.isArray()) { - return loadClassOrFail(c.getComponentType()).arrayType(); - } - return loadClassOrFail(c.getName()); - } - - @Platforms(Platform.HOSTED_ONLY.class) - public Class loadClassOrFail(String name) { - try { - return loadClass(name); - } catch (ClassNotFoundException e) { - throw VMError.shouldNotReachHere("%s unable to load class '%s'", getName(), name); - } - } - @Override protected Class findClass(final String name) throws ClassNotFoundException { - if (SubstrateUtil.HOSTED) { - String path = name.replace('.', '/').concat(".class"); - - String pathInImage = resources.get(path); - if (pathInImage != null) { - ImageLocation location = imageReader.findLocation(pathInImage); - if (location != null) { - ByteBuffer bb = Objects.requireNonNull(imageReader.getResourceBuffer(location)); - ProtectionDomain pd = null; - return super.defineClass(name, bb, pd); - } + String path = name.replace('.', '/').concat(".class"); + + String pathInImage = resources.get(path); + if (pathInImage != null) { + ImageLocation location = imageReader.findLocation(pathInImage); + if (location != null) { + ByteBuffer bb = Objects.requireNonNull(imageReader.getResourceBuffer(location)); + ProtectionDomain pd = null; + return super.defineClass(name, bb, pd); } } throw new ClassNotFoundException(name); @@ -222,35 +228,32 @@ protected Class findClass(final String name) */ private static final String RESOURCE_PROTOCOL = "resource"; - @Platforms(Platform.HOSTED_ONLY.class) // private URLStreamHandler serviceHandler; @Override protected URL findResource(String name) { - if (SubstrateUtil.HOSTED) { - URLStreamHandler handler = this.serviceHandler; - if (handler == null) { - this.serviceHandler = handler = new ImageURLStreamHandler(); - } - if (name.startsWith("META-INF/services/")) { - String service = name.substring("META-INF/services/".length()); - if (services.containsKey(service)) { - try { - var uri = new URI(SERVICE_PROTOCOL, service, null); - return URL.of(uri, handler); - } catch (URISyntaxException | MalformedURLException e) { - return null; - } + URLStreamHandler handler = this.serviceHandler; + if (handler == null) { + this.serviceHandler = handler = new ImageURLStreamHandler(); + } + if (name.startsWith("META-INF/services/")) { + String service = name.substring("META-INF/services/".length()); + if (services.containsKey(service)) { + try { + var uri = new URI(SERVICE_PROTOCOL, service, null); + return URL.of(uri, handler); + } catch (URISyntaxException | MalformedURLException e) { + return null; } - } else { - String path = resources.get(name); - if (path != null) { - try { - var uri = new URI(RESOURCE_PROTOCOL, name, null); - return URL.of(uri, handler); - } catch (URISyntaxException | MalformedURLException e) { - return null; - } + } + } else { + String path = resources.get(name); + if (path != null) { + try { + var uri = new URI(RESOURCE_PROTOCOL, name, null); + return URL.of(uri, handler); + } catch (URISyntaxException | MalformedURLException e) { + return null; } } } @@ -259,9 +262,6 @@ protected URL findResource(String name) { @Override protected Enumeration findResources(String name) throws IOException { - if (!SubstrateUtil.HOSTED) { - return Collections.emptyEnumeration(); - } return new Enumeration<>() { private URL next = findResource(name); @@ -284,9 +284,8 @@ public URL nextElement() { /** * A {@link URLStreamHandler} for use with URLs returned by - * {@link LibGraalClassLoader#findResource(java.lang.String)}. + * {@link HostedLibGraalClassLoader#findResource(java.lang.String)}. */ - @Platforms(Platform.HOSTED_ONLY.class) private class ImageURLStreamHandler extends URLStreamHandler { @Override public URLConnection openConnection(URL u) { @@ -307,7 +306,6 @@ public URLConnection openConnection(URL u) { } } - @Platforms(Platform.HOSTED_ONLY.class) private static class ImageURLConnection extends URLConnection { private final byte[] bytes; private InputStream in; @@ -341,4 +339,23 @@ public String getContentType() { return "application/octet-stream"; } } + + /** + * @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() { + return LibGraalClassLoader.singleton; + } +} + +public final class LibGraalClassLoader extends ClassLoader { + + static final String LOADER_NAME = "LibGraalClassLoader"; + static final LibGraalClassLoader singleton = new LibGraalClassLoader(); + + private LibGraalClassLoader() { + super(LOADER_NAME, null); + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java index 9ec41fa15672..762bd0cb647e 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java @@ -64,8 +64,6 @@ import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; /** * Filters certain method substitutions based on whether there is underlying hardware support for @@ -276,7 +274,6 @@ public T getInjectedArgument(Class capability) { return super.getInjectedArgument(capability); } - @Platforms(Platform.HOSTED_ONLY.class) public ResolvedJavaMethod findSnippetMethod(ResolvedJavaMethod thisMethod) { if (snippetEncoder == null) { throw new GraalError("findSnippetMethod called before initialization of Replacements"); diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index e685f649bc8f..e0473e546c65 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1468,6 +1468,7 @@ def _native_image_launcher_extra_jvm_args(): libgraal_jar_distributions = [ 'sdk:NATIVEBRIDGE', 'sdk:JNIUTILS', + 'compiler:LIBGRAAL_LOADER', 'substratevm:LIBGRAAL_LIBRARY'] def allow_build_path_in_libgraal(): diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 12feca342230..3448f7464fe0 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -1386,7 +1386,6 @@ ], "requiresConcealed" : { "java.base" : [ - "jdk.internal.jimage", "jdk.internal.misc", ], "jdk.internal.vm.ci" : [ 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 7420631d3611..120fe5a356e9 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 @@ -32,12 +32,12 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platform.HOSTED_ONLY; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.impl.ConfigurationCondition; 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,9 +45,18 @@ 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; + + public ClassForNameSupport(ClassLoader customLoader) { + setCustomLoader(customLoader); + } + + public void setCustomLoader(ClassLoader customLoader) { + this.customLoader = customLoader; + } + public static ClassForNameSupport singleton() { return ImageSingletons.lookup(ClassForNameSupport.class); } @@ -115,12 +124,9 @@ accessible through the builder class loader, and it was already registered by na } } - private static boolean isLibGraalClass(Class clazz) { - var loader = clazz.getClassLoader(); - if (loader == null) { - return false; - } - return "LibGraalClassLoader".equals(loader.getName()); + @Platforms(HOSTED_ONLY.class) + private boolean isLibGraalClass(Class clazz) { + return customLoader != null && clazz.getClassLoader() == customLoader; } 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 0c341c9ca9bc..77fec49a581f 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 @@ -58,22 +58,23 @@ import com.oracle.graal.pointsto.meta.ObjectReachableCallback; import com.oracle.graal.pointsto.reports.CallTreePrinter; import com.oracle.svm.core.SubstrateTargetDescription; -import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.hub.ClassForNameSupport; 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; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.util.ModuleSupport; +import com.oracle.svm.util.ModuleSupport.Access; import com.oracle.svm.util.ReflectionUtil; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.hotspot.CompilerConfigurationFactory; import jdk.graal.compiler.hotspot.libgraal.BuildTime; import jdk.graal.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin; -import jdk.graal.compiler.options.Option; import jdk.graal.compiler.options.OptionDescriptor; import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.serviceprovider.LibGraalService; @@ -82,12 +83,13 @@ /** * This feature builds the libgraal shared library (e.g., libjvmcicompiler.so on linux). *

- * With use of {@code -H:LibGraalJavaHome}, the Graal and JVMCI classes from which libgraal is built - * can be from a "guest" JDK that may be different from the JDK on which Native Image is running. + * With use of {@code -Djdk.graal.internal.libgraal.javahome=path}, the Graal and JVMCI classes from + * which libgraal is built can be from a "guest" JDK that may be different from the JDK on which + * Native Image is running. *

* This feature is composed of these key classes: *

    - *
  • {@link LibGraalClassLoader}
  • + *
  • {@code HostedLibGraalClassLoader}
  • *
  • {@link LibGraalEntryPoints}
  • *
  • {@link LibGraalSubstitutions}
  • *
@@ -95,14 +97,6 @@ @Platforms(Platform.HOSTED_ONLY.class) public final class LibGraalFeature implements Feature { - static class Options { - @Option(help = "The value of the java.home system property reported by the Java " + - "installation that includes the Graal classes in its runtime image " + - "from which libgraal will be built. If not provided, the java.home " + - "of the Java installation running native-image will be used.") // - public static final HostedOptionKey LibGraalJavaHome = new HostedOptionKey<>(Path.of(System.getProperty("java.home"))); - } - public static final class IsEnabled implements BooleanSupplier { @Override public boolean getAsBoolean() { @@ -120,7 +114,7 @@ public List> getRequiredFeatures() { /** * Loader used for loading classes from the guest GraalVM. */ - LibGraalClassLoader loader; + ClassLoader loader; /** * Handle to {@link BuildTime} in the guest. @@ -143,14 +137,32 @@ public List> getRequiredFeatures() { MethodHandle handleGlobalAtomicLongGetInitialValue; - public LibGraalClassLoader getLoader() { + public ClassLoader getLoader() { return loader; } + public Class loadClassOrFail(Class c) { + if (c.getClassLoader() == loader) { + return c; + } + if (c.isArray()) { + return loadClassOrFail(c.getComponentType()).arrayType(); + } + return loadClassOrFail(c.getName()); + } + + public Class loadClassOrFail(String name) { + try { + return loader.loadClass(name); + } catch (ClassNotFoundException e) { + throw new AssertionError("%s unable to load class '%s'".formatted(loader.getName(), name)); + } + } + /** * Performs tasks once this feature is registered. *
    - *
  • Create the {@link LibGraalClassLoader} instance.
  • + *
  • Create the {@code HostedLibGraalClassLoader} instance.
  • *
  • Get a handle to the {@link BuildTime} class in the guest.
  • *
  • Initializes the options in the guest.
  • *
  • Initializes some state needed by {@link LibGraalSubstitutions}.
  • @@ -173,9 +185,10 @@ public void afterRegistration(AfterRegistrationAccess access) { // org.graalvm.nativeimage.impl.IsolateSupport accessModulesToClass(ModuleSupport.Access.EXPORT, LibGraalFeature.class, "org.graalvm.nativeimage"); - loader = new LibGraalClassLoader(Options.LibGraalJavaHome.getValue().resolve(Path.of("lib", "modules"))); + loader = createHostedLibGraalClassLoader(access); + ImageSingletons.lookup(ClassForNameSupport.class).setCustomLoader(loader); - buildTimeClass = loader.loadClassOrFail("jdk.graal.compiler.hotspot.libgraal.BuildTime"); + buildTimeClass = loadClassOrFail("jdk.graal.compiler.hotspot.libgraal.BuildTime"); // Guest JVMCI and Graal need access to some JDK internal packages String[] basePackages = {"jdk.internal.misc", "jdk.internal.util", "jdk.internal.vm"}; @@ -186,7 +199,7 @@ public void afterRegistration(AfterRegistrationAccess access) { * Get GlobalAtomicLong.getInitialValue() method from LibGraalClassLoader for * LibGraalGraalSubstitutions.GlobalAtomicLongAddressProvider FieldValueTransformer */ - handleGlobalAtomicLongGetInitialValue = mhl.findVirtual(loader.loadClassOrFail("jdk.graal.compiler.serviceprovider.GlobalAtomicLong"), + handleGlobalAtomicLongGetInitialValue = mhl.findVirtual(loadClassOrFail("jdk.graal.compiler.serviceprovider.GlobalAtomicLong"), "getInitialValue", methodType(long.class)); } catch (Throwable e) { @@ -194,6 +207,13 @@ public void afterRegistration(AfterRegistrationAccess access) { } } + @SuppressWarnings("unchecked") + private static ClassLoader 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); + } + private static void accessModulesToClass(ModuleSupport.Access access, Class accessingClass, String... moduleNames) { for (String moduleName : moduleNames) { var module = getBootModule(moduleName); @@ -208,17 +228,25 @@ static Module getBootModule(String moduleName) { @Override 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); + access.registerObjectReplacer(obj -> obj == loader ? customRuntimeLoader : obj); + try { - var basePhaseStatisticsClass = loader.loadClassOrFail("jdk.graal.compiler.phases.BasePhase$BasePhaseStatistics"); - var lirPhaseStatisticsClass = loader.loadClassOrFail("jdk.graal.compiler.lir.phases.LIRPhase$LIRPhaseStatistics"); + var basePhaseStatisticsClass = loadClassOrFail("jdk.graal.compiler.phases.BasePhase$BasePhaseStatistics"); + var lirPhaseStatisticsClass = loadClassOrFail("jdk.graal.compiler.lir.phases.LIRPhase$LIRPhaseStatistics"); MethodType statisticsCTorType = methodType(void.class, Class.class); var basePhaseStatisticsCTor = mhl.findConstructor(basePhaseStatisticsClass, statisticsCTorType); var lirPhaseStatisticsCTor = mhl.findConstructor(lirPhaseStatisticsClass, statisticsCTorType); newBasePhaseStatistics = new StatisticsCreator(basePhaseStatisticsCTor)::create; newLIRPhaseStatistics = new StatisticsCreator(lirPhaseStatisticsCTor)::create; - basePhaseClass = loader.loadClassOrFail("jdk.graal.compiler.phases.BasePhase"); - lirPhaseClass = loader.loadClassOrFail("jdk.graal.compiler.lir.phases.LIRPhase"); + basePhaseClass = loadClassOrFail("jdk.graal.compiler.phases.BasePhase"); + lirPhaseClass = loadClassOrFail("jdk.graal.compiler.lir.phases.LIRPhase"); ImageSingletons.add(LibGraalCompilerSupport.class, new LibGraalCompilerSupport()); } catch (Throwable e) { @@ -229,7 +257,7 @@ public void duringSetup(DuringSetupAccess access) { accessImpl.registerClassReachabilityListener(this::registerPhaseStatistics); optionCollector = new OptionCollector(LibGraalEntryPoints.vmOptionDescriptors); accessImpl.registerObjectReachableCallback(OptionKey.class, optionCollector::doCallback); - accessImpl.registerObjectReachableCallback(loader.loadClassOrFail(OptionKey.class.getName()), optionCollector::doCallback); + accessImpl.registerObjectReachableCallback(loadClassOrFail(OptionKey.class.getName()), optionCollector::doCallback); GetJNIConfig.register(loader); } @@ -240,7 +268,7 @@ public void duringSetup(DuringSetupAccess access) { * {@link OptionKey} instances reached by the static analysis. The VM options are instances of * {@link OptionKey} loaded by the {@link com.oracle.svm.hosted.NativeImageClassLoader} and * compiler options are instances of {@link OptionKey} loaded by the - * {@link LibGraalClassLoader}. + * {@code HostedLibGraalClassLoader}. */ private class OptionCollector implements ObjectReachableCallback { private final Set options = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -298,7 +326,7 @@ void afterAnalysis(AfterAnalysisAccess access) { try { MethodType mt = methodType(Iterable.class, List.class, Object.class, Map.class); MethodHandle mh = mhl.findStatic(buildTimeClass, "finalizeLibgraalOptions", mt); - Map modules = loader.getModules(); + Map modules = ReflectionUtil.invokeMethod(ReflectionUtil.lookupMethod(loader.getClass(), "getModules"), loader); Iterable values = (Iterable) mh.invoke(compilerOptions, compilerOptionsInfo, modules); for (Object descriptor : values) { VMError.guarantee(access.isReachable(descriptor.getClass()), "%s", descriptor.getClass()); @@ -347,11 +375,11 @@ public void beforeAnalysis(BeforeAnalysisAccess baa) { var bb = impl.getBigBang(); /* Contains static fields that depend on HotSpotJVMCIRuntime */ - RuntimeClassInitialization.initializeAtRunTime(loader.loadClassOrFail("jdk.vm.ci.hotspot.HotSpotModifiers")); - RuntimeClassInitialization.initializeAtRunTime(loader.loadClassOrFail("jdk.vm.ci.hotspot.HotSpotCompiledCodeStream")); - RuntimeClassInitialization.initializeAtRunTime(loader.loadClassOrFail("jdk.vm.ci.hotspot.HotSpotCompiledCodeStream$Tag")); + RuntimeClassInitialization.initializeAtRunTime(loadClassOrFail("jdk.vm.ci.hotspot.HotSpotModifiers")); + RuntimeClassInitialization.initializeAtRunTime(loadClassOrFail("jdk.vm.ci.hotspot.HotSpotCompiledCodeStream")); + RuntimeClassInitialization.initializeAtRunTime(loadClassOrFail("jdk.vm.ci.hotspot.HotSpotCompiledCodeStream$Tag")); /* ThreadLocal in static field jdk.graal.compiler.debug.DebugContext.activated */ - RuntimeClassInitialization.initializeAtRunTime(loader.loadClassOrFail("jdk.graal.compiler.debug.DebugContext")); + RuntimeClassInitialization.initializeAtRunTime(loadClassOrFail("jdk.graal.compiler.debug.DebugContext")); /* Needed for runtime calls to BoxingSnippets.Templates.getCacheClass(JavaKind) */ RuntimeReflection.registerAllDeclaredClasses(Character.class); @@ -377,7 +405,7 @@ public void beforeAnalysis(BeforeAnalysisAccess baa) { List> guestServiceClasses = new ArrayList<>(); List> serviceClasses = impl.getImageClassLoader().findAnnotatedClasses(LibGraalService.class, false); - serviceClasses.stream().map(c -> loader.loadClassOrFail(c.getName())).forEach(guestServiceClasses::add); + serviceClasses.stream().map(c -> loadClassOrFail(c.getName())).forEach(guestServiceClasses::add); // Transfer libgraal qualifier (e.g. "PGO optimized") from host to guest. String nativeImageLocationQualifier = CompilerConfigurationFactory.getNativeImageLocationQualifier(); @@ -392,13 +420,16 @@ public void beforeAnalysis(BeforeAnalysisAccess baa) { String.class, // nativeImageLocationQualifier byte[].class // encodedGuestObjects )); - GetCompilerConfig.Result configResult = GetCompilerConfig.from(Options.LibGraalJavaHome.getValue(), bb.getOptions()); + Path libGraalJavaHome = ReflectionUtil.readField(loader.getClass(), "libGraalJavaHome", loader); + GetCompilerConfig.Result configResult = GetCompilerConfig.from(libGraalJavaHome, bb.getOptions()); for (var e : configResult.opens().entrySet()) { for (String source : e.getValue()) { ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, buildTimeClass, false, e.getKey(), source); } } + ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, buildTimeClass, false, "org.graalvm.word", "org.graalvm.word.impl"); + configureGraalForLibGraal.invoke(arch, guestServiceClasses, registerAsInHeap, @@ -420,7 +451,7 @@ private static void initGraalRuntimeHandles(MethodHandle getRuntimeHandles) thro @SuppressWarnings("unchecked") private void initializeTruffle() throws Throwable { - Class truffleBuildTimeClass = loader.loadClassOrFail("jdk.graal.compiler.hotspot.libgraal.truffle.BuildTime"); + Class truffleBuildTimeClass = loadClassOrFail("jdk.graal.compiler.hotspot.libgraal.truffle.BuildTime"); MethodHandle getLookup = mhl.findStatic(truffleBuildTimeClass, "initializeLookup", methodType(Map.Entry.class, Lookup.class, Class.class, Class.class)); Map.Entry> truffleLibGraal = (Map.Entry>) getLookup.invoke(mhl, TruffleFromLibGraalStartPoints.class, NativeImageHostEntryPoints.class); ImageSingletons.add(LibGraalTruffleToLibGraalEntryPoints.class, new LibGraalTruffleToLibGraalEntryPoints(truffleLibGraal.getKey(), truffleLibGraal.getValue())); diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFieldsOffsetsFeature.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFieldsOffsetsFeature.java index cf55ea2b1f88..13810ffb231b 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFieldsOffsetsFeature.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFieldsOffsetsFeature.java @@ -62,7 +62,7 @@ public final class LibGraalFieldsOffsetsFeature implements InternalFeature { private final MethodHandles.Lookup mhl = MethodHandles.lookup(); - private LibGraalClassLoader loader; + private LibGraalFeature libGraalFeature; private Class fieldsClass; private Class edgesClass; @@ -119,20 +119,20 @@ private static Map getReplacements() { @Override public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; - loader = ImageSingletons.lookup(LibGraalFeature.class).loader; - - fieldsClass = loader.loadClassOrFail("jdk.graal.compiler.core.common.Fields"); - edgesClass = loader.loadClassOrFail("jdk.graal.compiler.graph.Edges"); - edgesTypeClass = loader.loadClassOrFail("jdk.graal.compiler.graph.Edges$Type"); - nodeClass = loader.loadClassOrFail("jdk.graal.compiler.graph.Node"); - nodeClassClass = loader.loadClassOrFail("jdk.graal.compiler.graph.NodeClass"); - lirInstructionClass = loader.loadClassOrFail("jdk.graal.compiler.lir.LIRInstruction"); - lirInstructionClassClass = loader.loadClassOrFail("jdk.graal.compiler.lir.LIRInstructionClass"); - compositeValueClass = loader.loadClassOrFail("jdk.graal.compiler.lir.CompositeValue"); - compositeValueClassClass = loader.loadClassOrFail("jdk.graal.compiler.lir.CompositeValueClass"); - fieldIntrospectionClass = loader.loadClassOrFail("jdk.graal.compiler.core.common.FieldIntrospection"); - inputEdgesClass = loader.loadClassOrFail("jdk.graal.compiler.graph.InputEdges"); - successorEdgesClass = loader.loadClassOrFail("jdk.graal.compiler.graph.SuccessorEdges"); + libGraalFeature = ImageSingletons.lookup(LibGraalFeature.class); + + fieldsClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.core.common.Fields"); + edgesClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.graph.Edges"); + edgesTypeClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.graph.Edges$Type"); + nodeClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.graph.Node"); + nodeClassClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.graph.NodeClass"); + lirInstructionClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.lir.LIRInstruction"); + lirInstructionClassClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.lir.LIRInstructionClass"); + compositeValueClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.lir.CompositeValue"); + compositeValueClassClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.lir.CompositeValueClass"); + fieldIntrospectionClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.core.common.FieldIntrospection"); + inputEdgesClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.graph.InputEdges"); + successorEdgesClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.graph.SuccessorEdges"); try { fieldsClassGetOffsetsMethod = mhl.findVirtual(fieldsClass, "getOffsets", MethodType.methodType(long[].class)); @@ -171,7 +171,7 @@ public void duringSetup(DuringSetupAccess a) { public void beforeAnalysis(BeforeAnalysisAccess access) { MethodHandle getInputEdgesOffsets; MethodHandle getSuccessorEdgesOffsets; - var buildTimeClass = loader.loadClassOrFail("jdk.graal.compiler.hotspot.libgraal.BuildTime"); + var buildTimeClass = libGraalFeature.loadClassOrFail("jdk.graal.compiler.hotspot.libgraal.BuildTime"); try { MethodType offsetAccessorSignature = MethodType.methodType(long[].class, Object.class); getInputEdgesOffsets = mhl.findStatic(buildTimeClass, "getInputEdgesOffsets", offsetAccessorSignature); diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalSubstitutions.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalSubstitutions.java index 2c2fb99e2fbd..c885d19b899b 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalSubstitutions.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalSubstitutions.java @@ -30,11 +30,6 @@ import java.util.Map; import java.util.function.Supplier; -import com.oracle.svm.core.heap.GCCause; -import com.oracle.svm.core.heap.Heap; -import com.oracle.svm.core.jdk.JDKLatest; -import com.oracle.svm.core.log.FunctionPointerLogHandler; -import com.oracle.svm.graal.hotspot.LibGraalJNIMethodScope; import org.graalvm.jniutils.JNI; import org.graalvm.jniutils.JNIExceptionWrapper; import org.graalvm.jniutils.JNIMethodScope; @@ -61,7 +56,12 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.heap.GCCause; +import com.oracle.svm.core.heap.Heap; +import com.oracle.svm.core.jdk.JDKLatest; +import com.oracle.svm.core.log.FunctionPointerLogHandler; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.graal.hotspot.LibGraalJNIMethodScope; class LibGraalJVMCISubstitutions { @@ -461,7 +461,7 @@ void afterRun() { class LibGraalClassLoaderSupplier implements Supplier { @Override public ClassLoader get() { - LibGraalClassLoader loader = ImageSingletons.lookup(LibGraalFeature.class).loader; + ClassLoader loader = ImageSingletons.lookup(LibGraalFeature.class).loader; VMError.guarantee(loader != null); return loader; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassForNameSupportFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassForNameSupportFeature.java new file mode 100644 index 000000000000..78e7b41febc2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassForNameSupportFeature.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.hosted; + +import org.graalvm.nativeimage.ImageSingletons; + +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; + +@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)); + } +} 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 6685997b4886..085c67d72731 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,6 +24,7 @@ */ package com.oracle.svm.hosted; +import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -34,6 +35,7 @@ import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.imagelayer.CrossLayerConstantRegistry; import com.oracle.svm.hosted.imagelayer.ObjectToConstantFieldValueTransformer; import com.oracle.svm.hosted.jdk.HostedClassLoaderPackageManagement; @@ -118,6 +120,17 @@ 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) { + /* + * CustomLoader provides runtime-replacement ClassLoader instance. Make sure + * customLoader gets replaced by customRuntimeLoader instance in image. + */ + access.registerObjectReplacer(obj -> obj == customLoader ? customRuntimeLoader : obj); + } + } access.registerObjectReplacer(this::runtimeClassLoaderObjectReplacer); if (ImageLayerBuildingSupport.buildingInitialLayer()) { config.registerObjectReachableCallback(ClassLoader.class, (a1, classLoader, reason) -> { @@ -133,6 +146,12 @@ 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 418e0d584e24..66ee578138c5 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 @@ -48,8 +48,8 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeatureServiceRegistration; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.option.APIOption; -import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; +import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.InterruptImageBuilding; import com.oracle.svm.core.util.UserError; @@ -182,12 +182,26 @@ 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()); + } + for (String featureName : Options.userEnabledFeatures()) { - Class featureClass; - try { - featureClass = Class.forName(featureName, true, loader.getClassLoader()); - } catch (ClassNotFoundException e) { - throw UserError.abort("Feature %s class not found on the classpath. Ensure that the name is correct and that the class is on the classpath.", featureName); + Class featureClass = null; + for (ClassLoader featureClassLoader : featureClassLoaders) { + try { + featureClass = Class.forName(featureName, true, featureClassLoader); + break; + } catch (ClassNotFoundException e) { + /* Ignore */ + } + } + if (featureClass == null) { + throw UserError.abort("User-enabled Feature %s class not found. Ensure that the name is correct and that the class is on the class- or module-path.", featureName); } registerFeature(featureClass, specificClassProvider, access); } 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 e243c31a2167..6081a621f713 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,6 +72,7 @@ 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; @@ -118,6 +119,7 @@ public class NativeImageClassLoaderSupport { private final ConcurrentHashMap> serviceProviders; private final NativeImageClassLoader classLoader; + private final ClassLoader customLoader; public final ModuleFinder upgradeAndSystemModuleFinder; public final ModuleLayer moduleLayerForImageBuild; @@ -187,6 +189,23 @@ 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; @@ -219,6 +238,10 @@ public NativeImageClassLoader getClassLoader() { return classLoader; } + public ClassLoader getCustomLoader() { + return customLoader; + } + private static Path stringToPath(String path) { return Path.of(Path.of(path).toAbsolutePath().toUri().normalize()); } @@ -246,6 +269,16 @@ public void loadAllClasses(ForkJoinPool executor, ImageClassLoader imageClassLoa includeAllFromClassPath = IncludeAllFromClassPath.getValue(parsedHostedOptions); 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); + } + } } private static void missingFromSetOfEntriesError(Object entry, Collection allEntries, String typeOfEntry, 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 5d80ffe544e6..bcbf1ccb6068 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,7 +63,6 @@ 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; @@ -73,6 +72,7 @@ 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;