Skip to content

Commit 9ec1aec

Browse files
committed
reduced LibGraalLoader API
* renamed LibGraalLoader.getModuleMap to getClassModuleMap * replaced LibGraalLoader.getJavaHome with a protocol shared between HostedLibGraalClassLoader and LibGraalFeature * removed LibGraalLoader.getRuntimeClassLoader * remove LibGraalLoader.getServiceModules
1 parent 4880899 commit 9ec1aec

File tree

10 files changed

+91
-106
lines changed

10 files changed

+91
-106
lines changed

compiler/src/jdk.graal.compiler.libgraal.loader/src/jdk/graal/compiler/libgraal/loader/HostedLibGraalClassLoader.java

+33-32
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@
5757
import java.util.Map;
5858
import java.util.Optional;
5959
import java.util.Set;
60-
import java.util.stream.Collectors;
6160
import java.util.stream.Stream;
6261

6362
/**
6463
* A classloader that reads class files and resources from a jimage file and a module path at image
65-
* build time.
64+
* build time. The {@code java.home} of the JDK containing the jimage can be obtained by converting
65+
* the bytes of {@code getResourceAsStream("META-INF/libgraal.java.home")} to a string.
6666
*/
6767
@Platforms(Platform.HOSTED_ONLY.class)
6868
public final class HostedLibGraalClassLoader extends ClassLoader implements LibGraalLoader {
@@ -73,6 +73,12 @@ public final class HostedLibGraalClassLoader extends ClassLoader implements LibG
7373
*/
7474
private static final String LIBGRAAL_JAVA_HOME_PROPERTY_NAME = "libgraal.java.home";
7575

76+
/**
77+
* The {@code java.home} of the JDK whose runtime image contains the Graal and JVMCI classes
78+
* from which libgraal will be built.
79+
*/
80+
private static final Path LIBGRAAL_JAVA_HOME = Path.of(System.getProperty(LIBGRAAL_JAVA_HOME_PROPERTY_NAME, System.getProperty("java.home")));
81+
7682
/**
7783
* Name of the system property specifying a module path for the module(s) containing
7884
* {@code LibGraalFeature} and its dependencies that are not available in the runtime image.
@@ -167,30 +173,17 @@ byte[] readBytes() throws ClassNotFoundException {
167173
/**
168174
* Modules containing classes that can be annotated by {@code LibGraalService}.
169175
*/
170-
private static final Set<String> LIBGRAAL_SERVICES_MODULES = Set.of(
176+
private static final Set<String> LIBGRAAL_MODULES = Set.of(
177+
"jdk.internal.vm.ci",
171178
"jdk.graal.compiler",
172179
"jdk.graal.compiler.libgraal",
173180
"org.graalvm.truffle.compiler",
174181
"com.oracle.graal.graal_enterprise");
175182

176-
/**
177-
* Modules in which Graal and JVMCI classes are defined that this loader will load.
178-
*/
179-
private static final Set<String> LIBGRAAL_MODULES = Stream.concat(
180-
LIBGRAAL_SERVICES_MODULES.stream(), Stream.of("jdk.internal.vm.ci")) //
181-
.collect(Collectors.toUnmodifiableSet());
182-
183183
static {
184184
ClassLoader.registerAsParallelCapable();
185185
}
186186

187-
private final Path libgraalJavaHome = Path.of(System.getProperty(LIBGRAAL_JAVA_HOME_PROPERTY_NAME, System.getProperty("java.home")));
188-
189-
@Override
190-
public Path getJavaHome() {
191-
return libgraalJavaHome;
192-
}
193-
194187
/**
195188
* Converts the module path entry {@code s} to a {@link Path}.
196189
*
@@ -207,7 +200,7 @@ Path parseModulePathEntry(String s) {
207200
@SuppressWarnings("unused")
208201
public HostedLibGraalClassLoader() {
209202
// This loader delegates to the class loader that loaded its own class.
210-
super(LibGraalClassLoader.LOADER_NAME, HostedLibGraalClassLoader.class.getClassLoader());
203+
super("LibGraalClassLoader", HostedLibGraalClassLoader.class.getClassLoader());
211204

212205
try {
213206
/*
@@ -226,7 +219,7 @@ public HostedLibGraalClassLoader() {
226219

227220
Map<String, String> modulesMap = new HashMap<>();
228221

229-
Path imagePath = libgraalJavaHome.resolve(Path.of("lib", "modules"));
222+
Path imagePath = LIBGRAAL_JAVA_HOME.resolve(Path.of("lib", "modules"));
230223
this.imageReader = BasicImageReader.open(imagePath);
231224
for (var entry : imageReader.getEntryNames()) {
232225
int secondSlash = entry.indexOf('/', 1);
@@ -286,15 +279,10 @@ public HostedLibGraalClassLoader() {
286279
* name of its enclosing module.
287280
*/
288281
@Override
289-
public Map<String, String> getModuleMap() {
282+
public Map<String, String> getClassModuleMap() {
290283
return modules;
291284
}
292285

293-
@Override
294-
public Set<String> getServicesModules() {
295-
return LIBGRAAL_SERVICES_MODULES;
296-
}
297-
298286
@Override
299287
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
300288
if (!modules.containsKey(name)) {
@@ -328,6 +316,12 @@ protected Class<?> findClass(final String name) throws ClassNotFoundException {
328316
*/
329317
private static final String SERVICE_PROTOCOL = "service-config";
330318

319+
/**
320+
* Name of the protocol for accessing a file whose contents are the {@code java.home} of the JDK
321+
* whose runtime image contains the Graal and JVMCI * classes from which libgraal will be built.
322+
*/
323+
private static final String LIBGRAAL_JAVA_HOME_PROTOCOL = "libgraal-java-home";
324+
331325
/**
332326
* Name of the protocol for accessing entries in {@link #resources}.
333327
*/
@@ -341,7 +335,14 @@ protected URL findResource(String name) {
341335
if (handler == null) {
342336
this.serviceHandler = handler = new ImageURLStreamHandler();
343337
}
344-
if (name.startsWith("META-INF/services/")) {
338+
if (name.equals("META-INF/libgraal.java.home")) {
339+
try {
340+
var uri = new URI(LIBGRAAL_JAVA_HOME_PROTOCOL, "libgraal.java.home", null);
341+
return URL.of(uri, handler);
342+
} catch (URISyntaxException | MalformedURLException e) {
343+
return null;
344+
}
345+
} else if (name.startsWith("META-INF/services/")) {
345346
String service = name.substring("META-INF/services/".length());
346347
if (services.containsKey(service)) {
347348
try {
@@ -382,7 +383,12 @@ private class ImageURLStreamHandler extends URLStreamHandler {
382383
@Override
383384
public URLConnection openConnection(URL u) {
384385
String protocol = u.getProtocol();
385-
if (protocol.equalsIgnoreCase(SERVICE_PROTOCOL)) {
386+
if (protocol.equalsIgnoreCase(LIBGRAAL_JAVA_HOME_PROTOCOL)) {
387+
if (!u.getPath().equals("libgraal.java.home")) {
388+
throw new IllegalArgumentException(u.toString());
389+
}
390+
return new ImageURLConnection(u, LIBGRAAL_JAVA_HOME.toString().getBytes());
391+
} else if (protocol.equalsIgnoreCase(SERVICE_PROTOCOL)) {
386392
List<String> providers = services.get(u.getPath());
387393
if (providers != null) {
388394
return new ImageURLConnection(u, String.join("\n", providers).getBytes());
@@ -434,9 +440,4 @@ public String getContentType() {
434440
return "application/octet-stream";
435441
}
436442
}
437-
438-
@Override
439-
public ClassLoader getRuntimeClassLoader() {
440-
return LibGraalClassLoader.singleton;
441-
}
442443
}

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/GetJNIConfig.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ final class GetJNIConfig implements AutoCloseable {
7878

7979
int lineNo;
8080

81-
private GetJNIConfig(ClassLoader loader) {
81+
private GetJNIConfig(ClassLoader loader, Path libgraalJavaHome) {
8282
this.loader = loader;
83-
Path javaExe = getJavaExe(Path.of(GraalServices.getSavedProperty("java.home")));
83+
Path javaExe = getJavaExe(libgraalJavaHome);
8484
configFilePath = Path.of("libgraal_jniconfig.txt");
8585

8686
String[] command = {javaExe.toFile().getAbsolutePath(), "-XX:+UnlockExperimentalVMOptions", "-XX:+EnableJVMCI", "-XX:JVMCILibDumpJNIConfig=" + configFilePath};
@@ -196,8 +196,8 @@ private GraalError error(String format, Object... args) {
196196
* @param loader used to resolve type names in the config
197197
*/
198198
@SuppressWarnings("try")
199-
public static void register(ClassLoader loader) {
200-
try (GetJNIConfig source = new GetJNIConfig(loader)) {
199+
public static void register(ClassLoader loader, Path libgraalJavaHome) {
200+
try (GetJNIConfig source = new GetJNIConfig(loader, libgraalJavaHome)) {
201201
Map<String, Class<?>> classes = new HashMap<>();
202202
for (String line : source.lines) {
203203
source.lineNo++;
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -22,20 +22,17 @@
2222
* or visit www.oracle.com if you need additional information or have any
2323
* questions.
2424
*/
25-
package jdk.graal.compiler.libgraal.loader;
25+
package jdk.graal.compiler.libgraal;
2626

2727
import org.graalvm.nativeimage.hosted.Feature.DuringSetupAccess;
2828

2929
/**
3030
* The image runtime class loader that {@linkplain DuringSetupAccess#registerObjectReplacer
31-
* replaces} the build-time instance of {@link HostedLibGraalClassLoader}.
31+
* replaces} the build-time instance of the {@link jdk.graal.nativeimage.LibGraalLoader}.
3232
*/
33-
public final class LibGraalClassLoader extends ClassLoader {
33+
final class LibGraalClassLoader extends ClassLoader {
3434

35-
static final String LOADER_NAME = "LibGraalClassLoader";
36-
static final LibGraalClassLoader singleton = new LibGraalClassLoader();
37-
38-
private LibGraalClassLoader() {
39-
super(LOADER_NAME, null);
35+
LibGraalClassLoader(String name) {
36+
super(name, null);
4037
}
4138
}

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java

+40-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package jdk.graal.compiler.libgraal;
2626

27+
import java.io.IOException;
28+
import java.io.InputStream;
2729
import java.lang.module.ModuleDescriptor;
2830
import java.lang.reflect.Field;
2931
import java.lang.reflect.Modifier;
@@ -114,7 +116,23 @@ public boolean getAsBoolean() {
114116
}
115117
}
116118

119+
/**
120+
* See javadoc for {@code jdk.graal.compiler.libgraal.loader.HostedLibGraalClassLoader}.
121+
*/
122+
private static Path readLibgraalJavaHome(ClassLoader cl) {
123+
try (InputStream in = cl.getResourceAsStream("META-INF/libgraal.java.home")) {
124+
if (in == null) {
125+
throw new GraalError(cl.getClass().getName() + " does not support META-INF/libgraal.java.home protocol (see javadoc of HostedLibGraalClassLoader)");
126+
}
127+
return Path.of(new String(in.readAllBytes()));
128+
} catch (IOException e) {
129+
throw new GraalError(e);
130+
}
131+
}
132+
117133
private final LibGraalLoader libgraalLoader = (LibGraalLoader) getClass().getClassLoader();
134+
private final Path libgraalJavaHome = readLibgraalJavaHome(getClass().getClassLoader());
135+
118136
private BeforeAnalysisAccess beforeAnalysisAccess;
119137
private BeforeCompilationAccess beforeCompilationAccess;
120138
private OptionCollector optionCollector;
@@ -126,7 +144,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
126144
// All qualified exports to libgraal modules need to be further exported to
127145
// ALL-UNNAMED so that access is also possible when the libgraal classes
128146
// are loaded via the libgraal loader into unnamed modules.
129-
Set<String> libgraalModules = Set.copyOf(libgraalLoader.getModuleMap().values());
147+
Set<String> libgraalModules = Set.copyOf(libgraalLoader.getClassModuleMap().values());
130148
for (Module module : ModuleLayer.boot().modules()) {
131149
Set<ModuleDescriptor.Exports> exports = module.getDescriptor().exports();
132150
for (ModuleDescriptor.Exports e : exports) {
@@ -141,26 +159,43 @@ public void afterRegistration(AfterRegistrationAccess access) {
141159
public void duringSetup(DuringSetupAccess access) {
142160
optionCollector = new OptionCollector();
143161

162+
/*
163+
* Replace HostedLibGraalClassLoader with a basic class loader at runtime that cuts out the
164+
* parent class loader (i.e., NativeImageClassLoader) and is guaranteed not to make any
165+
* additional types/methods/fields accidentally reachable.
166+
*/
167+
String loaderName = ((ClassLoader) libgraalLoader).getName();
168+
LibGraalClassLoader runtimeLibgraalLoader = new LibGraalClassLoader(loaderName);
169+
access.registerObjectReplacer(obj -> obj == libgraalLoader ? runtimeLibgraalLoader : obj);
170+
171+
// Register reachability handler used to initialize OptionsParser.libgraalOptions.
144172
access.registerObjectReachabilityHandler(optionCollector::accept, OptionKey.class);
173+
174+
// Register reachability handler that marks the fields that are accessed via unsafe.
145175
access.registerObjectReachabilityHandler(fields -> {
146176
for (int i = 0; i < fields.getOffsets().length; i++) {
147177
Field field = fields.getField(i);
148178
beforeAnalysisAccess.registerAsUnsafeAccessed(field);
149179
}
150180
}, Fields.class);
181+
182+
// Register reachability handler that marks NodeClass subclasses as unsafe allocated
183+
// (see jdk.graal.compiler.graph.NodeClass.allocateInstance).
151184
access.registerObjectReachabilityHandler(nodeClass -> {
152185
Class<?> clazz = nodeClass.getClazz();
153186
if (!Modifier.isAbstract(clazz.getModifiers())) {
154187
/* Support for NodeClass.allocateInstance. */
155188
beforeAnalysisAccess.registerAsUnsafeAllocated(clazz);
156189
}
157190
}, NodeClass.class);
158-
GetJNIConfig.register((ClassLoader) libgraalLoader);
191+
192+
// Register the fields/methods/classes accessed via JNI.
193+
GetJNIConfig.register((ClassLoader) libgraalLoader, libgraalJavaHome);
159194
}
160195

161196
/**
162197
* Collects all instances of the LibGraalLoader loaded {@link OptionKey} class reached by the
163-
* static analysis.
198+
* static analysis and uses them to initialize {@link OptionsParser#libgraalOptions}.
164199
*/
165200
private final class OptionCollector implements Consumer<OptionKey<?>> {
166201
private final Set<OptionKey<?>> options = Collections.newSetFromMap(new ConcurrentHashMap<>());
@@ -178,7 +213,7 @@ public void accept(OptionKey<?> option) {
178213

179214
void afterAnalysis(AfterAnalysisAccess access) {
180215
sealed = true;
181-
Map<String, String> modules = libgraalLoader.getModuleMap();
216+
Map<String, String> modules = libgraalLoader.getClassModuleMap();
182217
for (OptionKey<?> option : options) {
183218
OptionDescriptor descriptor = option.getDescriptor();
184219
if (descriptor.isServiceLoaded()) {
@@ -272,8 +307,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
272307

273308
doLegacyJVMCIInitialization();
274309

275-
Path libGraalJavaHome = libgraalLoader.getJavaHome();
276-
GetCompilerConfig.Result configResult = GetCompilerConfig.from(libGraalJavaHome);
310+
GetCompilerConfig.Result configResult = GetCompilerConfig.from(libgraalJavaHome);
277311
for (var e : configResult.opens().entrySet()) {
278312
Module module = ModuleLayer.boot().findModule(e.getKey()).orElseThrow();
279313
for (String source : e.getValue()) {

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalSupportImpl.java

+2-9
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.Comparator;
3333
import java.util.List;
3434
import java.util.Map;
35-
import java.util.Set;
3635
import java.util.function.Supplier;
3736

3837
import jdk.graal.compiler.core.common.LibGraalSupport;
@@ -152,14 +151,8 @@ private static long getJNIEnv() {
152151

153152
@Platforms(Platform.HOSTED_ONLY.class)
154153
@Override
155-
public Map<String, String> getModuleMap() {
156-
return libGraalLoader.getModuleMap();
157-
}
158-
159-
@Platforms(Platform.HOSTED_ONLY.class)
160-
@Override
161-
public Set<String> getServicesModules() {
162-
return libGraalLoader.getServicesModules();
154+
public Map<String, String> getClassModuleMap() {
155+
return libGraalLoader.getClassModuleMap();
163156
}
164157

165158
@Override

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/LibGraalSupport.java

+3-9
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import java.lang.ref.ReferenceQueue;
3535
import java.util.Map;
3636
import java.util.ServiceLoader;
37-
import java.util.Set;
3837
import java.util.function.Supplier;
3938

4039
/**
@@ -81,19 +80,14 @@ public interface LibGraalSupport {
8180
@Platforms(Platform.HOSTED_ONLY.class)
8281
Supplier<Long> createGlobal(long initialValue);
8382

84-
/**
85-
* Gets the names of the modules containing classes that can be annotated by
86-
* {@code LibGraalService}.
87-
*/
88-
Set<String> getServicesModules();
89-
9083
/**
9184
* Gets a map from the {@linkplain Class#forName(String) name} of a class to the name of its
92-
* enclosing module. There is one entry in the map for each class compiled into libgraal.
85+
* enclosing module. There is one entry in the map for each class loadable via the libgraal
86+
* class loader.
9387
*
9488
* @return an unmodifiable map
9589
*/
96-
Map<String, String> getModuleMap();
90+
Map<String, String> getClassModuleMap();
9791

9892
/**
9993
* Notifies the runtime that the caller is at a point where the live set of objects is expected

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/serviceprovider/GraalServices.java

+1-6
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import java.util.Properties;
3939
import java.util.ServiceConfigurationError;
4040
import java.util.ServiceLoader;
41-
import java.util.Set;
4241

4342
import jdk.graal.compiler.core.ArchitectureSpecific;
4443
import jdk.graal.compiler.core.common.LibGraalSupport;
@@ -115,12 +114,8 @@ private static void addProviders(String arch, Class<?> service) {
115114
LibGraalSupport libgraal = LibGraalSupport.INSTANCE;
116115
if (libgraal != null) {
117116
libgraalServices = new HashMap<>();
118-
Set<String> libgraalServicesModules = libgraal.getServicesModules();
119-
Map<String, String> modules = libgraal.getModuleMap();
120117
String arch = getJVMCIArch();
121-
modules.entrySet().stream()//
122-
.filter(e -> libgraalServicesModules.contains(e.getValue()))//
123-
.map(Map.Entry::getKey)//
118+
libgraal.getClassModuleMap().keySet().stream()//
124119
.map(GraalServices::loadClassOrNull)//
125120
.filter(c -> c != null && c.getAnnotation(LibGraalService.class) != null)//
126121
.forEach(service -> addProviders(arch, service));

0 commit comments

Comments
 (0)