Skip to content
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
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions compiler/mx.compiler/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,22 @@
"workingSets" : "Graal,Test",
"graalCompilerSourceEdition": "ignore",
},

"jdk.graal.compiler.libgraal.loader" : {
"subDir" : "src",
"sourceDirs" : ["src"],
"workingSets" : "Graal",
"javaCompliance" : "21+",
"dependencies" : [
"jdk.graal.compiler",
],
"requiresConcealed" : {
"java.base" : [
"jdk.internal.module",
"jdk.internal.jimage",
],
},
},
},

"distributions" : {
Expand Down Expand Up @@ -691,6 +707,17 @@
"graalCompilerSourceEdition": "ignore",
},

"LIBGRAAL_LOADER" : {
"subDir": "src",
"dependencies" : [
"jdk.graal.compiler.libgraal.loader",
],
"distDependencies" : [
"GRAAL",
],
"maven": False,
},

"GRAAL_PROFDIFF_TEST" : {
"subDir" : "src",
"dependencies" : [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,59 +38,58 @@
import java.nio.file.Path;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.util.VMError;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.svm.util.ModuleSupport;
import jdk.graal.compiler.debug.GraalError;
import jdk.internal.jimage.BasicImageReader;
import jdk.internal.jimage.ImageLocation;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import jdk.internal.module.Modules;

/**
* A classloader, that reads class files and resources from a jimage file at image build time.
*/
public class LibGraalClassLoader extends ClassLoader {
@Platforms(Platform.HOSTED_ONLY.class)
final class HostedLibGraalClassLoader extends ClassLoader {

private static final String JAVA_HOME_PROPERTY_KEY = "jdk.graal.internal.libgraal.javahome";
private static final String JAVA_HOME_PROPERTY_VALUE = System.getProperty(JAVA_HOME_PROPERTY_KEY, System.getProperty("java.home"));

/**
* Reader for the image.
*/
@Platforms(Platform.HOSTED_ONLY.class) //
private final BasicImageReader imageReader;

/**
* Map from the name of a resource (without module qualifier) to its path in the image.
*/
@Platforms(Platform.HOSTED_ONLY.class) //
private final Map<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;

/**
Expand All @@ -100,7 +99,6 @@ public class LibGraalClassLoader extends ClassLoader {
"jdk.internal.vm.ci",
"org.graalvm.collections",
"org.graalvm.word",
"org.graalvm.nativeimage",
"jdk.graal.compiler",
"org.graalvm.truffle.compiler",
"com.oracle.graal.graal_enterprise");
Expand All @@ -109,17 +107,29 @@ public class LibGraalClassLoader extends ClassLoader {
ClassLoader.registerAsParallelCapable();
}

/**
* @param imagePath path to the runtime image of a Java installation
*/
@Platforms(Platform.HOSTED_ONLY.class)
LibGraalClassLoader(Path imagePath) {
super("LibGraalClassLoader", null);
public final Path libGraalJavaHome;

public HostedLibGraalClassLoader() {
super(LibGraalClassLoader.LOADER_NAME, Feature.class.getClassLoader());
libGraalJavaHome = Path.of(JAVA_HOME_PROPERTY_VALUE);

Map<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.
*/
var javaBaseModule = Object.class.getModule();
Modules.addExports(javaBaseModule, "jdk.internal.jimage", HostedLibGraalClassLoader.class.getModule());

/*
* The classes that will get loaded by this loader require access to several internal
* packages of java.base. Make sure packages will be accessible to those classes.
*/
Module unnamedModuleOfThisLoader = getUnnamedModule();
Modules.addExports(javaBaseModule, "jdk.internal.vm", unnamedModuleOfThisLoader);
Modules.addExports(javaBaseModule, "jdk.internal.misc", unnamedModuleOfThisLoader);

Path imagePath = libGraalJavaHome.resolve(Path.of("lib", "modules"));
this.imageReader = BasicImageReader.open(imagePath);
for (var entry : imageReader.getEntryNames()) {
int secondSlash = entry.indexOf('/', 1);
Expand Down Expand Up @@ -152,15 +162,33 @@ public class LibGraalClassLoader extends ClassLoader {

/**
* Gets an unmodifiable map from the {@linkplain Class#forName(String) name} of a class to the
* name of its enclosing module.
* name of its enclosing module. Reflectively accessed by
* {@code LibGraalFeature.OptionCollector#afterAnalysis(AfterAnalysisAccess)}.
*/
@SuppressWarnings("unused")
public Map<String, String> getModules() {
return modules;
}

/* Allow image builder to perform registration action on each class this loader provides. */
@SuppressWarnings("unused")
public void forEachClass(Consumer<Class<?>> action) {
for (String className : classes.keySet()) {
if (className.equals("module-info")) {
continue;
}
try {
var clazz = loadClass(className);
action.accept(clazz);
} catch (ClassNotFoundException e) {
throw GraalError.shouldNotReachHere(e, LibGraalClassLoader.LOADER_NAME + " could not load class " + className);
}
}
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (!SubstrateUtil.HOSTED || !classes.containsKey(name)) {
if (!classes.containsKey(name)) {
return super.loadClass(name, resolve);
}
synchronized (getClassLoadingLock(name)) {
Expand All @@ -172,40 +200,18 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
}
}

@Platforms(Platform.HOSTED_ONLY.class)
public Class<?> loadClassOrFail(Class<?> c) {
if (c.getClassLoader() == this) {
return c;
}
if (c.isArray()) {
return loadClassOrFail(c.getComponentType()).arrayType();
}
return loadClassOrFail(c.getName());
}

@Platforms(Platform.HOSTED_ONLY.class)
public Class<?> loadClassOrFail(String name) {
try {
return loadClass(name);
} catch (ClassNotFoundException e) {
throw VMError.shouldNotReachHere("%s unable to load class '%s'", getName(), name);
}
}

@Override
protected Class<?> findClass(final String name)
throws ClassNotFoundException {
if (SubstrateUtil.HOSTED) {
String path = name.replace('.', '/').concat(".class");

String pathInImage = resources.get(path);
if (pathInImage != null) {
ImageLocation location = imageReader.findLocation(pathInImage);
if (location != null) {
ByteBuffer bb = Objects.requireNonNull(imageReader.getResourceBuffer(location));
ProtectionDomain pd = null;
return super.defineClass(name, bb, pd);
}
String path = name.replace('.', '/').concat(".class");

String pathInImage = resources.get(path);
if (pathInImage != null) {
ImageLocation location = imageReader.findLocation(pathInImage);
if (location != null) {
ByteBuffer bb = Objects.requireNonNull(imageReader.getResourceBuffer(location));
ProtectionDomain pd = null;
return super.defineClass(name, bb, pd);
}
}
throw new ClassNotFoundException(name);
Expand All @@ -222,35 +228,32 @@ protected Class<?> findClass(final String name)
*/
private static final String RESOURCE_PROTOCOL = "resource";

@Platforms(Platform.HOSTED_ONLY.class) //
private URLStreamHandler serviceHandler;

@Override
protected URL findResource(String name) {
if (SubstrateUtil.HOSTED) {
URLStreamHandler handler = this.serviceHandler;
if (handler == null) {
this.serviceHandler = handler = new ImageURLStreamHandler();
}
if (name.startsWith("META-INF/services/")) {
String service = name.substring("META-INF/services/".length());
if (services.containsKey(service)) {
try {
var uri = new URI(SERVICE_PROTOCOL, service, null);
return URL.of(uri, handler);
} catch (URISyntaxException | MalformedURLException e) {
return null;
}
URLStreamHandler handler = this.serviceHandler;
if (handler == null) {
this.serviceHandler = handler = new ImageURLStreamHandler();
}
if (name.startsWith("META-INF/services/")) {
String service = name.substring("META-INF/services/".length());
if (services.containsKey(service)) {
try {
var uri = new URI(SERVICE_PROTOCOL, service, null);
return URL.of(uri, handler);
} catch (URISyntaxException | MalformedURLException e) {
return null;
}
} else {
String path = resources.get(name);
if (path != null) {
try {
var uri = new URI(RESOURCE_PROTOCOL, name, null);
return URL.of(uri, handler);
} catch (URISyntaxException | MalformedURLException e) {
return null;
}
}
} else {
String path = resources.get(name);
if (path != null) {
try {
var uri = new URI(RESOURCE_PROTOCOL, name, null);
return URL.of(uri, handler);
} catch (URISyntaxException | MalformedURLException e) {
return null;
}
}
}
Expand All @@ -259,9 +262,6 @@ protected URL findResource(String name) {

@Override
protected Enumeration<URL> findResources(String name) throws IOException {
if (!SubstrateUtil.HOSTED) {
return Collections.emptyEnumeration();
}
return new Enumeration<>() {
private URL next = findResource(name);

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

/**
* A {@link URLStreamHandler} for use with URLs returned by
* {@link LibGraalClassLoader#findResource(java.lang.String)}.
* {@link HostedLibGraalClassLoader#findResource(java.lang.String)}.
*/
@Platforms(Platform.HOSTED_ONLY.class)
private class ImageURLStreamHandler extends URLStreamHandler {
@Override
public URLConnection openConnection(URL u) {
Expand All @@ -307,7 +306,6 @@ public URLConnection openConnection(URL u) {
}
}

@Platforms(Platform.HOSTED_ONLY.class)
private static class ImageURLConnection extends URLConnection {
private final byte[] bytes;
private InputStream in;
Expand Down Expand Up @@ -341,4 +339,23 @@ public String getContentType() {
return "application/octet-stream";
}
}

/**
* @return instance of ClassLoader that should be seen at image-runtime if a class was loaded at
* image-buildtime by this classloader.
*/
@SuppressWarnings("unused")
public static ClassLoader getRuntimeClassLoader() {
return LibGraalClassLoader.singleton;
}
}

public final class LibGraalClassLoader extends ClassLoader {

static final String LOADER_NAME = "LibGraalClassLoader";
static final LibGraalClassLoader singleton = new LibGraalClassLoader();

private LibGraalClassLoader() {
super(LOADER_NAME, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

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

@Platforms(Platform.HOSTED_ONLY.class)
public ResolvedJavaMethod findSnippetMethod(ResolvedJavaMethod thisMethod) {
if (snippetEncoder == null) {
throw new GraalError("findSnippetMethod called before initialization of Replacements");
Expand Down
1 change: 1 addition & 0 deletions substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,7 @@ def _native_image_launcher_extra_jvm_args():
libgraal_jar_distributions = [
'sdk:NATIVEBRIDGE',
'sdk:JNIUTILS',
'compiler:LIBGRAAL_LOADER',
'substratevm:LIBGRAAL_LIBRARY']

def allow_build_path_in_libgraal():
Expand Down
1 change: 0 additions & 1 deletion substratevm/mx.substratevm/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -1386,7 +1386,6 @@
],
"requiresConcealed" : {
"java.base" : [
"jdk.internal.jimage",
"jdk.internal.misc",
],
"jdk.internal.vm.ci" : [
Expand Down
Loading