Skip to content

Commit e3836ac

Browse files
committed
Support boot class path append in BootClassRegistry
1 parent 026d3b2 commit e3836ac

5 files changed

Lines changed: 92 additions & 17 deletions

File tree

‎substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/BootClassRegistry.java‎

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.graalvm.nativeimage.Platforms;
4040

4141
import com.oracle.svm.core.hub.RuntimeClassLoading.ClassDefinitionInfo;
42+
import com.oracle.svm.core.jdk.BootLoaderClassPathSupport;
4243
import com.oracle.svm.espresso.classfile.descriptors.Symbol;
4344
import com.oracle.svm.espresso.classfile.descriptors.Type;
4445
import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols;
@@ -102,33 +103,43 @@ private FileSystem getFileSystem() {
102103
// synchronized until parallel class loading is implemented (GR-62338)
103104
@Override
104105
public synchronized Class<?> doLoadClass(Symbol<Type> type) {
105-
// Only looking into the jimage for now. There could be appended elements.
106-
// see GraalServices.getSavedProperty("jdk.boot.class.path.append")
107106
String pkg = packageFromType(type);
108-
if (pkg == null) {
109-
return null;
110-
}
111107
try {
112-
String moduleName = ClassRegistries.getBootModuleForPackage(pkg);
113-
if (moduleName == null) {
114-
return null;
115-
}
116-
FileSystem fileSystem = getFileSystem();
117-
if (fileSystem == null) {
118-
return null;
108+
byte[] bytes = pkg == null ? null : loadFromJImage(type, pkg);
109+
if (bytes == null) {
110+
/* Preserve boot class path append semantics by looking there after the jimage. */
111+
bytes = loadFromAppendedBootClassPathBytes(type);
119112
}
120-
var jrtTypePath = TypeSymbols.typeToName(type);
121-
Path classPath = fileSystem.getPath("/modules/" + moduleName + "/" + jrtTypePath + ".class");
122-
if (!Files.exists(classPath)) {
113+
if (bytes == null) {
123114
return null;
124115
}
125-
byte[] bytes = Files.readAllBytes(classPath);
126116
return defineClass(type, bytes, 0, bytes.length, ClassDefinitionInfo.EMPTY);
127117
} catch (IOException e) {
128118
throw VMError.shouldNotReachHere(e);
129119
}
130120
}
131121

122+
private byte[] loadFromJImage(Symbol<Type> type, String pkg) throws IOException {
123+
String moduleName = ClassRegistries.getBootModuleForPackage(pkg);
124+
if (moduleName == null) {
125+
return null;
126+
}
127+
FileSystem fileSystem = getFileSystem();
128+
if (fileSystem == null) {
129+
return null;
130+
}
131+
var typeName = TypeSymbols.typeToName(type);
132+
Path classPath = fileSystem.getPath("/modules/" + moduleName + "/" + typeName + ".class");
133+
if (!Files.exists(classPath)) {
134+
return null;
135+
}
136+
return Files.readAllBytes(classPath);
137+
}
138+
139+
private static byte[] loadFromAppendedBootClassPathBytes(Symbol<Type> type) throws IOException {
140+
return BootLoaderClassPathSupport.getResourceBytes(TypeSymbols.typeToName(type) + ".class");
141+
}
142+
132143
private static String packageFromType(Symbol<Type> type) {
133144
int lastSlash = type.lastIndexOf((byte) '/');
134145
if (lastSlash == -1) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2026, 2026, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jdk;
26+
27+
import java.io.IOException;
28+
29+
/// Accesses the runtime boot loader class path exposed via `ClassLoaders.bootLoader().ucp`.
30+
/// This is used for resources and classes supplied with `-Xbootclasspath/a:` in
31+
/// configurations where the boot loader is initialized at run time.
32+
public final class BootLoaderClassPathSupport {
33+
private BootLoaderClassPathSupport() {
34+
}
35+
36+
/// Looks up a resource on the boot loader's runtime class path and returns its bytes.
37+
public static byte[] getResourceBytes(String resourceName) throws IOException {
38+
Target_jdk_internal_loader_BuiltinClassLoader bootLoader = Target_jdk_internal_loader_ClassLoaders.bootLoader();
39+
if (bootLoader == null || bootLoader.ucp == null) {
40+
return null;
41+
}
42+
Target_jdk_internal_loader_Resource resource = bootLoader.ucp.getResource(resourceName);
43+
if (resource == null) {
44+
return null;
45+
}
46+
return resource.getBytes();
47+
}
48+
}

‎substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_net_URLClassLoader.java‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ final class Target_jdk_internal_loader_URLClassPath {
4848
@Alias
4949
public native Enumeration<URL> findResources(String name);
5050

51+
@Alias
52+
public native Target_jdk_internal_loader_Resource getResource(String name);
53+
5154
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClass = ArrayList.class)//
5255
private ArrayList<?> loaders;
5356

@@ -59,6 +62,13 @@ final class Target_jdk_internal_loader_URLClassPath {
5962
private ArrayList<URL> path;
6063
}
6164

65+
@TargetClass(className = "jdk.internal.loader.Resource")
66+
@SuppressWarnings({"unused", "static-method"})
67+
final class Target_jdk_internal_loader_Resource {
68+
@Alias
69+
public native byte[] getBytes() throws java.io.IOException;
70+
}
71+
6272
@TargetClass(URLClassLoader.class)
6373
@SuppressWarnings({"unused", "static-method"})
6474
final class Target_java_net_URLClassLoader {

‎substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_BuiltinClassLoader.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
@SuppressWarnings({"unused", "static-method"})
5151
final class Target_jdk_internal_loader_BuiltinClassLoader {
5252

53-
@Alias private Target_jdk_internal_loader_URLClassPath ucp;
53+
@Alias Target_jdk_internal_loader_URLClassPath ucp;
5454

5555
@Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = NewConcurrentHashMap.class) //
5656
private Map<ModuleReference, ModuleReader> moduleToReader;

‎substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/XOptions.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import com.oracle.svm.core.SubstrateGCOptions;
3232
import com.oracle.svm.core.SubstrateOptions;
33+
import com.oracle.svm.core.jdk.SystemPropertiesSupport;
3334
import com.oracle.svm.shared.option.SubstrateOptionsParser;
3435

3536
import jdk.graal.compiler.options.OptionKey;
@@ -55,6 +56,11 @@ public static boolean parse(String keyAndValue, EconomicMap<OptionKey<?>, Object
5556
long value = parse(xFlag, keyAndValue);
5657
xFlag.optionKey.update(values, value);
5758
return true;
59+
} else if (keyAndValue.startsWith("bootclasspath/a:")) {
60+
// Set the property read by jdk.internal.loader.ClassLoaders.<clinit>
61+
String value = keyAndValue.substring("bootclasspath/a:".length());
62+
SystemPropertiesSupport.singleton().initializeProperty("jdk.boot.class.path.append", value);
63+
return true;
5864
}
5965
return false;
6066
}

0 commit comments

Comments
 (0)