Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,95 @@
package com.oracle.svm.core.hub;

import org.graalvm.collections.EconomicMap;
import org.graalvm.compiler.options.Option;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.option.HostedOptionKey;
import com.oracle.svm.core.util.ExitStatus;
import com.oracle.svm.core.util.ImageHeapMap;
import com.oracle.svm.core.util.VMError;

@AutomaticallyRegisteredImageSingleton
public final class ClassForNameSupport {

public static class Options {
@Option(help = "Enable termination caused by missing metadata.")//
public static final HostedOptionKey<Boolean> ExitOnUnknownClassLoadingFailure = new HostedOptionKey<>(false);
}

static ClassForNameSupport singleton() {
return ImageSingletons.lookup(ClassForNameSupport.class);
}

/** The map used to collect registered classes. */
private final EconomicMap<String, Class<?>> knownClasses = ImageHeapMap.create();
private final EconomicMap<String, Object> knownClasses = ImageHeapMap.create();

@Platforms(Platform.HOSTED_ONLY.class)
public static void registerClass(Class<?> clazz) {
assert !clazz.isPrimitive() : "primitive classes cannot be looked up by name";
if (PredefinedClassesSupport.isPredefined(clazz)) {
return; // must be defined at runtime before it can be looked up
}
singleton().knownClasses.put(clazz.getName(), clazz);
String name = clazz.getName();
VMError.guarantee(!singleton().knownClasses.containsKey(name) || singleton().knownClasses.get(name) == clazz);
singleton().knownClasses.put(name, clazz);
}

@Platforms(Platform.HOSTED_ONLY.class)
public static void registerExceptionForClass(String className, Throwable t) {
singleton().knownClasses.put(className, t);
}

public static Class<?> forNameOrNull(String className, ClassLoader classLoader) {
try {
return forName(className, classLoader, true);
} catch (ClassNotFoundException e) {
throw VMError.shouldNotReachHere("ClassForNameSupport.forNameOrNull should not throw", e);
}
}

public static Class<?> forName(String className, ClassLoader classLoader) throws ClassNotFoundException {
return forName(className, classLoader, false);
}

private static Class<?> forName(String className, ClassLoader classLoader, boolean returnNullOnException) throws ClassNotFoundException {
if (className == null) {
return null;
}
Class<?> result = singleton().knownClasses.get(className);
Object result = singleton().knownClasses.get(className);
if (result == null) {
result = PredefinedClassesSupport.getLoadedForNameOrNull(className, classLoader);
}
// Note: for non-predefined classes, we (currently) don't need to check the provided loader
return result;
// TODO rewrite stack traces (GR-42813)
if (result instanceof Class<?>) {
return (Class<?>) result;
} else if (returnNullOnException && (result instanceof Throwable || result == null)) {
return null;
} else if (result == null) {
if (ClassForNameSupport.Options.ExitOnUnknownClassLoadingFailure.getValue()) {
terminateUnconfigured(className);
}
throw new ClassNotFoundException(className);
} else if (result instanceof Error) {
throw (Error) result;
} else if (result instanceof ClassNotFoundException) {
throw (ClassNotFoundException) result;
} else {
throw VMError.shouldNotReachHere("Class.forName result should be Class, ClassNotFoundException or Error: " + result);
}
}

public static int count() {
return singleton().knownClasses.size();
}

private static void terminateUnconfigured(String className) {
System.out.println("Missing metadata error: Unable to process Class.forName invocation for class name " + className);
new ClassNotFoundException(className).printStackTrace();
System.exit(ExitStatus.MISSING_METADATA.getValue());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@
import com.oracle.svm.core.jdk.Resources;
import com.oracle.svm.core.meta.SharedType;
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder;
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder.ConstructorDescriptor;
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder.FieldDescriptor;
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder.MethodDescriptor;
import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent;
import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool;
import com.oracle.svm.core.util.LazyFinalReference;
Expand Down Expand Up @@ -1148,12 +1151,15 @@ private static Class<?> forName(String name, boolean initialize, ClassLoader loa
if (name == null) {
throw new NullPointerException();
}
Class<?> result = ClassForNameSupport.forNameOrNull(name, loader);
if (result == null && loader != null && PredefinedClassesSupport.hasBytecodeClasses()) {
result = loader.loadClass(name); // may throw
}
if (result == null) {
throw ClassLoadingExceptionSupport.getExceptionForClass(name, new ClassNotFoundException(name));
Class<?> result;
try {
result = ClassForNameSupport.forName(name, loader);
} catch (ClassNotFoundException e) {
if (loader != null && PredefinedClassesSupport.hasBytecodeClasses()) {
result = loader.loadClass(name); // may throw
} else {
throw e;
}
}
if (initialize) {
DynamicHub.fromClass(result).ensureInitialized();
Expand Down Expand Up @@ -1419,23 +1425,23 @@ private Field[] getDeclaredFields0(boolean publicOnly) {
if (reflectionMetadata == null || reflectionMetadata.fieldsEncodingIndex == NO_DATA) {
return new Field[0];
}
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, reflectionMetadata.fieldsEncodingIndex, publicOnly, true);
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, reflectionMetadata.fieldsEncodingIndex, publicOnly);
}

@Substitute
private Method[] getDeclaredMethods0(boolean publicOnly) {
if (reflectionMetadata == null || reflectionMetadata.methodsEncodingIndex == NO_DATA) {
return new Method[0];
}
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, reflectionMetadata.methodsEncodingIndex, publicOnly, true);
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, reflectionMetadata.methodsEncodingIndex, publicOnly);
}

@Substitute
private Constructor<?>[] getDeclaredConstructors0(boolean publicOnly) {
if (reflectionMetadata == null || reflectionMetadata.constructorsEncodingIndex == NO_DATA) {
return new Constructor<?>[0];
}
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, reflectionMetadata.constructorsEncodingIndex, publicOnly, true);
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, reflectionMetadata.constructorsEncodingIndex, publicOnly);
}

@Substitute
Expand Down Expand Up @@ -1670,25 +1676,25 @@ private ReflectionMetadata(int fieldsEncodingIndex, int methodsEncodingIndex, in
}
}

public Field[] getReachableFields() {
public FieldDescriptor[] getReachableFields() {
if (reflectionMetadata == null || reflectionMetadata.fieldsEncodingIndex == NO_DATA) {
return new Field[0];
return new FieldDescriptor[0];
}
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, reflectionMetadata.fieldsEncodingIndex, false, false);
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseReachableFields(this, reflectionMetadata.fieldsEncodingIndex);
}

public Method[] getReachableMethods() {
public MethodDescriptor[] getReachableMethods() {
if (reflectionMetadata == null || reflectionMetadata.methodsEncodingIndex == NO_DATA) {
return new Method[0];
return new MethodDescriptor[0];
}
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, reflectionMetadata.methodsEncodingIndex, false, false);
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseReachableMethods(this, reflectionMetadata.methodsEncodingIndex);
}

public Constructor<?>[] getReachableConstructors() {
public ConstructorDescriptor[] getReachableConstructors() {
if (reflectionMetadata == null || reflectionMetadata.constructorsEncodingIndex == NO_DATA) {
return new Constructor<?>[0];
return new ConstructorDescriptor[0];
}
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, reflectionMetadata.constructorsEncodingIndex, false, false);
return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseReachableConstructors(this, reflectionMetadata.constructorsEncodingIndex);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@
public interface ReflectionMetadataDecoder {
int NO_DATA = -1;

Field[] parseFields(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly);
Field[] parseFields(DynamicHub declaringType, int index, boolean publicOnly);

Method[] parseMethods(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly);
FieldDescriptor[] parseReachableFields(DynamicHub declaringType, int index);

Constructor<?>[] parseConstructors(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly);
Method[] parseMethods(DynamicHub declaringType, int index, boolean publicOnly);

MethodDescriptor[] parseReachableMethods(DynamicHub declaringType, int index);

Constructor<?>[] parseConstructors(DynamicHub declaringType, int index, boolean publicOnly);

ConstructorDescriptor[] parseReachableConstructors(DynamicHub declaringType, int index);

Class<?>[] parseClasses(int index);

Expand All @@ -54,4 +60,81 @@ public interface ReflectionMetadataDecoder {
boolean isHiding(int modifiers);

long getMetadataByteLength();

class ElementDescriptor {
private final Class<?> declaringClass;

public ElementDescriptor(Class<?> declaringClass) {
this.declaringClass = declaringClass;
}

public Class<?> getDeclaringClass() {
return declaringClass;
}

protected static String[] getParameterTypeNames(Class<?>[] parameterTypes) {
String[] parameterTypeNames = new String[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; ++i) {
parameterTypeNames[i] = parameterTypes[i].getTypeName();
}
return parameterTypeNames;
}
}

class FieldDescriptor extends ElementDescriptor {
public final String name;

public FieldDescriptor(Class<?> declaringClass, String name) {
super(declaringClass);
this.name = name;
}

public FieldDescriptor(Field field) {
this(field.getDeclaringClass(), field.getName());
}

public String getName() {
return name;
}
}

class MethodDescriptor extends ElementDescriptor {
public final String name;
public final String[] parameterTypeNames;

public MethodDescriptor(Class<?> declaringClass, String name, String[] parameterTypeNames) {
super(declaringClass);
this.name = name;
this.parameterTypeNames = parameterTypeNames;
}

public MethodDescriptor(Method method) {
this(method.getDeclaringClass(), method.getName(), getParameterTypeNames(method.getParameterTypes()));
}

public String getName() {
return name;
}

public String[] getParameterTypeNames() {
return parameterTypeNames;
}
}

class ConstructorDescriptor extends ElementDescriptor {
public final String[] parameterTypeNames;

public ConstructorDescriptor(Class<?> declaringClass, String[] parameterTypeNames) {
super(declaringClass);
this.parameterTypeNames = parameterTypeNames;
}

public ConstructorDescriptor(Constructor<?> constructor) {
this(constructor.getDeclaringClass(), getParameterTypeNames(constructor.getParameterTypes()));
}

public String[] getParameterTypeNames() {
return parameterTypeNames;
}
}
}
Loading