-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GR-57627] Allow LibGraalFeature class to be loaded by HostedLibGraalClassLoader. #10049
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dd6c6d2
to
032c5f3
Compare
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;
032c5f3
to
45253c7
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Allow specifying FQN of custom classloader that gets used to lookup Features and
@CEntryPoint
definitions.Using this ability is only supported when building the libgraal shared library.
It is not meant to be used for regular image building.
When building the libgraal shared library this is used to specify a custom classloader
that the image builder then:
--features
option.@CEntryPoint
definitions in classes managed by this custom loader are registered.Command line demonstrating the current state:
The experimental classes
NewLibGraalFeature
andNewLibGraalEntryPoints
are not on this PR since we do not want to commit them to master.NewLibGraalEntryPoints.java
NewLibGraalFeature.java
A follow up PR will further refine this:
org.graalvm.nativeimage.experimental.loader
into a HostedOption with detailed description what setting a custom loader does.HostedLibGraalClassLoader#forEachClass
withHostedLibGraalClassLoader#getClasses
that just returns aCollection<String>
and make an interface forgetClasses
andgetRuntimeClassLoader
that is defined in the compiler module