diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java index cece21f8368b..c4f8594cc920 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java @@ -26,20 +26,22 @@ package com.oracle.graal.pointsto.standalone; -import com.oracle.graal.pointsto.api.HostVM; -import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.HostedProviders; -import com.oracle.graal.pointsto.standalone.plugins.StandaloneGraphBuilderPhase; -import com.oracle.graal.pointsto.util.AnalysisError; -import jdk.vm.ci.meta.ResolvedJavaType; +import java.util.Comparator; +import java.util.concurrent.ConcurrentHashMap; + import org.graalvm.compiler.java.GraphBuilderPhase; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.OptimisticOptimizations; -import java.util.Comparator; -import java.util.concurrent.ConcurrentHashMap; +import com.oracle.graal.pointsto.api.HostVM; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.graal.pointsto.standalone.plugins.StandaloneGraphBuilderPhase; +import com.oracle.graal.pointsto.util.AnalysisError; + +import jdk.vm.ci.meta.ResolvedJavaType; public class StandaloneHost extends HostVM { private final ConcurrentHashMap> typeToClass = new ConcurrentHashMap<>(); @@ -80,10 +82,6 @@ public void onTypeReachable(AnalysisType type) { */ } - @Override - public void onTypeInstantiated(AnalysisType newValue) { - } - @Override public GraphBuilderPhase.Instance createGraphBuilderPhase(HostedProviders builderProviders, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java index 471b8bd317d2..899363fa399f 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java @@ -154,13 +154,6 @@ public void checkType(ResolvedJavaType type, AnalysisUniverse universe) { */ public abstract void onTypeReachable(AnalysisType newValue); - /** - * Run initialization tasks for a newly instantiated {@link AnalysisType}. - * - * @param newValue the type to initialize - */ - public abstract void onTypeInstantiated(AnalysisType newValue); - /** * Check if an {@link AnalysisType} is initialized. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index f436e891dc7b..bceed7310194 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -692,7 +692,6 @@ public void onFieldAccessed(AnalysisField field) { } public void onTypeInstantiated(AnalysisType type, UsageKind usage) { - hostVM.onTypeInstantiated(type); bb.onTypeInstantiated(type, usage); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java index a952e1a348c2..c39b13dc41ac 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java @@ -84,7 +84,11 @@ private void parseClass(EconomicMap data) { } ConfigurationCondition condition = conditionResult.get(); - TypeResult result = delegate.resolveType(condition, className, false); + /* + * Even if primitives cannot be queried through Class.forName, they can be registered to + * allow getDeclaredMethods() and similar bulk queries at run time. + */ + TypeResult result = delegate.resolveType(condition, className, true); if (!result.isPresent()) { handleError("Could not resolve class " + className + " for reflection configuration.", result.getException()); return; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java index 76bdeb694542..f08b4d7b8e8d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java @@ -55,8 +55,14 @@ public static void registerClass(Class clazz) { return; // must be defined at runtime before it can be looked up } String name = clazz.getName(); - VMError.guarantee(!singleton().knownClasses.containsKey(name) || singleton().knownClasses.get(name) == clazz); - singleton().knownClasses.put(name, clazz); + if (!singleton().knownClasses.containsKey(name) || !(singleton().knownClasses.get(name) instanceof Throwable)) { + /* + * If the class has already been seen as throwing an error, we don't overwrite this + * error + */ + VMError.guarantee(!singleton().knownClasses.containsKey(name) || singleton().knownClasses.get(name) == clazz); + singleton().knownClasses.put(name, clazz); + } } @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index e4c4e2d9a431..bd4ddb0dcfe1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -992,41 +992,28 @@ public Field getField(String fieldName) throws NoSuchFieldException, SecurityExc private void checkField(String fieldName, Field field, boolean publicOnly) throws NoSuchFieldException { boolean throwMissingErrors = throwMissingRegistrationErrors(); - boolean noSuchField = false; - boolean missingRegistration = false; + Class clazz = DynamicHub.toClass(this); if (field == null) { - if (throwMissingErrors) { - if (isClassFlagSet(ALL_DECLARED_FIELDS_FLAG) || (publicOnly && isClassFlagSet(ALL_FIELDS_FLAG))) { - /* - * If getDeclaredFields (or getFields for a public field) is registered, we know - * for sure that the field does indeed not exist if we don't find it. - */ - noSuchField = true; - } else { - missingRegistration = true; - } + if (throwMissingErrors && !allElementsRegistered(publicOnly, ALL_DECLARED_FIELDS_FLAG, ALL_FIELDS_FLAG)) { + throw MissingReflectionRegistrationUtils.forField(clazz, fieldName); } else { - noSuchField = true; + /* + * If getDeclaredFields (or getFields for a public field) is registered, we know for + * sure that the field does indeed not exist if we don't find it. + */ + throw new NoSuchFieldException(fieldName); } } else { ReflectionMetadataDecoder decoder = ImageSingletons.lookup(ReflectionMetadataDecoder.class); int fieldModifiers = field.getModifiers(); - if (decoder.isNegative(fieldModifiers)) { - noSuchField = true; - } else if (decoder.isHiding(fieldModifiers)) { - if (throwMissingErrors) { - missingRegistration = true; - } else { - noSuchField = true; - } + boolean negative = decoder.isNegative(fieldModifiers); + boolean hiding = decoder.isHiding(fieldModifiers); + if (throwMissingErrors && hiding) { + throw MissingReflectionRegistrationUtils.forField(clazz, fieldName); + } else if (negative || hiding) { + throw new NoSuchFieldException(fieldName); } } - VMError.guarantee(!missingRegistration || !noSuchField, "Either a MissingRegistrationError or a NoSuchFieldException should be thrown, not both"); - if (missingRegistration) { - throw MissingReflectionRegistrationUtils.forField(DynamicHub.toClass(this), fieldName); - } else if (noSuchField) { - throw new NoSuchFieldException(fieldName); - } } @Substitute @@ -1039,41 +1026,32 @@ private Method getMethod(String methodName, Class... parameterTypes) throws N private void checkMethod(String methodName, Class[] parameterTypes, Executable method, boolean publicOnly) throws NoSuchMethodException { boolean throwMissingErrors = throwMissingRegistrationErrors(); - boolean noSuchMethod = false; - boolean missingRegistration = false; + Class clazz = DynamicHub.toClass(this); if (method == null) { - if (throwMissingErrors) { - if (isClassFlagSet(ALL_DECLARED_METHODS_FLAG) || (publicOnly && isClassFlagSet(ALL_METHODS_FLAG))) { - /* - * If getDeclaredMethods (or getMethods for a public method) is registered, we - * know for sure that the method does indeed not exist if we don't find it. - */ - noSuchMethod = true; - } else { - missingRegistration = true; - } + if (throwMissingErrors && !allElementsRegistered(publicOnly, ALL_DECLARED_METHODS_FLAG, ALL_METHODS_FLAG)) { + throw MissingReflectionRegistrationUtils.forMethod(clazz, methodName, parameterTypes); } else { - noSuchMethod = true; + /* + * If getDeclaredMethods (or getMethods for a public method) is registered, we know + * for sure that the method does indeed not exist if we don't find it. + */ + throw new NoSuchMethodException(methodToString(methodName, parameterTypes)); } } else { ReflectionMetadataDecoder decoder = ImageSingletons.lookup(ReflectionMetadataDecoder.class); int methodModifiers = method.getModifiers(); - if (decoder.isNegative(methodModifiers)) { - noSuchMethod = true; - } else if (decoder.isHiding(methodModifiers)) { - if (throwMissingErrors) { - missingRegistration = true; - } else { - noSuchMethod = true; - } + boolean negative = decoder.isNegative(methodModifiers); + boolean hiding = decoder.isHiding(methodModifiers); + if (throwMissingErrors && hiding) { + throw MissingReflectionRegistrationUtils.forMethod(clazz, methodName, parameterTypes); + } else if (negative || hiding) { + throw new NoSuchMethodException(methodToString(methodName, parameterTypes)); } } - VMError.guarantee(!missingRegistration || !noSuchMethod, "Either a MissingRegistrationError or a NoSuchMethodException should be thrown, not both"); - if (missingRegistration) { - throw MissingReflectionRegistrationUtils.forMethod(DynamicHub.toClass(this), methodName, parameterTypes); - } else if (noSuchMethod) { - throw new NoSuchMethodException(methodToString(methodName, parameterTypes)); - } + } + + private boolean allElementsRegistered(boolean publicOnly, int allDeclaredElementsFlag, int allPublicElementsFlag) { + return isClassFlagSet(allDeclaredElementsFlag) || (publicOnly && isClassFlagSet(allPublicElementsFlag)); } @KeepOriginal diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index a22a6b90d0fe..d93b5dccfada 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -70,8 +70,6 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.RelocatedPointer; -import org.graalvm.nativeimage.impl.ConfigurationCondition; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.PointsToAnalysis; @@ -150,7 +148,6 @@ public class SVMHost extends HostVM { private final LinkAtBuildTimeSupport linkAtBuildTimeSupport; private final HostedStringDeduplication stringTable; private final UnsafeAutomaticSubstitutionProcessor automaticSubstitutions; - private final RuntimeReflectionSupport reflectionSupport; /** * Optionally keep the Graal graphs alive during analysis. This increases the memory footprint @@ -188,7 +185,6 @@ public SVMHost(OptionValues options, ClassLoader classLoader, ClassInitializatio multiMethodAnalysisPolicy = DEFAULT_MULTIMETHOD_ANALYSIS_POLICY; } parsingSupport = ImageSingletons.contains(SVMParsingSupport.class) ? ImageSingletons.lookup(SVMParsingSupport.class) : null; - this.reflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class); } private static Map> setupForbiddenTypes(OptionValues options) { @@ -316,14 +312,6 @@ public void onTypeReachable(AnalysisType analysisType) { automaticSubstitutions.computeSubstitutions(this, GraalAccess.getOriginalProviders().getMetaAccess().lookupJavaType(analysisType.getJavaClass())); } - @Override - public void onTypeInstantiated(AnalysisType newValue) { - if (newValue.isAnnotation()) { - /* getDeclaredMethods is called in the AnnotationType constructor */ - reflectionSupport.registerAllDeclaredMethodsQuery(ConfigurationCondition.alwaysTrue(), true, newValue.getJavaClass()); - } - } - @Override public boolean isInitialized(AnalysisType type) { boolean shouldInitializeAtRuntime = classInitializationSupport.shouldInitializeAtRuntime(type); @@ -647,7 +635,7 @@ public void methodBeforeTypeFlowCreationHook(BigBang bb, AnalysisMethod method, if (n instanceof StackValueNode) { containsStackValueNode.put(method, true); } else if (n instanceof ReachabilityRegistrationNode node) { - bb.postTask(debug -> node.getRegistrationTask().ensureDone()); + bb.postTask(debug -> node.getRegistrationTask().ensureDone()); } checkClassInitializerSideEffect(method, n); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java index 12b3718b3aa5..aebdf77bede2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java @@ -175,14 +175,22 @@ public void register(ConfigurationCondition condition, boolean unsafeInstantiate public void registerAllClassesQuery(ConfigurationCondition condition, Class clazz) { checkNotSealed(); registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_CLASSES_FLAG)); - register(condition, clazz.getClasses()); + try { + register(condition, clazz.getClasses()); + } catch (LinkageError e) { + /* Ignore the error */ + } } @Override public void registerAllDeclaredClassesQuery(ConfigurationCondition condition, Class clazz) { checkNotSealed(); registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_CLASSES_FLAG)); - register(condition, clazz.getDeclaredClasses()); + try { + register(condition, clazz.getDeclaredClasses()); + } catch (LinkageError e) { + /* Ignore the error */ + } } private void registerClass(Class clazz, boolean unsafeInstantiated) { @@ -217,7 +225,7 @@ public void registerClassLookupException(ConfigurationCondition condition, Strin public void registerClassLookup(ConfigurationCondition condition, String typeName) { checkNotSealed(); try { - register(condition, Class.forName(typeName)); + register(condition, Class.forName(typeName, false, null)); } catch (ClassNotFoundException e) { registerConditionalConfiguration(condition, () -> ClassForNameSupport.registerNegativeQuery(typeName)); } catch (Throwable t) { @@ -276,14 +284,22 @@ public void registerAllMethodsQuery(ConfigurationCondition condition, boolean qu final Class currentLambda = current; registerConditionalConfiguration(condition, () -> setQueryFlag(currentLambda, ALL_METHODS_FLAG)); } - register(condition, queriedOnly, clazz.getMethods()); + try { + register(condition, queriedOnly, clazz.getMethods()); + } catch (LinkageError e) { + /* Ignore the error */ + } } @Override public void registerAllDeclaredMethodsQuery(ConfigurationCondition condition, boolean queriedOnly, Class clazz) { checkNotSealed(); registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_METHODS_FLAG)); - register(condition, queriedOnly, clazz.getDeclaredMethods()); + try { + register(condition, queriedOnly, clazz.getDeclaredMethods()); + } catch (LinkageError e) { + /* Ignore the error */ + } } @Override @@ -293,14 +309,22 @@ public void registerAllConstructorsQuery(ConfigurationCondition condition, boole final Class currentLambda = current; registerConditionalConfiguration(condition, () -> setQueryFlag(currentLambda, ALL_CONSTRUCTORS_FLAG)); } - register(condition, queriedOnly, clazz.getConstructors()); + try { + register(condition, queriedOnly, clazz.getConstructors()); + } catch (LinkageError e) { + /* Ignore the error */ + } } @Override public void registerAllDeclaredConstructorsQuery(ConfigurationCondition condition, boolean queriedOnly, Class clazz) { checkNotSealed(); registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_CONSTRUCTORS_FLAG)); - register(condition, queriedOnly, clazz.getDeclaredConstructors()); + try { + register(condition, queriedOnly, clazz.getDeclaredConstructors()); + } catch (LinkageError e) { + /* Ignore the error */ + } } private void registerMethod(boolean queriedOnly, Executable reflectExecutable) { @@ -392,14 +416,22 @@ public void registerAllFieldsQuery(ConfigurationCondition condition, Class cl final Class currentLambda = current; registerConditionalConfiguration(condition, () -> setQueryFlag(currentLambda, ALL_FIELDS_FLAG)); } - registerInternal(condition, clazz.getFields()); + try { + registerInternal(condition, clazz.getFields()); + } catch (LinkageError e) { + /* Ignore the error */ + } } @Override public void registerAllDeclaredFieldsQuery(ConfigurationCondition condition, Class clazz) { checkNotSealed(); registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_FIELDS_FLAG)); - registerInternal(condition, clazz.getDeclaredFields()); + try { + registerInternal(condition, clazz.getDeclaredFields()); + } catch (LinkageError e) { + /* Ignore the error */ + } } private void registerField(Field reflectField) { @@ -719,7 +751,7 @@ private void registerTypesForAnnotations(AnnotatedElement annotatedElement) { for (AnnotationValue annotation : annotationExtractor.getDeclaredAnnotationData(annotatedElement)) { if (includeAnnotation(annotation)) { includedAnnotations.add(annotation); - registerTypes(annotation.getTypes()); + registerTypesForAnnotation(annotation); } } filteredAnnotations.put(annotatedElement, includedAnnotations.toArray(new AnnotationValue[0])); @@ -738,7 +770,7 @@ private void registerTypesForParameterAnnotations(AnalysisMethod method) { for (AnnotationValue annotation : annotations) { if (includeAnnotation(annotation)) { includedAnnotations.add(annotation); - registerTypes(annotation.getTypes()); + registerTypesForAnnotation(annotation); } } includedParameterAnnotations[i] = includedAnnotations.toArray(new AnnotationValue[0]); @@ -755,7 +787,7 @@ private void registerTypesForTypeAnnotations(AnnotatedElement annotatedElement) for (TypeAnnotationValue typeAnnotation : annotationExtractor.getTypeAnnotationData(annotatedElement)) { if (includeAnnotation(typeAnnotation.getAnnotationData())) { includedTypeAnnotations.add(typeAnnotation); - registerTypes(typeAnnotation.getAnnotationData().getTypes()); + registerTypesForAnnotation(typeAnnotation.getAnnotationData()); } } filteredTypeAnnotations.put(annotatedElement, includedTypeAnnotations.toArray(new TypeAnnotationValue[0])); @@ -782,6 +814,14 @@ private boolean includeAnnotation(AnnotationValue annotationValue) { return true; } + private void registerTypesForAnnotation(AnnotationValue annotationValue) { + registerTypes(annotationValue.getTypes()); + Class annotationType = annotationValue.getType(); + if (annotationType != null) { + RuntimeReflection.registerAllDeclaredMethods(annotationType); + } + } + @SuppressWarnings("cast") private void registerTypes(Collection> types) { for (Class type : types) {