Skip to content

Commit 6528f31

Browse files
committed
[GR-55523] Change ClassLoaderFeature to be layer-aware.
PullRequest: graal/18314
2 parents 473b1d2 + 4a2ebf2 commit 6528f31

14 files changed

+547
-192
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ClassLoaderSupport.java

Lines changed: 0 additions & 112 deletions
This file was deleted.

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import java.security.ProtectionDomain;
3030
import java.util.Collections;
3131
import java.util.Enumeration;
32-
import java.util.HashMap;
3332
import java.util.Set;
3433
import java.util.Vector;
3534
import java.util.WeakHashMap;
@@ -43,7 +42,6 @@
4342
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
4443
import com.oracle.svm.core.annotate.Substitute;
4544
import com.oracle.svm.core.annotate.TargetClass;
46-
import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability;
4745
import com.oracle.svm.core.hub.ClassForNameSupport;
4846
import com.oracle.svm.core.hub.PredefinedClassesSupport;
4947
import com.oracle.svm.core.util.VMError;
@@ -72,13 +70,6 @@ public final class Target_java_lang_ClassLoader {
7270
@Alias @RecomputeFieldValue(kind = Kind.NewInstanceWhenNotNull, declClass = ConcurrentHashMap.class)//
7371
private ConcurrentHashMap<String, Object> parallelLockMap;
7472

75-
/**
76-
* Recompute ClassLoader.packages; See {@link ClassLoaderSupport} for explanation on why this
77-
* information must be reset.
78-
*/
79-
@Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = PackageFieldTransformer.class)//
80-
private ConcurrentHashMap<String, Package> packages;
81-
8273
@Alias //
8374
private static ClassLoader scl;
8475

@@ -330,31 +321,6 @@ private static Class<?> defineClass0(ClassLoader loader, Class<?> lookup, String
330321
final class Target_java_lang_AssertionStatusDirectives {
331322
}
332323

333-
class PackageFieldTransformer implements FieldValueTransformerWithAvailability {
334-
335-
@Override
336-
public ValueAvailability valueAvailability() {
337-
return ValueAvailability.AfterAnalysis;
338-
}
339-
340-
@Override
341-
public Object transform(Object receiver, Object originalValue) {
342-
assert receiver instanceof ClassLoader;
343-
344-
/* JDK9+ stores packages in a ConcurrentHashMap, while 8 and before use a HashMap. */
345-
boolean useConcurrentHashMap = originalValue instanceof ConcurrentHashMap;
346-
347-
/* Retrieving initial package state for this class loader. */
348-
ConcurrentHashMap<String, Package> packages = ClassLoaderSupport.getRegisteredPackages((ClassLoader) receiver);
349-
if (packages == null) {
350-
/* No package state available - have to create clean state. */
351-
return useConcurrentHashMap ? new ConcurrentHashMap<String, Package>() : new HashMap<String, Package>();
352-
} else {
353-
return useConcurrentHashMap ? packages : new HashMap<>(packages);
354-
}
355-
}
356-
}
357-
358324
@TargetClass(className = "java.lang.ClassLoader", innerClass = "ParallelLoaders")
359325
final class Target_java_lang_ClassLoader_ParallelLoaders {
360326

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/BootLoaderSupport.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,20 @@
2626

2727
import java.lang.reflect.Method;
2828

29-
import com.oracle.svm.core.util.VMError;
3029
import com.oracle.svm.util.ReflectionUtil;
3130

31+
import jdk.internal.loader.ClassLoaders;
32+
3233
public class BootLoaderSupport {
3334

34-
/**
35-
* Gets the boot loader or {@code null} if it does not exist.
36-
*/
35+
private static ClassLoader bootLoader;
36+
3737
public static ClassLoader getBootLoader() {
38-
Class<?> classLoadersClass;
39-
try {
40-
classLoadersClass = Class.forName("jdk.internal.loader.ClassLoaders");
41-
Method method = ReflectionUtil.lookupMethod(classLoadersClass, "bootLoader");
42-
Object r = method.invoke(null);
43-
return (ClassLoader) r;
44-
} catch (ReflectiveOperationException e) {
45-
throw VMError.shouldNotReachHere("Cannot get access to the boot loader", e);
38+
if (bootLoader != null) {
39+
return bootLoader;
4640
}
41+
Method method = ReflectionUtil.lookupMethod(ClassLoaders.class, "bootLoader");
42+
bootLoader = ReflectionUtil.invokeMethod(method, null);
43+
return bootLoader;
4744
}
4845
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderFeature.java

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,51 @@
2424
*/
2525
package com.oracle.svm.hosted;
2626

27-
import com.oracle.svm.core.feature.InternalFeature;
27+
import java.util.concurrent.ConcurrentHashMap;
28+
29+
import com.oracle.graal.pointsto.heap.ImageHeapConstant;
2830
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
31+
import com.oracle.svm.core.feature.InternalFeature;
32+
import com.oracle.svm.core.fieldvaluetransformer.FieldValueTransformerWithAvailability;
33+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
34+
import com.oracle.svm.core.util.VMError;
35+
import com.oracle.svm.hosted.imagelayer.CrossLayerConstantRegistry;
36+
import com.oracle.svm.hosted.jdk.HostedClassLoaderPackageManagement;
37+
import com.oracle.svm.util.ReflectionUtil;
38+
39+
import jdk.internal.loader.ClassLoaders;
2940

3041
@AutomaticallyRegisteredFeature
3142
public class ClassLoaderFeature implements InternalFeature {
3243

44+
private static final String APP_KEY_NAME = "ClassLoader#App";
45+
private static final String PLATFORM_KEY_NAME = "ClassLoader#Platform";
46+
private static final String BOOT_KEY_NAME = "ClassLoader#Boot";
47+
3348
private static final NativeImageSystemClassLoader nativeImageSystemClassLoader = NativeImageSystemClassLoader.singleton();
3449

50+
private static final ClassLoader bootClassLoader;
51+
private static final ClassLoader platformClassLoader;
52+
53+
static {
54+
if (ImageLayerBuildingSupport.buildingImageLayer()) {
55+
platformClassLoader = ClassLoaders.platformClassLoader();
56+
bootClassLoader = BootLoaderSupport.getBootLoader();
57+
} else {
58+
platformClassLoader = null;
59+
bootClassLoader = null;
60+
}
61+
}
62+
3563
public static ClassLoader getRuntimeClassLoader(ClassLoader original) {
36-
if (needsReplacement(original)) {
64+
if (replaceWithAppClassLoader(original)) {
3765
return nativeImageSystemClassLoader.defaultSystemClassLoader;
3866
}
67+
3968
return original;
4069
}
4170

42-
private static boolean needsReplacement(ClassLoader loader) {
71+
private static boolean replaceWithAppClassLoader(ClassLoader loader) {
4372
if (loader == nativeImageSystemClassLoader) {
4473
return true;
4574
}
@@ -50,14 +79,79 @@ private static boolean needsReplacement(ClassLoader loader) {
5079
}
5180

5281
private Object runtimeClassLoaderObjectReplacer(Object replaceCandidate) {
53-
if (replaceCandidate instanceof ClassLoader) {
54-
return getRuntimeClassLoader((ClassLoader) replaceCandidate);
82+
if (replaceCandidate instanceof ClassLoader loader) {
83+
return getRuntimeClassLoader(loader);
5584
}
5685
return replaceCandidate;
5786
}
5887

88+
ImageHeapConstant replaceClassLoadersWithLayerConstant(CrossLayerConstantRegistry registry, Object object) {
89+
if (object instanceof ClassLoader loader) {
90+
if (replaceWithAppClassLoader(loader) || loader == nativeImageSystemClassLoader.defaultSystemClassLoader) {
91+
return registry.getConstant(APP_KEY_NAME);
92+
} else if (loader == platformClassLoader) {
93+
return registry.getConstant(PLATFORM_KEY_NAME);
94+
} else if (loader == bootClassLoader) {
95+
return registry.getConstant(BOOT_KEY_NAME);
96+
} else {
97+
throw VMError.shouldNotReachHere("Currently unhandled class loader seen in extension layer: %s", loader);
98+
}
99+
}
100+
101+
return null;
102+
}
103+
59104
@Override
60105
public void duringSetup(DuringSetupAccess access) {
61-
access.registerObjectReplacer(this::runtimeClassLoaderObjectReplacer);
106+
var packageManager = HostedClassLoaderPackageManagement.singleton();
107+
var registry = CrossLayerConstantRegistry.singletonOrNull();
108+
if (ImageLayerBuildingSupport.buildingImageLayer()) {
109+
packageManager.initialize(nativeImageSystemClassLoader.defaultSystemClassLoader, registry);
110+
}
111+
112+
if (ImageLayerBuildingSupport.firstImageBuild()) {
113+
access.registerObjectReplacer(this::runtimeClassLoaderObjectReplacer);
114+
} else {
115+
var config = (FeatureImpl.DuringSetupAccessImpl) access;
116+
config.registerObjectToConstantReplacer(obj -> replaceClassLoadersWithLayerConstant(registry, obj));
117+
// relink packages defined in the prior layers
118+
config.registerObjectToConstantReplacer(packageManager::replaceWithPriorLayerPackage);
119+
}
120+
}
121+
122+
@Override
123+
public void beforeAnalysis(BeforeAnalysisAccess access) {
124+
var packagesField = ReflectionUtil.lookupField(ClassLoader.class, "packages");
125+
access.registerFieldValueTransformer(packagesField, new FieldValueTransformerWithAvailability() {
126+
127+
@Override
128+
public ValueAvailability valueAvailability() {
129+
return ValueAvailability.AfterAnalysis;
130+
}
131+
132+
@Override
133+
public Object transform(Object receiver, Object originalValue) {
134+
assert receiver instanceof ClassLoader : receiver;
135+
assert originalValue instanceof ConcurrentHashMap : "Underlying representation has changed: " + originalValue;
136+
137+
/* Retrieving initial package state for this class loader. */
138+
ConcurrentHashMap<String, Package> packages = HostedClassLoaderPackageManagement.singleton().getRegisteredPackages((ClassLoader) receiver);
139+
/* If no package state is available then we must create a clean state. */
140+
return packages == null ? new ConcurrentHashMap<>() : packages;
141+
}
142+
});
143+
144+
if (ImageLayerBuildingSupport.buildingInitialLayer()) {
145+
/*
146+
* Note we cannot register these heap constants until the field value transformer has
147+
* been registered. Otherwise there is a race between this feature and
148+
* ObservableImageHeapMapProviderImpl#beforeAnalysis, as during heap scanning all
149+
* fieldValueInterceptors will be computed for the scanned objects.
150+
*/
151+
var registry = CrossLayerConstantRegistry.singletonOrNull();
152+
registry.registerHeapConstant(APP_KEY_NAME, nativeImageSystemClassLoader.defaultSystemClassLoader);
153+
registry.registerHeapConstant(PLATFORM_KEY_NAME, platformClassLoader);
154+
registry.registerHeapConstant(BOOT_KEY_NAME, bootClassLoader);
155+
}
62156
}
63157
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ public NativeImageSystemClassLoader(ClassLoader defaultSystemClassLoader) {
6565
systemIOWrappers.replaceSystemOutErr();
6666
}
6767

68+
/*
69+
* Note this is not an image singleton; this ClassLoader is installed while starting
70+
* NativeImageGeneratorRunner.
71+
*/
6872
public static NativeImageSystemClassLoader singleton() {
6973
ClassLoader loader = ClassLoader.getSystemClassLoader();
7074
if (loader instanceof NativeImageSystemClassLoader) {
@@ -87,14 +91,17 @@ public void setNativeImageClassLoader(ClassLoader nativeImageClassLoader) {
8791
this.nativeImageClassLoader = nativeImageClassLoader;
8892
}
8993

90-
private boolean isNativeImageClassLoader(ClassLoader current, ClassLoader c) {
91-
ClassLoader loader = current;
94+
/**
95+
* Checks class loaders match. {@code start} is searched up to the system class loader.
96+
*/
97+
private boolean matchesClassLoaderOrParent(ClassLoader start, ClassLoader candidate) {
98+
ClassLoader current = start;
9299
do {
93-
if (loader == c) {
100+
if (current == candidate) {
94101
return true;
95102
}
96-
loader = loader.getParent();
97-
} while (loader != defaultSystemClassLoader);
103+
current = current.getParent();
104+
} while (current != defaultSystemClassLoader);
98105
return false;
99106
}
100107

@@ -103,12 +110,12 @@ public boolean isNativeImageClassLoader(ClassLoader c) {
103110
if (loader == null) {
104111
return false;
105112
}
106-
return isNativeImageClassLoader(nativeImageClassLoader, c);
113+
return matchesClassLoaderOrParent(nativeImageClassLoader, c);
107114
}
108115

109116
public boolean isDisallowedClassLoader(ClassLoader c) {
110117
for (ClassLoader disallowedClassLoader : disallowedClassLoaders) {
111-
if (isNativeImageClassLoader(disallowedClassLoader, c)) {
118+
if (matchesClassLoaderOrParent(disallowedClassLoader, c)) {
112119
return true;
113120
}
114121
}

0 commit comments

Comments
 (0)