Skip to content

Commit 45253c7

Browse files
committed
Refactor LibGraalClassLoader to be usable as customLoader
diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py index 6fa9c61e2f5..7c4d8918601 100644 --- a/compiler/mx.compiler/suite.py +++ b/compiler/mx.compiler/suite.py @@ -465,6 +465,22 @@ suite = { "workingSets" : "Graal,Test", "graalCompilerSourceEdition": "ignore", }, + + "jdk.graal.compiler.libgraal" : { + "subDir" : "src", + "sourceDirs" : ["src"], + "workingSets" : "Graal", + "javaCompliance" : "21+", + "dependencies" : [ + "jdk.graal.compiler", + ], + "requiresConcealed" : { + "java.base" : [ + "jdk.internal.module", + "jdk.internal.jimage", + ], + }, + }, }, "distributions" : { @@ -581,6 +597,7 @@ suite = { "GRAAL_VERSION", ], "distDependencies" : [ + "sdk:NATIVEIMAGE", "sdk:COLLECTIONS", "sdk:WORD", "sdk:NATIVEIMAGE", @@ -691,6 +708,17 @@ suite = { "graalCompilerSourceEdition": "ignore", }, + "GRAAL_LIBGRAAL" : { + "subDir": "src", + "dependencies" : [ + "jdk.graal.compiler.libgraal", + ], + "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/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java similarity index 70% 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/src/jdk/graal/compiler/hotspot/libgraal/LibGraalClassLoader.java index aaa171c742a..2c1e53e6850 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/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.ByteBuffer; 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; @@ -47,50 +46,49 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; -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 = "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<String, String> 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<String, String> classes = new HashMap<>(); /** * Map from a service name to a list of providers. */ - @Platforms(Platform.HOSTED_ONLY.class) // private final Map<String, List<String>> 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<String, String> modules; /** @@ -109,17 +107,22 @@ 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<String, String> 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. + */ + Modules.addExports(Object.class.getModule(), "jdk.internal.jimage", HostedLibGraalClassLoader.class.getModule()); + + Modules.addExports(Object.class.getModule(), "jdk.internal.misc", HostedLibGraalClassLoader.class.getModule()); + + 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 +155,28 @@ 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<String, String> getModules() { return modules; } + @SuppressWarnings("unused") + public List<Class<?>> getSystemClasses() { + Class<?> clazz = null; + try { + clazz = loadClass("jdk.graal.compiler.hotspot.libgraal.NewLibGraalEntryPoints"); + } catch (ClassNotFoundException e) { + throw GraalError.shouldNotReachHere("FIXME"); + } + return List.of(clazz); + } + @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 +188,18 @@ public class LibGraalClassLoader extends ClassLoader { } } - @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 +216,32 @@ public class LibGraalClassLoader extends ClassLoader { */ 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 +250,6 @@ public class LibGraalClassLoader extends ClassLoader { @Override protected Enumeration<URL> findResources(String name) throws IOException { - if (!SubstrateUtil.HOSTED) { - return Collections.emptyEnumeration(); - } return new Enumeration<>() { private URL next = findResource(name); @@ -284,9 +272,8 @@ public class LibGraalClassLoader extends ClassLoader { /** * 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 +294,6 @@ public class LibGraalClassLoader extends ClassLoader { } } - @Platforms(Platform.HOSTED_ONLY.class) private static class ImageURLConnection extends URLConnection { private final byte[] bytes; private InputStream in; @@ -341,4 +327,23 @@ public class LibGraalClassLoader extends ClassLoader { 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/libgraal/NewLibGraalEntryPoints.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/NewLibGraalEntryPoints.java new file mode 100644 index 00000000000..e5a5ab499ef --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/NewLibGraalEntryPoints.java @@ -0,0 +1,36 @@ +package jdk.graal.compiler.hotspot.libgraal; + +import java.util.function.BooleanSupplier; + +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.c.function.CEntryPoint; + +import jdk.graal.compiler.core.phases.EconomyLowTier; + +public class NewLibGraalEntryPoints { + + private static class LoadedByLibGraalClassLoader implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + ClassLoader loader = NewLibGraalEntryPoints.class.getClassLoader(); + return loader != null && "LibGraalClassLoader".equals(loader.getName()); + } + } + + @CEntryPoint(name = "runMain", include = LoadedByLibGraalClassLoader.class) + public static int runMain(@SuppressWarnings("unused") IsolateThread thread) { + System.out.println("Hello from jdk.graal.compiler.hotspot.libgraal.NewLibGraalEntryPoints.runMain"); + System.out.println("EconomyLowTier.class.getClassLoader() = " + EconomyLowTier.class.getClassLoader()); + try { + CompilerConfig.main(new String[]{"CompilerConfig.out"}); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + @CEntryPoint(name = "java_check_heap", include = LoadedByLibGraalClassLoader.class) + protected static int checkHeap(@SuppressWarnings("unused") IsolateThread thread) { + return 0; + } +} \ No newline at end of file diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/NewLibGraalFeature.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/NewLibGraalFeature.java new file mode 100644 index 00000000000..0fbc82d734d --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/NewLibGraalFeature.java @@ -0,0 +1,27 @@ +package jdk.graal.compiler.hotspot.libgraal; + +import java.io.PrintStream; + +import org.graalvm.nativeimage.hosted.Feature; + +import jdk.graal.compiler.core.phases.CommunityCompilerConfiguration; +import jdk.graal.compiler.debug.GraalError; + +public class NewLibGraalFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + System.out.println("access.getApplicationClassLoader() = " + access.getApplicationClassLoader()); + ClassLoader loader = NewLibGraalFeature.class.getClassLoader(); + System.out.println("CommunityCompilerConfiguration.class.getClassLoader() = " + CommunityCompilerConfiguration.class.getClassLoader()); + if (loader == null || !"HostedLibGraalClassLoader".equals(loader.getClass().getSimpleName())) { + throw GraalError.shouldNotReachHere("NewLibGraalFeature was not loaded by HostedLibGraalClassLoader"); + } + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + access.registerSubtypeReachabilityHandler((duringAnalysisAccess, aClass) -> { + System.out.println(aClass.getName() + " reachable"); + }, PrintStream.class); + } +} diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index e685f649bc8..d930dbb8220 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1468,6 +1468,7 @@ mx_sdk_vm.register_graalvm_component(mx_sdk_vm.GraalVMSvmMacro( libgraal_jar_distributions = [ 'sdk:NATIVEBRIDGE', 'sdk:JNIUTILS', + 'compiler:GRAAL_LIBGRAAL', 'substratevm:LIBGRAAL_LIBRARY'] def allow_build_path_in_libgraal(): diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 12feca34223..58b4a8b610d 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -298,6 +298,7 @@ suite = { "sun.util.resources", "jdk.internal.access", "jdk.internal.event", + "jdk.internal.jimage", "jdk.internal.loader", "jdk.internal.logger", "jdk.internal.misc", @@ -1386,7 +1387,6 @@ suite = { ], "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 7420631d361..120fe5a356e 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 java.util.Objects; 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.reflect.MissingReflectionRegistrationUtils; 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 @@ public final class ClassForNameSupport implements MultiLayeredImageSingleton, Un } } - 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<Object> updateConditionalValue(ConditionalRuntimeValue<Object> 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 0c341c9ca9b..bb3d3bcd51f 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.AnalysisType; 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; @@ -87,7 +88,7 @@ import jdk.vm.ci.code.TargetDescription; * <p> * This feature is composed of these key classes: * <ul> - * <li>{@link LibGraalClassLoader}</li> + * <li>{@code HostedLibGraalClassLoader}</li> * <li>{@link LibGraalEntryPoints}</li> * <li>{@link LibGraalSubstitutions}</li> * </ul> @@ -95,14 +96,6 @@ import jdk.vm.ci.code.TargetDescription; @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<Path> LibGraalJavaHome = new HostedOptionKey<>(Path.of(System.getProperty("java.home"))); - } - public static final class IsEnabled implements BooleanSupplier { @Override public boolean getAsBoolean() { @@ -120,7 +113,7 @@ public final class LibGraalFeature implements Feature { /** * Loader used for loading classes from the guest GraalVM. */ - LibGraalClassLoader loader; + ClassLoader loader; /** * Handle to {@link BuildTime} in the guest. @@ -143,14 +136,32 @@ public final class LibGraalFeature implements Feature { 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. * <ul> - * <li>Create the {@link LibGraalClassLoader} instance.</li> + * <li>Create the {@code HostedLibGraalClassLoader} instance.</li> * <li>Get a handle to the {@link BuildTime} class in the guest.</li> * <li>Initializes the options in the guest.</li> * <li>Initializes some state needed by {@link LibGraalSubstitutions}.</li> @@ -173,9 +184,10 @@ public final class LibGraalFeature implements Feature { // 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 +198,7 @@ public final class LibGraalFeature implements Feature { * 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 +206,13 @@ public final class LibGraalFeature implements Feature { } } + @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<ClassLoader>) hostedLibGraalClassLoaderClass); + } + private static void accessModulesToClass(ModuleSupport.Access access, Class<?> accessingClass, String... moduleNames) { for (String moduleName : moduleNames) { var module = getBootModule(moduleName); @@ -208,17 +227,25 @@ public final class LibGraalFeature implements Feature { @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 +256,7 @@ public final class LibGraalFeature implements Feature { 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 +267,7 @@ public final class LibGraalFeature implements Feature { * {@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<Object> { private final Set<Object> options = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -298,7 +325,7 @@ public final class LibGraalFeature implements Feature { try { MethodType mt = methodType(Iterable.class, List.class, Object.class, Map.class); MethodHandle mh = mhl.findStatic(buildTimeClass, "finalizeLibgraalOptions", mt); - Map<String, String> modules = loader.getModules(); + Map<String, String> modules = ReflectionUtil.invokeMethod(ReflectionUtil.lookupMethod(loader.getClass(), "getModules"), loader); Iterable<Object> values = (Iterable<Object>) mh.invoke(compilerOptions, compilerOptionsInfo, modules); for (Object descriptor : values) { VMError.guarantee(access.isReachable(descriptor.getClass()), "%s", descriptor.getClass()); @@ -347,11 +374,11 @@ public final class LibGraalFeature implements Feature { 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 +404,7 @@ public final class LibGraalFeature implements Feature { List<Class<?>> guestServiceClasses = new ArrayList<>(); List<Class<?>> 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 +419,16 @@ public final class LibGraalFeature implements Feature { 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 +450,7 @@ public final class LibGraalFeature implements Feature { @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<Lookup, Class<?>> truffleLibGraal = (Map.Entry<Lookup, Class<?>>) 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 cf55ea2b1f8..13810ffb231 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 @@ import jdk.internal.misc.Unsafe; 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 @@ public final class LibGraalFieldsOffsetsFeature implements InternalFeature { @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 final class LibGraalFieldsOffsetsFeature implements InternalFeature { 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 2c2fb99e2fb..c885d19b899 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.lang.ref.ReferenceQueue; 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.TargetClass; 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 @@ public class LibGraalSubstitutions { class LibGraalClassLoaderSupplier implements Supplier<ClassLoader> { @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 00000000000..521e2fe4265 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassForNameSupportFeature.java @@ -0,0 +1,23 @@ +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.LoadedLayeredImageSingletonInfo; +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) { + if (ImageSingletons.lookup(LoadedLayeredImageSingletonInfo.class).handledDuringLoading(ClassForNameSupport.class)) { + return; + } + ClassLoader customLoader = ((AfterRegistrationAccessImpl) access).getImageClassLoader().classLoaderSupport.getCustomLoader(); + ImageSingletons.add(ClassForNameSupport.class, new ClassForNameSupport(customLoader)); + } +} \ No newline at end of file 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 6685997b488..085c67d7273 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.feature.InternalFeature; 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 class ClassLoaderFeature implements InternalFeature { 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 class ClassLoaderFeature implements InternalFeature { } } + public static ClassLoader getCustomRuntimeClassLoader(ClassLoader customLoader) { + Class<? extends ClassLoader> 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 418e0d584e2..250f5abf07f 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.AutomaticallyRegisteredFeature; 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; @@ -183,11 +183,24 @@ public class FeatureHandler { } 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); + ClassLoader customLoader = loader.classLoaderSupport.getCustomLoader(); + List<ClassLoader> featureClassLoaders; + if (customLoader != null) { + featureClassLoaders = List.of(customLoader, loader.getClassLoader()); + } else { + featureClassLoaders = List.of(loader.getClassLoader()); + } + 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/ImageClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java index c55efe36d59..fc87c58847d 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 @@ -99,7 +99,7 @@ public final class ImageClassLoader { classLoaderSupport.reportBuilderClassesInApplication(); } - private void findSystemElements(Class<?> systemClass) { + void findSystemElements(Class<?> systemClass) { Method[] declaredMethods = null; try { declaredMethods = systemClass.getDeclaredMethods(); 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 e243c31a216..9611c625370 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 @@ -118,6 +118,7 @@ public class NativeImageClassLoaderSupport { private final ConcurrentHashMap<String, LinkedHashSet<String>> serviceProviders; private final NativeImageClassLoader classLoader; + private final ClassLoader customLoader; public final ModuleFinder upgradeAndSystemModuleFinder; public final ModuleLayer moduleLayerForImageBuild; @@ -187,6 +188,23 @@ public class NativeImageClassLoaderSupport { 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 +237,10 @@ public class NativeImageClassLoaderSupport { return classLoader; } + public ClassLoader getCustomLoader() { + return customLoader; + } + private static Path stringToPath(String path) { return Path.of(Path.of(path).toAbsolutePath().toUri().normalize()); } @@ -246,6 +268,16 @@ public class NativeImageClassLoaderSupport { includeAllFromClassPath = IncludeAllFromClassPath.getValue(parsedHostedOptions); new LoadClassHandler(executor, imageClassLoader).run(); + if (customLoader != null) { + Class<? extends ClassLoader> customLoaderClass = customLoader.getClass(); + Method getSystemClassesMethod = ReflectionUtil.lookupMethod(true, customLoaderClass, "getSystemClasses"); + if (getSystemClassesMethod != null) { + List<Class<?>> customSystemClasses = ReflectionUtil.invokeMethod(getSystemClassesMethod, customLoader); + for (Class<?> systemClass : customSystemClasses) { + imageClassLoader.findSystemElements(systemClass); + } + } + } } 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 5d80ffe544e..bcbf1ccb606 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.feature.InternalFeature; 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.SubstrateConstructorAccessor; 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;
1 parent 8dc6edf commit 45253c7

File tree

14 files changed

+348
-160
lines changed

14 files changed

+348
-160
lines changed

compiler/mx.compiler/suite.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,22 @@
465465
"workingSets" : "Graal,Test",
466466
"graalCompilerSourceEdition": "ignore",
467467
},
468+
469+
"jdk.graal.compiler.libgraal.loader" : {
470+
"subDir" : "src",
471+
"sourceDirs" : ["src"],
472+
"workingSets" : "Graal",
473+
"javaCompliance" : "21+",
474+
"dependencies" : [
475+
"jdk.graal.compiler",
476+
],
477+
"requiresConcealed" : {
478+
"java.base" : [
479+
"jdk.internal.module",
480+
"jdk.internal.jimage",
481+
],
482+
},
483+
},
468484
},
469485

470486
"distributions" : {
@@ -691,6 +707,17 @@
691707
"graalCompilerSourceEdition": "ignore",
692708
},
693709

710+
"LIBGRAAL_LOADER" : {
711+
"subDir": "src",
712+
"dependencies" : [
713+
"jdk.graal.compiler.libgraal.loader",
714+
],
715+
"distDependencies" : [
716+
"GRAAL",
717+
],
718+
"maven": False,
719+
},
720+
694721
"GRAAL_PROFDIFF_TEST" : {
695722
"subDir" : "src",
696723
"dependencies" : [
Lines changed: 103 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* or visit www.oracle.com if you need additional information or have any
2323
* questions.
2424
*/
25-
package com.oracle.svm.graal.hotspot.libgraal;
25+
package jdk.graal.compiler.hotspot.libgraal;
2626

2727
import java.io.ByteArrayInputStream;
2828
import java.io.IOException;
@@ -38,59 +38,58 @@
3838
import java.nio.file.Path;
3939
import java.security.ProtectionDomain;
4040
import java.util.ArrayList;
41-
import java.util.Collections;
4241
import java.util.Enumeration;
4342
import java.util.HashMap;
4443
import java.util.List;
4544
import java.util.Map;
4645
import java.util.NoSuchElementException;
4746
import java.util.Objects;
4847
import java.util.Set;
48+
import java.util.function.Consumer;
4949

50-
import com.oracle.svm.core.SubstrateUtil;
51-
import com.oracle.svm.core.util.VMError;
50+
import org.graalvm.nativeimage.Platform;
51+
import org.graalvm.nativeimage.Platforms;
52+
import org.graalvm.nativeimage.hosted.Feature;
5253

53-
import com.oracle.svm.util.ModuleSupport;
54+
import jdk.graal.compiler.debug.GraalError;
5455
import jdk.internal.jimage.BasicImageReader;
5556
import jdk.internal.jimage.ImageLocation;
56-
import org.graalvm.nativeimage.Platform;
57-
import org.graalvm.nativeimage.Platforms;
57+
import jdk.internal.module.Modules;
5858

5959
/**
6060
* A classloader, that reads class files and resources from a jimage file at image build time.
6161
*/
62-
public class LibGraalClassLoader extends ClassLoader {
62+
@Platforms(Platform.HOSTED_ONLY.class)
63+
final class HostedLibGraalClassLoader extends ClassLoader {
64+
65+
private static final String JAVA_HOME_PROPERTY_KEY = "jdk.graal.internal.libgraal.javahome";
66+
private static final String JAVA_HOME_PROPERTY_VALUE = System.getProperty(JAVA_HOME_PROPERTY_KEY, System.getProperty("java.home"));
6367

6468
/**
6569
* Reader for the image.
6670
*/
67-
@Platforms(Platform.HOSTED_ONLY.class) //
6871
private final BasicImageReader imageReader;
6972

7073
/**
7174
* Map from the name of a resource (without module qualifier) to its path in the image.
7275
*/
73-
@Platforms(Platform.HOSTED_ONLY.class) //
7476
private final Map<String, String> resources = new HashMap<>();
7577

7678
/**
7779
* Map from the {@linkplain Class#forName(String) name} of a class to the image path of its
7880
* class file.
7981
*/
80-
@Platforms(Platform.HOSTED_ONLY.class) //
8182
private final Map<String, String> classes = new HashMap<>();
8283

8384
/**
8485
* Map from a service name to a list of providers.
8586
*/
86-
@Platforms(Platform.HOSTED_ONLY.class) //
8787
private final Map<String, List<String>> services = new HashMap<>();
8888

8989
/**
9090
* Map from the {@linkplain Class#forName(String) name} of a class to the name of its enclosing
9191
* module.
9292
*/
93-
@Platforms(Platform.HOSTED_ONLY.class) //
9493
private final Map<String, String> modules;
9594

9695
/**
@@ -100,7 +99,6 @@ public class LibGraalClassLoader extends ClassLoader {
10099
"jdk.internal.vm.ci",
101100
"org.graalvm.collections",
102101
"org.graalvm.word",
103-
"org.graalvm.nativeimage",
104102
"jdk.graal.compiler",
105103
"org.graalvm.truffle.compiler",
106104
"com.oracle.graal.graal_enterprise");
@@ -109,17 +107,29 @@ public class LibGraalClassLoader extends ClassLoader {
109107
ClassLoader.registerAsParallelCapable();
110108
}
111109

112-
/**
113-
* @param imagePath path to the runtime image of a Java installation
114-
*/
115-
@Platforms(Platform.HOSTED_ONLY.class)
116-
LibGraalClassLoader(Path imagePath) {
117-
super("LibGraalClassLoader", null);
110+
public final Path libGraalJavaHome;
111+
112+
public HostedLibGraalClassLoader() {
113+
super(LibGraalClassLoader.LOADER_NAME, Feature.class.getClassLoader());
114+
libGraalJavaHome = Path.of(JAVA_HOME_PROPERTY_VALUE);
115+
118116
Map<String, String> modulesMap = new HashMap<>();
119117
try {
120-
// Need access to jdk.internal.jimage
121-
ModuleSupport.accessPackagesToClass(ModuleSupport.Access.EXPORT, getClass(), false,
122-
"java.base", "jdk.internal.jimage");
118+
/*
119+
* Access to jdk.internal.jimage classes is needed by this Classloader implementation.
120+
*/
121+
var javaBaseModule = Object.class.getModule();
122+
Modules.addExports(javaBaseModule, "jdk.internal.jimage", HostedLibGraalClassLoader.class.getModule());
123+
124+
/*
125+
* The classes that will get loaded by this loader require access to several internal
126+
* packages of java.base. Make sure packages will be accessible to those classes.
127+
*/
128+
Module unnamedModuleOfThisLoader = getUnnamedModule();
129+
Modules.addExports(javaBaseModule, "jdk.internal.vm", unnamedModuleOfThisLoader);
130+
Modules.addExports(javaBaseModule, "jdk.internal.misc", unnamedModuleOfThisLoader);
131+
132+
Path imagePath = libGraalJavaHome.resolve(Path.of("lib", "modules"));
123133
this.imageReader = BasicImageReader.open(imagePath);
124134
for (var entry : imageReader.getEntryNames()) {
125135
int secondSlash = entry.indexOf('/', 1);
@@ -152,15 +162,33 @@ public class LibGraalClassLoader extends ClassLoader {
152162

153163
/**
154164
* Gets an unmodifiable map from the {@linkplain Class#forName(String) name} of a class to the
155-
* name of its enclosing module.
165+
* name of its enclosing module. Reflectively accessed by
166+
* {@code LibGraalFeature.OptionCollector#afterAnalysis(AfterAnalysisAccess)}.
156167
*/
168+
@SuppressWarnings("unused")
157169
public Map<String, String> getModules() {
158170
return modules;
159171
}
160172

173+
/* Allow image builder to perform registration action on each class this loader provides. */
174+
@SuppressWarnings("unused")
175+
public void forEachClass(Consumer<Class<?>> action) {
176+
for (String className : classes.keySet()) {
177+
if (className.equals("module-info")) {
178+
continue;
179+
}
180+
try {
181+
var clazz = loadClass(className);
182+
action.accept(clazz);
183+
} catch (ClassNotFoundException e) {
184+
throw GraalError.shouldNotReachHere(e, LibGraalClassLoader.LOADER_NAME + " could not load class " + className);
185+
}
186+
}
187+
}
188+
161189
@Override
162190
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
163-
if (!SubstrateUtil.HOSTED || !classes.containsKey(name)) {
191+
if (!classes.containsKey(name)) {
164192
return super.loadClass(name, resolve);
165193
}
166194
synchronized (getClassLoadingLock(name)) {
@@ -172,40 +200,18 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
172200
}
173201
}
174202

175-
@Platforms(Platform.HOSTED_ONLY.class)
176-
public Class<?> loadClassOrFail(Class<?> c) {
177-
if (c.getClassLoader() == this) {
178-
return c;
179-
}
180-
if (c.isArray()) {
181-
return loadClassOrFail(c.getComponentType()).arrayType();
182-
}
183-
return loadClassOrFail(c.getName());
184-
}
185-
186-
@Platforms(Platform.HOSTED_ONLY.class)
187-
public Class<?> loadClassOrFail(String name) {
188-
try {
189-
return loadClass(name);
190-
} catch (ClassNotFoundException e) {
191-
throw VMError.shouldNotReachHere("%s unable to load class '%s'", getName(), name);
192-
}
193-
}
194-
195203
@Override
196204
protected Class<?> findClass(final String name)
197205
throws ClassNotFoundException {
198-
if (SubstrateUtil.HOSTED) {
199-
String path = name.replace('.', '/').concat(".class");
200-
201-
String pathInImage = resources.get(path);
202-
if (pathInImage != null) {
203-
ImageLocation location = imageReader.findLocation(pathInImage);
204-
if (location != null) {
205-
ByteBuffer bb = Objects.requireNonNull(imageReader.getResourceBuffer(location));
206-
ProtectionDomain pd = null;
207-
return super.defineClass(name, bb, pd);
208-
}
206+
String path = name.replace('.', '/').concat(".class");
207+
208+
String pathInImage = resources.get(path);
209+
if (pathInImage != null) {
210+
ImageLocation location = imageReader.findLocation(pathInImage);
211+
if (location != null) {
212+
ByteBuffer bb = Objects.requireNonNull(imageReader.getResourceBuffer(location));
213+
ProtectionDomain pd = null;
214+
return super.defineClass(name, bb, pd);
209215
}
210216
}
211217
throw new ClassNotFoundException(name);
@@ -222,35 +228,32 @@ protected Class<?> findClass(final String name)
222228
*/
223229
private static final String RESOURCE_PROTOCOL = "resource";
224230

225-
@Platforms(Platform.HOSTED_ONLY.class) //
226231
private URLStreamHandler serviceHandler;
227232

228233
@Override
229234
protected URL findResource(String name) {
230-
if (SubstrateUtil.HOSTED) {
231-
URLStreamHandler handler = this.serviceHandler;
232-
if (handler == null) {
233-
this.serviceHandler = handler = new ImageURLStreamHandler();
234-
}
235-
if (name.startsWith("META-INF/services/")) {
236-
String service = name.substring("META-INF/services/".length());
237-
if (services.containsKey(service)) {
238-
try {
239-
var uri = new URI(SERVICE_PROTOCOL, service, null);
240-
return URL.of(uri, handler);
241-
} catch (URISyntaxException | MalformedURLException e) {
242-
return null;
243-
}
235+
URLStreamHandler handler = this.serviceHandler;
236+
if (handler == null) {
237+
this.serviceHandler = handler = new ImageURLStreamHandler();
238+
}
239+
if (name.startsWith("META-INF/services/")) {
240+
String service = name.substring("META-INF/services/".length());
241+
if (services.containsKey(service)) {
242+
try {
243+
var uri = new URI(SERVICE_PROTOCOL, service, null);
244+
return URL.of(uri, handler);
245+
} catch (URISyntaxException | MalformedURLException e) {
246+
return null;
244247
}
245-
} else {
246-
String path = resources.get(name);
247-
if (path != null) {
248-
try {
249-
var uri = new URI(RESOURCE_PROTOCOL, name, null);
250-
return URL.of(uri, handler);
251-
} catch (URISyntaxException | MalformedURLException e) {
252-
return null;
253-
}
248+
}
249+
} else {
250+
String path = resources.get(name);
251+
if (path != null) {
252+
try {
253+
var uri = new URI(RESOURCE_PROTOCOL, name, null);
254+
return URL.of(uri, handler);
255+
} catch (URISyntaxException | MalformedURLException e) {
256+
return null;
254257
}
255258
}
256259
}
@@ -259,9 +262,6 @@ protected URL findResource(String name) {
259262

260263
@Override
261264
protected Enumeration<URL> findResources(String name) throws IOException {
262-
if (!SubstrateUtil.HOSTED) {
263-
return Collections.emptyEnumeration();
264-
}
265265
return new Enumeration<>() {
266266
private URL next = findResource(name);
267267

@@ -284,9 +284,8 @@ public URL nextElement() {
284284

285285
/**
286286
* A {@link URLStreamHandler} for use with URLs returned by
287-
* {@link LibGraalClassLoader#findResource(java.lang.String)}.
287+
* {@link HostedLibGraalClassLoader#findResource(java.lang.String)}.
288288
*/
289-
@Platforms(Platform.HOSTED_ONLY.class)
290289
private class ImageURLStreamHandler extends URLStreamHandler {
291290
@Override
292291
public URLConnection openConnection(URL u) {
@@ -307,7 +306,6 @@ public URLConnection openConnection(URL u) {
307306
}
308307
}
309308

310-
@Platforms(Platform.HOSTED_ONLY.class)
311309
private static class ImageURLConnection extends URLConnection {
312310
private final byte[] bytes;
313311
private InputStream in;
@@ -341,4 +339,23 @@ public String getContentType() {
341339
return "application/octet-stream";
342340
}
343341
}
342+
343+
/**
344+
* @return instance of ClassLoader that should be seen at image-runtime if a class was loaded at
345+
* image-buildtime by this classloader.
346+
*/
347+
@SuppressWarnings("unused")
348+
public static ClassLoader getRuntimeClassLoader() {
349+
return LibGraalClassLoader.singleton;
350+
}
351+
}
352+
353+
public final class LibGraalClassLoader extends ClassLoader {
354+
355+
static final String LOADER_NAME = "LibGraalClassLoader";
356+
static final LibGraalClassLoader singleton = new LibGraalClassLoader();
357+
358+
private LibGraalClassLoader() {
359+
super(LOADER_NAME, null);
360+
}
344361
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@
6464
import jdk.vm.ci.meta.MetaAccessProvider;
6565
import jdk.vm.ci.meta.ResolvedJavaMethod;
6666
import jdk.vm.ci.meta.ResolvedJavaType;
67-
import org.graalvm.nativeimage.Platform;
68-
import org.graalvm.nativeimage.Platforms;
6967

7068
/**
7169
* Filters certain method substitutions based on whether there is underlying hardware support for
@@ -276,7 +274,6 @@ public <T> T getInjectedArgument(Class<T> capability) {
276274
return super.getInjectedArgument(capability);
277275
}
278276

279-
@Platforms(Platform.HOSTED_ONLY.class)
280277
public ResolvedJavaMethod findSnippetMethod(ResolvedJavaMethod thisMethod) {
281278
if (snippetEncoder == null) {
282279
throw new GraalError("findSnippetMethod called before initialization of Replacements");

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,7 @@ def _native_image_launcher_extra_jvm_args():
14681468
libgraal_jar_distributions = [
14691469
'sdk:NATIVEBRIDGE',
14701470
'sdk:JNIUTILS',
1471+
'compiler:LIBGRAAL_LOADER',
14711472
'substratevm:LIBGRAAL_LIBRARY']
14721473

14731474
def allow_build_path_in_libgraal():

substratevm/mx.substratevm/suite.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,6 @@
13861386
],
13871387
"requiresConcealed" : {
13881388
"java.base" : [
1389-
"jdk.internal.jimage",
13901389
"jdk.internal.misc",
13911390
],
13921391
"jdk.internal.vm.ci" : [

0 commit comments

Comments
 (0)