Skip to content

Commit f4d05a0

Browse files
committed
[GR-31953] Support predefinition of classes with subtype and outer class relationships.
PullRequest: graal/9189
2 parents 1b41b6e + c67aecc commit f4d05a0

File tree

8 files changed

+225
-41
lines changed

8 files changed

+225
-41
lines changed

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeClassInitialization.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public final class RuntimeClassInitialization {
9696
public static void initializeAtRunTime(Class<?>... classes) {
9797
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
9898
for (Class<?> aClass : classes) {
99-
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtRunTime(aClass, classReason(stacktrace, aClass.getSimpleName() + ".class'"));
99+
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtRunTime(aClass, classReason(stacktrace, aClass.getSimpleName() + ".class"));
100100
}
101101
}
102102

@@ -115,7 +115,7 @@ public static void initializeAtRunTime(Class<?>... classes) {
115115
public static void initializeAtBuildTime(Class<?>... classes) {
116116
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
117117
for (Class<?> aClass : classes) {
118-
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtBuildTime(aClass, classReason(stacktrace, aClass.getSimpleName() + ".class'"));
118+
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtBuildTime(aClass, classReason(stacktrace, aClass.getSimpleName() + ".class"));
119119
}
120120
}
121121

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ public static Class<?> forName(String className, boolean initialize, ClassLoader
7272
}
7373
return result;
7474
}
75+
76+
/** Whether a call to {@link Class#forName} for the given class can be folded to a constant. */
77+
public static boolean canBeFolded(Class<?> clazz) {
78+
return !PredefinedClassesSupport.isPredefined(clazz);
79+
}
7580
}
7681

7782
@AutomaticFeature

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,10 @@ private ClassLoader getClassLoader0() {
747747
return (ClassLoader) classLoader;
748748
}
749749

750+
boolean hasClassLoader() {
751+
return classLoader != FROM_COMPANION || (companion.isPresent() && companion.get().hasClassLoader());
752+
}
753+
750754
void setClassLoaderAtRuntime(ClassLoader loader) {
751755
companion.get().setClassLoader(loader);
752756
}
@@ -839,6 +843,7 @@ public boolean isLocalOrAnonymousClass() {
839843

840844
@Substitute
841845
private Object getEnclosingClass() {
846+
PredefinedClassesSupport.throwIfUnresolvable(toClass(enclosingClass), getClassLoader0());
842847
return enclosingClass;
843848
}
844849

@@ -862,7 +867,7 @@ private Object getDeclaringClassInternal() {
862867
if (isLocalOrAnonymousClass()) {
863868
return null;
864869
} else {
865-
return enclosingClass;
870+
return getEnclosingClass();
866871
}
867872
}
868873

@@ -1122,11 +1127,17 @@ private Method getMethod(@SuppressWarnings("hiding") String name, Class<?>... pa
11221127

11231128
@Substitute
11241129
private Class<?>[] getDeclaredClasses() {
1130+
for (Class<?> clazz : rd.declaredClasses) {
1131+
PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0());
1132+
}
11251133
return rd.declaredClasses;
11261134
}
11271135

11281136
@Substitute
11291137
private Class<?>[] getClasses() {
1138+
for (Class<?> clazz : rd.publicClasses) {
1139+
PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0());
1140+
}
11301141
return rd.publicClasses;
11311142
}
11321143

@@ -1271,6 +1282,7 @@ public AnnotatedType[] getAnnotatedInterfaces() {
12711282
@Substitute
12721283
private Method getEnclosingMethod() {
12731284
if (rd.enclosingMethodOrConstructor instanceof Method) {
1285+
PredefinedClassesSupport.throwIfUnresolvable(rd.enclosingMethodOrConstructor.getDeclaringClass(), getClassLoader0());
12741286
return (Method) rd.enclosingMethodOrConstructor;
12751287
}
12761288
return null;
@@ -1279,6 +1291,7 @@ private Method getEnclosingMethod() {
12791291
@Substitute
12801292
private Constructor<?> getEnclosingConstructor() {
12811293
if (rd.enclosingMethodOrConstructor instanceof Constructor) {
1294+
PredefinedClassesSupport.throwIfUnresolvable(rd.enclosingMethodOrConstructor.getDeclaringClass(), getClassLoader0());
12821295
return (Constructor<?>) rd.enclosingMethodOrConstructor;
12831296
}
12841297
return null;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public String getPackageName() {
4747
return packageName;
4848
}
4949

50+
boolean hasClassLoader() {
51+
return classLoader != null;
52+
}
53+
5054
public ClassLoader getClassLoader() {
5155
ClassLoader loader = classLoader;
5256
VMError.guarantee(loader != null);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,18 @@ public static String hash(byte[] classData, int offset, int length) {
6666
private final ReentrantLock lock = new ReentrantLock();
6767

6868
/** Predefined classes by hash. */
69-
private final EconomicMap<String, Class<?>> predefinedClassesByName = ImageHeapMap.create();
69+
private final EconomicMap<String, Class<?>> predefinedClassesByHash = ImageHeapMap.create();
7070

7171
/** Predefined classes which have already been loaded, by name. */
7272
private final EconomicMap<String, Class<?>> loadedClassesByName = EconomicMap.create();
7373

7474
@Platforms(Platform.HOSTED_ONLY.class)
7575
public static void registerClass(String hash, Class<?> clazz) {
76-
Class<?> existing = singleton().predefinedClassesByName.putIfAbsent(hash, clazz);
77-
VMError.guarantee(existing == null, "Can define only one class per hash");
78-
singleton().predefinedClasses.add(clazz);
76+
Class<?> existing = singleton().predefinedClassesByHash.putIfAbsent(hash, clazz);
77+
if (existing != clazz) {
78+
VMError.guarantee(existing == null, "Can define only one class per hash");
79+
singleton().predefinedClasses.add(clazz);
80+
}
7981
}
8082

8183
@Platforms(Platform.HOSTED_ONLY.class)
@@ -85,7 +87,7 @@ public static boolean isPredefined(Class<?> clazz) {
8587

8688
public static Class<?> loadClass(ClassLoader classLoader, String expectedName, byte[] data, int offset, int length, ProtectionDomain protectionDomain) {
8789
String hash = hash(data, offset, length);
88-
Class<?> clazz = singleton().predefinedClassesByName.get(hash);
90+
Class<?> clazz = singleton().predefinedClassesByHash.get(hash);
8991
if (clazz == null) {
9092
String name = (expectedName != null) ? expectedName : "(name not specified)";
9193
throw VMError.unsupportedFeature("Defining a class from new bytecodes at run time is not supported. Class " + name +
@@ -97,7 +99,7 @@ public static Class<?> loadClass(ClassLoader classLoader, String expectedName, b
9799
private Class<?> load(ClassLoader classLoader, ProtectionDomain protectionDomain, Class<?> clazz) {
98100
lock.lock();
99101
try {
100-
boolean alreadyLoaded = singleton().loadedClassesByName.putIfAbsent(clazz.getName(), clazz) != null;
102+
boolean alreadyLoaded = (loadedClassesByName.get(clazz.getName()) == clazz);
101103
if (alreadyLoaded) {
102104
if (classLoader == clazz.getClassLoader()) {
103105
throw new LinkageError("loader " + classLoader + " attempted duplicate class definition for " + clazz.getName() + " defined by " + clazz.getClassLoader());
@@ -107,6 +109,12 @@ private Class<?> load(ClassLoader classLoader, ProtectionDomain protectionDomain
107109
"been loaded by class loader: " + clazz.getClassLoader());
108110
}
109111
}
112+
113+
throwIfUnresolvable(clazz.getSuperclass(), classLoader);
114+
for (Class<?> intf : clazz.getInterfaces()) {
115+
throwIfUnresolvable(intf, classLoader);
116+
}
117+
110118
/*
111119
* The following is part of the locked block so that other threads can observe only the
112120
* initialized values once the class can be found.
@@ -116,12 +124,33 @@ private Class<?> load(ClassLoader classLoader, ProtectionDomain protectionDomain
116124
if (protectionDomain != null) {
117125
hub.setProtectionDomainAtRuntime(protectionDomain);
118126
}
127+
loadedClassesByName.put(clazz.getName(), clazz);
119128
return clazz;
120129
} finally {
121130
lock.unlock();
122131
}
123132
}
124133

134+
public static void throwIfUnresolvable(Class<?> clazz, ClassLoader classLoader) {
135+
if (clazz == null) {
136+
return;
137+
}
138+
DynamicHub hub = DynamicHub.fromClass(clazz);
139+
if (!hub.hasClassLoader()) {
140+
throwResolutionError(clazz.getName());
141+
}
142+
if (!isSameOrParent(clazz.getClassLoader(), classLoader)) { // common case: same loader
143+
throwResolutionError(clazz.getName());
144+
}
145+
}
146+
147+
private static void throwResolutionError(String name) {
148+
// NoClassDefFoundError with ClassNotFoundException required by Java VM specification, 5.3
149+
NoClassDefFoundError error = new NoClassDefFoundError(name.replace('.', '/'));
150+
error.initCause(new ClassNotFoundException(name));
151+
throw error;
152+
}
153+
125154
static Class<?> getLoadedForNameOrNull(String name, ClassLoader classLoader) {
126155
Class<?> clazz = singleton().getLoaded(name);
127156
if (clazz == null || !isSameOrParent(clazz.getClassLoader(), classLoader)) {
@@ -145,7 +174,7 @@ private static boolean isSameOrParent(ClassLoader parent, ClassLoader child) {
145174
}
146175
ClassLoader c = child;
147176
do {
148-
if (c == parent) {
177+
if (c == parent) { // common case
149178
return true;
150179
}
151180
c = c.getParent();
@@ -155,7 +184,7 @@ private static boolean isSameOrParent(ClassLoader parent, ClassLoader child) {
155184

156185
public static class TestingBackdoor {
157186
public static UnmodifiableEconomicMap<String, Class<?>> getPredefinedClasses() {
158-
return singleton().predefinedClassesByName;
187+
return singleton().predefinedClassesByHash;
159188
}
160189
}
161190
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/LazyFinalReference.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public LazyFinalReference(Supplier<T> supplier) {
6363
this.supplier = supplier;
6464
}
6565

66+
public boolean isPresent() {
67+
return value != UNINITIALIZED || UNSAFE.getObjectVolatile(this, VALUE_OFFSET) != UNINITIALIZED;
68+
}
69+
6670
@SuppressWarnings("unchecked")
6771
public T get() {
6872
T v = value;

0 commit comments

Comments
 (0)