diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest index e612ff3293ec..ac531e7431e5 100644 --- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest +++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest @@ -37,6 +37,15 @@ meth public static <%0 extends java.lang.Enum<{%%0}>> {%%0} valueOf(java.lang.Cl supr java.lang.Object hfds name,ordinal +CLSS public java.lang.Error +cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean) +cons public init() +cons public init(java.lang.String) +cons public init(java.lang.String,java.lang.Throwable) +cons public init(java.lang.Throwable) +supr java.lang.Throwable +hfds serialVersionUID + CLSS public java.lang.Exception cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean) cons public init() @@ -222,6 +231,15 @@ meth public abstract void fatalError() meth public abstract void flush() meth public abstract void log(org.graalvm.nativeimage.c.type.CCharPointer,org.graalvm.word.UnsignedWord) +CLSS public final org.graalvm.nativeimage.MissingReflectionRegistrationError +cons public init(java.lang.String,java.lang.Class>,java.lang.Class>,java.lang.String,java.lang.Class>[]) +meth public java.lang.Class> getElementType() +meth public java.lang.Class> getDeclaringClass() +meth public java.lang.String getElementName() +meth public java.lang.Class>[] getParameterTypes() +supr java.lang.Error +hfds serialVersionUID + CLSS public abstract interface org.graalvm.nativeimage.ObjectHandle intf org.graalvm.word.ComparableWord @@ -1075,10 +1093,26 @@ meth public !varargs static void register(boolean,boolean,java.lang.reflect.Fiel meth public !varargs static void register(boolean,java.lang.reflect.Field[]) anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="21.1") meth public !varargs static void register(java.lang.Class>[]) +meth public static void registerClassLookup(java.lang.String) meth public !varargs static void register(java.lang.reflect.Executable[]) +meth public !varargs static void registerMethodLookup(java.lang.Class>,java.lang.String,java.lang.Class>[]) +meth public !varargs static void registerConstructorLookup(java.lang.Class>,java.lang.Class>[]) meth public !varargs static void register(java.lang.reflect.Field[]) +meth public static void registerFieldLookup(java.lang.Class>,java.lang.String) meth public !varargs static void registerAsQueried(java.lang.reflect.Executable[]) meth public !varargs static void registerForReflectiveInstantiation(java.lang.Class>[]) +meth public static void registerAllClasses(java.lang.Class>) +meth public static void registerAllDeclaredClasses(java.lang.Class>) +meth public static void registerAllConstructors(java.lang.Class>) +meth public static void registerAllDeclaredConstructors(java.lang.Class>) +meth public static void registerAllFields(java.lang.Class>) +meth public static void registerAllDeclaredFields(java.lang.Class>) +meth public static void registerAllMethods(java.lang.Class>) +meth public static void registerAllDeclaredMethods(java.lang.Class>) +meth public static void registerAllNestMembers(java.lang.Class>) +meth public static void registerAllPermittedSubclasses(java.lang.Class>) +meth public static void registerAllRecordComponents(java.lang.Class>) +meth public static void registerAllSigners(java.lang.Class>) supr java.lang.Object CLSS public final org.graalvm.nativeimage.hosted.RuntimeResourceAccess diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingReflectionRegistrationError.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingReflectionRegistrationError.java new file mode 100644 index 000000000000..47567d0daac5 --- /dev/null +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingReflectionRegistrationError.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.nativeimage; + +import java.io.Serial; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * This exception is thrown when a reflective query (such as + * {@link Class#getMethod(String, Class[])}) tries to access an element that was not registered + * for reflection in the program. When an element is not registered, the exception will be + * thrown both for elements that exist and elements that do not exist on the given classpath. + *
+ * The purpose of this exception is to easily discover unregistered elements and to assure that all + * reflective operations for registered elements have the expected behaviour. + * + * We distinguish between two types of reflective queries: bulk queries and individual queries. + *+ * ObjectIndex[] objects + *+ */ + @Override + public Object[] parseObjects(int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + return decodeArray(reader, Object.class, (i) -> decodeObject(reader)); + } + /** * Parameters encoding for executables. * @@ -208,6 +241,11 @@ public boolean isHiding(int modifiers) { return (modifiers & HIDING_FLAG_MASK) != 0; } + @Override + public boolean isNegative(int modifiers) { + return (modifiers & NEGATIVE_FLAG_MASK) != 0; + } + @Override public long getMetadataByteLength() { return ImageSingletons.lookup(ReflectionMetadataEncoding.class).getEncoding().length; @@ -267,6 +305,15 @@ private static
* ReachableFieldEncoding : FieldMetadata {
+ * int modifiers (including EXISTS flag)
+ * StringIndex name
+ * }
+ *
+ *
+ * Negative query field encoding.
+ *
+ *
+ * NegativeQueryFieldEncoding : FieldMetadata {
* int modifiers (always zero)
* StringIndex name
* }
@@ -289,16 +336,18 @@ private static Object decodeField(UnsafeArrayTypeReader buf, Class> declaringC
}
boolean hiding = (modifiers & HIDING_FLAG_MASK) != 0;
assert !(complete && hiding);
+ boolean negative = (modifiers & NEGATIVE_FLAG_MASK) != 0;
+ assert !(negative && (complete || hiding));
modifiers &= ~COMPLETE_FLAG_MASK;
String name = decodeName(buf);
Class> type = (complete || hiding) ? decodeType(buf) : null;
if (!complete) {
- if (reflectOnly != hiding) {
+ if (reflectOnly != (hiding || negative)) {
/*
- * When querying for reflection fields, we want the hiding fields but not the
- * reachable fields. When querying for reachable fields, we want the reachable
- * fields but not the hiding fields.
+ * When querying for reflection fields, we want the hiding fields and negative
+ * queries but not the reachable fields. When querying for reachable fields, we want
+ * the reachable fields but not the hiding fields and negative queries.
*/
return null;
}
@@ -307,9 +356,9 @@ private static Object decodeField(UnsafeArrayTypeReader buf, Class> declaringC
}
Target_java_lang_reflect_Field field = new Target_java_lang_reflect_Field();
if (JavaVersionUtil.JAVA_SPEC >= 17) {
- field.constructorJDK17OrLater(declaringClass, name, type, modifiers, false, -1, null, null);
+ field.constructorJDK17OrLater(declaringClass, name, negative ? Object.class : type, modifiers, false, -1, null, null);
} else {
- field.constructorJDK11OrEarlier(declaringClass, name, type, modifiers, -1, null, null);
+ field.constructorJDK11OrEarlier(declaringClass, name, negative ? Object.class : type, modifiers, -1, null, null);
}
return SubstrateUtil.cast(field, Field.class);
}
@@ -379,6 +428,16 @@ private static Object decodeField(UnsafeArrayTypeReader buf, Class> declaringC
*
*
* ReachableMethodMetadata : MethodMetadata {
+ * int modifiers (including EXISTS flag)
+ * StringIndex name
+ * ClassIndex[] parameterTypes
+ * }
+ *
+ *
+ * Negative query method encoding.
+ *
+ *
+ * NegativeQueryMethodMetadata : MethodMetadata {
* int modifiers (always zero)
* StringIndex name
* ClassIndex[] parameterTypes
@@ -413,6 +472,15 @@ private static Object decodeField(UnsafeArrayTypeReader buf, Class> declaringC
*
*
* ReachableConstructorMetadata : ConstructorMetadata {
+ * int modifiers (including EXISTS flag)
+ * ClassIndex[] parameterTypes
+ * }
+ *
+ *
+ * Negative query constructor encoding.
+ *
+ *
+ * NegativeQueryConstructorMetadata : ConstructorMetadata {
* int modifiers (always zero)
* ClassIndex[] parameterTypes
* }
@@ -441,18 +509,20 @@ private static Object decodeExecutable(UnsafeArrayTypeReader buf, Class> decla
}
boolean hiding = (modifiers & HIDING_FLAG_MASK) != 0;
assert !(complete && hiding);
+ boolean negative = (modifiers & NEGATIVE_FLAG_MASK) != 0;
+ assert !(negative && (complete || hiding));
modifiers &= ~COMPLETE_FLAG_MASK;
String name = isMethod ? decodeName(buf) : null;
Object[] parameterTypes;
- if (complete || hiding) {
+ if (complete || hiding || negative) {
parameterTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf));
} else {
parameterTypes = decodeArray(buf, String.class, (i) -> decodeName(buf));
}
Class> returnType = isMethod && (complete || hiding) ? decodeType(buf) : null;
if (!complete) {
- if (reflectOnly != hiding) {
+ if (reflectOnly != (hiding || negative)) {
/*
* When querying for reflection methods, we want the hiding methods but not the
* reachable methods. When querying for reachable methods, we want the reachable
@@ -465,7 +535,7 @@ private static Object decodeExecutable(UnsafeArrayTypeReader buf, Class> decla
return new MethodDescriptor(declaringClass, name, (String[]) parameterTypes);
}
Target_java_lang_reflect_Method method = new Target_java_lang_reflect_Method();
- method.constructor(declaringClass, name, (Class>[]) parameterTypes, returnType, null, modifiers, -1, null, null, null, null);
+ method.constructor(declaringClass, name, (Class>[]) parameterTypes, negative ? Object.class : returnType, null, modifiers, -1, null, null, null, null);
return SubstrateUtil.cast(method, Executable.class);
} else {
if (!reflectOnly) {
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java
index dc45967966e2..dc0d9b700f97 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java
@@ -32,13 +32,14 @@
import org.graalvm.nativeimage.ImageSingletons;
+import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
-import com.oracle.svm.core.util.VMError;
+import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
import sun.reflect.generics.repository.ConstructorRepository;
@@ -70,7 +71,7 @@ public native void constructor(Class> declaringClass, Class>[] parameterType
@Substitute
Target_jdk_internal_reflect_ConstructorAccessor acquireConstructorAccessor() {
if (constructorAccessor == null) {
- throw VMError.unsupportedFeature("Runtime reflection is not supported for " + this);
+ throw MissingReflectionRegistrationUtils.forQueriedOnlyExecutable(SubstrateUtil.cast(this, Executable.class));
}
return constructorAccessor;
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java
index 934c71888855..bd90ba19007b 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java
@@ -32,13 +32,14 @@
import org.graalvm.nativeimage.ImageSingletons;
+import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
-import com.oracle.svm.core.util.VMError;
+import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
import sun.reflect.generics.repository.MethodRepository;
@@ -73,7 +74,7 @@ public native void constructor(Class> declaringClass, String name, Class>[]
@Substitute
public Target_jdk_internal_reflect_MethodAccessor acquireMethodAccessor() {
if (methodAccessor == null) {
- throw VMError.unsupportedFeature("Runtime reflection is not supported for " + this);
+ throw MissingReflectionRegistrationUtils.forQueriedOnlyExecutable(SubstrateUtil.cast(this, Executable.class));
}
return methodAccessor;
}
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 d4b002372b12..e24975be8677 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,6 +70,8 @@
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;
@@ -148,6 +150,7 @@ 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
@@ -185,6 +188,7 @@ 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) {
@@ -312,6 +316,14 @@ 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);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/TypeAnnotationValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/TypeAnnotationValue.java
index 750f30e9ddd0..5dbccfa154b4 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/TypeAnnotationValue.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/TypeAnnotationValue.java
@@ -24,7 +24,6 @@
*/
package com.oracle.svm.hosted.annotation;
-import java.lang.annotation.AnnotationFormatError;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Objects;
@@ -108,7 +107,7 @@ private static byte[] extractTargetInfo(ByteBuffer buf) {
buf.get();
break;
default:
- throw new AnnotationFormatError("Could not parse bytes for type annotations");
+ throw new IllegalArgumentException("Invalid target info code");
}
int endPos = buf.position();
byte[] targetInfo = new byte[endPos - startPos];
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java
index e1c3e9d606af..06b12f696877 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java
@@ -55,7 +55,7 @@
public final class ConfigurationParserUtils {
public static ReflectionConfigurationParser>> create(ReflectionRegistry registry, ImageClassLoader imageClassLoader) {
- return new ReflectionConfigurationParser<>(new ReflectionRegistryAdapter(registry, imageClassLoader),
+ return new ReflectionConfigurationParser<>(RegistryAdapter.create(registry, imageClassLoader),
ConfigurationFiles.Options.StrictConfiguration.getValue());
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java
index 7ceeb4899080..700961f041e4 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,200 +24,135 @@
*/
package com.oracle.svm.hosted.config;
-import java.lang.reflect.Executable;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
+import static com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.throwMissingRegistrationErrors;
+
import java.util.List;
import org.graalvm.nativeimage.impl.ConfigurationCondition;
-import org.graalvm.nativeimage.impl.ReflectionRegistry;
+import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
import com.oracle.svm.core.TypeResult;
import com.oracle.svm.core.configure.ConditionalElement;
-import com.oracle.svm.core.configure.ReflectionConfigurationParserDelegate;
-import com.oracle.svm.core.hub.ClassForNameSupport;
-import com.oracle.svm.core.jdk.SealedClassSupport;
import com.oracle.svm.hosted.ImageClassLoader;
-import com.oracle.svm.util.ClassUtil;
-
-import jdk.vm.ci.meta.MetaUtil;
-public class ReflectionRegistryAdapter implements ReflectionConfigurationParserDelegate>> {
- private final ReflectionRegistry registry;
- private final ImageClassLoader classLoader;
+public class ReflectionRegistryAdapter extends RegistryAdapter {
+ private final RuntimeReflectionSupport reflectionSupport;
- public ReflectionRegistryAdapter(ReflectionRegistry registry, ImageClassLoader classLoader) {
- this.registry = registry;
- this.classLoader = classLoader;
+ ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, ImageClassLoader classLoader) {
+ super(reflectionSupport, classLoader);
+ this.reflectionSupport = reflectionSupport;
}
@Override
- public void registerType(ConditionalElement> type) {
- registry.register(type.getCondition(), type.getElement());
+ public TypeResult>> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives) {
+ TypeResult>> result = super.resolveType(condition, typeName, allowPrimitives);
+ if (!result.isPresent()) {
+ Throwable classLookupException = result.getException();
+ if (classLookupException instanceof LinkageError) {
+ reflectionSupport.registerClassLookupException(condition, typeName, classLookupException);
+ } else if (throwMissingRegistrationErrors() && classLookupException instanceof ClassNotFoundException) {
+ reflectionSupport.registerClassLookup(condition, typeName);
+ }
+ }
+ return result;
}
@Override
- public TypeResult resolveCondition(String typeName) {
- String canonicalizedName = canonicalizeTypeName(typeName);
- TypeResult> clazz = classLoader.findClass(canonicalizedName);
- return clazz.map(Class::getTypeName)
- .map(ConfigurationCondition::create);
+ public void registerPublicClasses(ConditionalElement> type) {
+ reflectionSupport.registerAllClassesQuery(type.getCondition(), type.getElement());
}
@Override
- public TypeResult>> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives) {
- String name = canonicalizeTypeName(typeName);
- TypeResult> clazz = classLoader.findClass(name, allowPrimitives);
- if (!clazz.isPresent()) {
- Throwable classLookupException = clazz.getException();
- if (classLookupException instanceof LinkageError || ClassForNameSupport.Options.ExitOnUnknownClassLoadingFailure.getValue()) {
- registry.registerClassLookupException(condition, typeName, classLookupException);
- }
- }
- return clazz.map(c -> new ConditionalElement<>(condition, c));
+ public void registerDeclaredClasses(ConditionalElement> type) {
+ reflectionSupport.registerAllDeclaredClassesQuery(type.getCondition(), type.getElement());
}
- private static String canonicalizeTypeName(String typeName) {
- String name = typeName;
- if (name.indexOf('[') != -1) {
- /* accept "int[][]", "java.lang.String[]" */
- name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true);
- }
- return name;
+ @Override
+ public void registerRecordComponents(ConditionalElement> type) {
+ reflectionSupport.registerAllRecordComponentsQuery(type.getCondition(), type.getElement());
}
@Override
- public void registerPublicClasses(ConditionalElement> type) {
- registry.register(type.getCondition(), type.getElement().getClasses());
+ public void registerPermittedSubclasses(ConditionalElement> type) {
+ reflectionSupport.registerAllPermittedSubclassesQuery(type.getCondition(), type.getElement());
}
@Override
- public void registerDeclaredClasses(ConditionalElement> type) {
- registry.register(type.getCondition(), type.getElement().getDeclaredClasses());
+ public void registerNestMembers(ConditionalElement> type) {
+ reflectionSupport.registerAllNestMembersQuery(type.getCondition(), type.getElement());
}
@Override
- public void registerPermittedSubclasses(ConditionalElement> type) {
- Class>[] classes = SealedClassSupport.singleton().getPermittedSubclasses(type.getElement());
- if (classes != null) {
- registry.register(type.getCondition(), classes);
- }
+ public void registerSigners(ConditionalElement> type) {
+ reflectionSupport.registerAllSignersQuery(type.getCondition(), type.getElement());
}
@Override
public void registerPublicFields(ConditionalElement> type) {
- registry.register(type.getCondition(), false, type.getElement().getFields());
+ reflectionSupport.registerAllFieldsQuery(type.getCondition(), type.getElement());
}
@Override
public void registerDeclaredFields(ConditionalElement> type) {
- registry.register(type.getCondition(), false, type.getElement().getDeclaredFields());
+ reflectionSupport.registerAllDeclaredFieldsQuery(type.getCondition(), type.getElement());
}
@Override
public void registerPublicMethods(boolean queriedOnly, ConditionalElement> type) {
- registry.register(type.getCondition(), queriedOnly, type.getElement().getMethods());
+ reflectionSupport.registerAllMethodsQuery(type.getCondition(), queriedOnly, type.getElement());
}
@Override
public void registerDeclaredMethods(boolean queriedOnly, ConditionalElement> type) {
- registry.register(type.getCondition(), queriedOnly, type.getElement().getDeclaredMethods());
+ reflectionSupport.registerAllDeclaredMethodsQuery(type.getCondition(), queriedOnly, type.getElement());
}
@Override
public void registerPublicConstructors(boolean queriedOnly, ConditionalElement> type) {
- registry.register(type.getCondition(), queriedOnly, type.getElement().getConstructors());
+ reflectionSupport.registerAllConstructorsQuery(type.getCondition(), queriedOnly, type.getElement());
}
@Override
public void registerDeclaredConstructors(boolean queriedOnly, ConditionalElement> type) {
- registry.register(type.getCondition(), queriedOnly, type.getElement().getDeclaredConstructors());
+ reflectionSupport.registerAllDeclaredConstructorsQuery(type.getCondition(), queriedOnly, type.getElement());
}
@Override
public void registerField(ConditionalElement> type, String fieldName, boolean allowWrite) throws NoSuchFieldException {
- registry.register(type.getCondition(), allowWrite, type.getElement().getDeclaredField(fieldName));
- }
-
- @Override
- public boolean registerAllMethodsWithName(boolean queriedOnly, ConditionalElement> type, String methodName) {
- boolean found = false;
- Executable[] methods = type.getElement().getDeclaredMethods();
- for (Executable method : methods) {
- if (method.getName().equals(methodName)) {
- registerExecutable(type.getCondition(), queriedOnly, method);
- found = true;
+ try {
+ super.registerField(type, fieldName, allowWrite);
+ } catch (NoSuchFieldException e) {
+ if (throwMissingRegistrationErrors()) {
+ reflectionSupport.registerFieldLookup(type.getCondition(), type.getElement(), fieldName);
+ } else {
+ throw e;
}
}
- return found;
- }
-
- @Override
- public boolean registerAllConstructors(boolean queriedOnly, ConditionalElement> type) {
- Executable[] methods = type.getElement().getDeclaredConstructors();
- registerExecutable(type.getCondition(), queriedOnly, methods);
- return methods.length > 0;
- }
-
- @Override
- public void registerUnsafeAllocated(ConditionalElement> clazz) {
- Class> type = clazz.getElement();
- if (!type.isArray() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
- registry.register(clazz.getCondition(), true, clazz.getElement());
- /*
- * Ignore otherwise as the implementation of allocateInstance will anyhow throw an
- * exception.
- */
- }
}
@Override
public void registerMethod(boolean queriedOnly, ConditionalElement> type, String methodName, List>> methodParameterTypes) throws NoSuchMethodException {
- Class>[] parameterTypesArray = getParameterTypes(methodParameterTypes);
- Method method;
try {
- method = type.getElement().getDeclaredMethod(methodName, parameterTypesArray);
- } catch (NoClassDefFoundError e) {
- /*
- * getDeclaredMethod() builds a set of all the declared methods, which can fail when a
- * symbolic reference from another method to a type (via parameters, return value)
- * cannot be resolved. getMethod() builds a different set of methods and can still
- * succeed. This case must be handled for predefined classes when, during the run
- * observed by the agent, a referenced class was not loaded and is not available now
- * precisely because the application used getMethod() instead of getDeclaredMethod().
- */
- try {
- method = type.getElement().getMethod(methodName, parameterTypesArray);
- } catch (Throwable ignored) {
+ super.registerMethod(queriedOnly, type, methodName, methodParameterTypes);
+ } catch (NoSuchMethodException e) {
+ if (throwMissingRegistrationErrors()) {
+ reflectionSupport.registerMethodLookup(type.getCondition(), type.getElement(), methodName, getParameterTypes(methodParameterTypes));
+ } else {
throw e;
}
}
- registerExecutable(type.getCondition(), queriedOnly, method);
}
@Override
public void registerConstructor(boolean queriedOnly, ConditionalElement> type, List>> methodParameterTypes) throws NoSuchMethodException {
- Class>[] parameterTypesArray = getParameterTypes(methodParameterTypes);
- registerExecutable(type.getCondition(), queriedOnly, type.getElement().getDeclaredConstructor(parameterTypesArray));
- }
-
- private static Class>[] getParameterTypes(List>> methodParameterTypes) {
- return methodParameterTypes.stream()
- .map(ConditionalElement::getElement)
- .toArray(Class>[]::new);
- }
-
- private void registerExecutable(ConfigurationCondition condition, boolean queriedOnly, Executable... executable) {
- registry.register(condition, queriedOnly, executable);
- }
-
- @Override
- public String getTypeName(ConditionalElement> type) {
- return type.getElement().getTypeName();
- }
-
- @Override
- public String getSimpleName(ConditionalElement> type) {
- return ClassUtil.getUnqualifiedName(type.getElement());
+ try {
+ super.registerConstructor(queriedOnly, type, methodParameterTypes);
+ } catch (NoSuchMethodException e) {
+ if (throwMissingRegistrationErrors()) {
+ reflectionSupport.registerConstructorLookup(type.getCondition(), type.getElement(), getParameterTypes(methodParameterTypes));
+ } else {
+ throw e;
+ }
+ }
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java
new file mode 100644
index 000000000000..1725a1cc7752
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.hosted.config;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import org.graalvm.nativeimage.impl.ConfigurationCondition;
+import org.graalvm.nativeimage.impl.ReflectionRegistry;
+import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
+
+import com.oracle.svm.core.TypeResult;
+import com.oracle.svm.core.configure.ConditionalElement;
+import com.oracle.svm.core.configure.ReflectionConfigurationParserDelegate;
+import com.oracle.svm.hosted.ImageClassLoader;
+import com.oracle.svm.util.ClassUtil;
+
+import jdk.vm.ci.meta.MetaUtil;
+
+public class RegistryAdapter implements ReflectionConfigurationParserDelegate>> {
+ private final ReflectionRegistry registry;
+ private final ImageClassLoader classLoader;
+
+ public static RegistryAdapter create(ReflectionRegistry registry, ImageClassLoader classLoader) {
+ if (registry instanceof RuntimeReflectionSupport) {
+ return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, classLoader);
+ } else {
+ return new RegistryAdapter(registry, classLoader);
+ }
+ }
+
+ RegistryAdapter(ReflectionRegistry registry, ImageClassLoader classLoader) {
+ this.registry = registry;
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public void registerType(ConditionalElement> type) {
+ registry.register(type.getCondition(), type.getElement());
+ }
+
+ @Override
+ public TypeResult resolveCondition(String typeName) {
+ String canonicalizedName = canonicalizeTypeName(typeName);
+ TypeResult> clazz = classLoader.findClass(canonicalizedName);
+ return clazz.map(Class::getTypeName)
+ .map(ConfigurationCondition::create);
+ }
+
+ @Override
+ public TypeResult>> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives) {
+ String name = canonicalizeTypeName(typeName);
+ TypeResult> clazz = classLoader.findClass(name, allowPrimitives);
+ return clazz.map(c -> new ConditionalElement<>(condition, c));
+ }
+
+ private static String canonicalizeTypeName(String typeName) {
+ String name = typeName;
+ if (name.indexOf('[') != -1) {
+ /* accept "int[][]", "java.lang.String[]" */
+ name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true);
+ }
+ return name;
+ }
+
+ @Override
+ public void registerPublicClasses(ConditionalElement> type) {
+ registry.register(type.getCondition(), type.getElement().getClasses());
+ }
+
+ @Override
+ public void registerDeclaredClasses(ConditionalElement> type) {
+ registry.register(type.getCondition(), type.getElement().getDeclaredClasses());
+ }
+
+ @Override
+ public void registerRecordComponents(ConditionalElement> type) {
+ }
+
+ @Override
+ public void registerPermittedSubclasses(ConditionalElement> type) {
+ }
+
+ @Override
+ public void registerNestMembers(ConditionalElement> type) {
+ }
+
+ @Override
+ public void registerSigners(ConditionalElement> type) {
+ }
+
+ @Override
+ public void registerPublicFields(ConditionalElement> type) {
+ registry.register(type.getCondition(), false, type.getElement().getFields());
+ }
+
+ @Override
+ public void registerDeclaredFields(ConditionalElement> type) {
+ registry.register(type.getCondition(), false, type.getElement().getDeclaredFields());
+ }
+
+ @Override
+ public void registerPublicMethods(boolean queriedOnly, ConditionalElement> type) {
+ registry.register(type.getCondition(), queriedOnly, type.getElement().getMethods());
+ }
+
+ @Override
+ public void registerDeclaredMethods(boolean queriedOnly, ConditionalElement> type) {
+ registry.register(type.getCondition(), queriedOnly, type.getElement().getDeclaredMethods());
+ }
+
+ @Override
+ public void registerPublicConstructors(boolean queriedOnly, ConditionalElement> type) {
+ registry.register(type.getCondition(), queriedOnly, type.getElement().getConstructors());
+ }
+
+ @Override
+ public void registerDeclaredConstructors(boolean queriedOnly, ConditionalElement> type) {
+ registry.register(type.getCondition(), queriedOnly, type.getElement().getDeclaredConstructors());
+ }
+
+ @Override
+ public void registerField(ConditionalElement> type, String fieldName, boolean allowWrite) throws NoSuchFieldException {
+ registry.register(type.getCondition(), allowWrite, type.getElement().getDeclaredField(fieldName));
+ }
+
+ @Override
+ public boolean registerAllMethodsWithName(boolean queriedOnly, ConditionalElement> type, String methodName) {
+ boolean found = false;
+ Executable[] methods = type.getElement().getDeclaredMethods();
+ for (Executable method : methods) {
+ if (method.getName().equals(methodName)) {
+ registerExecutable(type.getCondition(), queriedOnly, method);
+ found = true;
+ }
+ }
+ return found;
+ }
+
+ @Override
+ public boolean registerAllConstructors(boolean queriedOnly, ConditionalElement> type) {
+ Executable[] methods = type.getElement().getDeclaredConstructors();
+ registerExecutable(type.getCondition(), queriedOnly, methods);
+ return methods.length > 0;
+ }
+
+ @Override
+ public void registerUnsafeAllocated(ConditionalElement> clazz) {
+ Class> type = clazz.getElement();
+ if (!type.isArray() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
+ registry.register(clazz.getCondition(), true, clazz.getElement());
+ /*
+ * Ignore otherwise as the implementation of allocateInstance will anyhow throw an
+ * exception.
+ */
+ }
+ }
+
+ @Override
+ public void registerMethod(boolean queriedOnly, ConditionalElement> type, String methodName, List>> methodParameterTypes) throws NoSuchMethodException {
+ Class>[] parameterTypesArray = getParameterTypes(methodParameterTypes);
+ Method method;
+ try {
+ method = type.getElement().getDeclaredMethod(methodName, parameterTypesArray);
+ } catch (NoClassDefFoundError e) {
+ /*
+ * getDeclaredMethod() builds a set of all the declared methods, which can fail when a
+ * symbolic reference from another method to a type (via parameters, return value)
+ * cannot be resolved. getMethod() builds a different set of methods and can still
+ * succeed. This case must be handled for predefined classes when, during the run
+ * observed by the agent, a referenced class was not loaded and is not available now
+ * precisely because the application used getMethod() instead of getDeclaredMethod().
+ */
+ try {
+ method = type.getElement().getMethod(methodName, parameterTypesArray);
+ } catch (Throwable ignored) {
+ throw e;
+ }
+ }
+ registerExecutable(type.getCondition(), queriedOnly, method);
+ }
+
+ @Override
+ public void registerConstructor(boolean queriedOnly, ConditionalElement> type, List>> methodParameterTypes) throws NoSuchMethodException {
+ Class>[] parameterTypesArray = getParameterTypes(methodParameterTypes);
+ registerExecutable(type.getCondition(), queriedOnly, type.getElement().getDeclaredConstructor(parameterTypesArray));
+ }
+
+ static Class>[] getParameterTypes(List>> methodParameterTypes) {
+ return methodParameterTypes.stream()
+ .map(ConditionalElement::getElement)
+ .toArray(Class>[]::new);
+ }
+
+ private void registerExecutable(ConfigurationCondition condition, boolean queriedOnly, Executable... executable) {
+ registry.register(condition, queriedOnly, executable);
+ }
+
+ @Override
+ public String getTypeName(ConditionalElement> type) {
+ return type.getElement().getTypeName();
+ }
+
+ @Override
+ public String getSimpleName(ConditionalElement> type) {
+ return ClassUtil.getUnqualifiedName(type.getElement());
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java
index 1ef40573259a..2ec6df5486d7 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java
@@ -24,6 +24,7 @@
*/
package com.oracle.svm.hosted.image;
+import static com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.throwMissingRegistrationErrors;
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
import java.lang.reflect.AccessibleObject;
@@ -61,6 +62,7 @@
import com.oracle.graal.pointsto.infrastructure.WrappedElement;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
+import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.objectfile.ObjectFile;
import com.oracle.svm.common.meta.MultiMethod;
import com.oracle.svm.core.SubstrateOptions;
@@ -268,93 +270,116 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code
}
}
- Set includedFields = new HashSet<>();
- Set includedMethods = new HashSet<>();
+ Set includedFields = new HashSet<>();
+ Set includedMethods = new HashSet<>();
Map configurationFields = reflectionSupport.getReflectionFields();
Map configurationExecutables = reflectionSupport.getReflectionExecutables();
reflectionSupport.getHeapReflectionFields().forEach(((analysisField, reflectField) -> {
- HostedField hostedField = hUniverse.lookup(analysisField);
- if (!includedFields.contains(hostedField)) {
+ if (includedFields.add(analysisField)) {
+ HostedField hostedField = hUniverse.lookup(analysisField);
reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, hostedField, reflectField, configurationFields.containsKey(analysisField));
- includedFields.add(hostedField);
}
}));
reflectionSupport.getHeapReflectionExecutables().forEach(((analysisMethod, reflectMethod) -> {
- HostedMethod hostedMethod = hUniverse.lookup(analysisMethod);
- if (!includedMethods.contains(hostedMethod)) {
+ if (includedMethods.add(analysisMethod)) {
+ HostedMethod hostedMethod = hUniverse.lookup(analysisMethod);
reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, hostedMethod, reflectMethod, configurationExecutables.containsKey(analysisMethod));
- includedMethods.add(hostedMethod);
}
}));
configurationFields.forEach(((analysisField, reflectField) -> {
- HostedField hostedField = hUniverse.lookup(analysisField);
- if (!includedFields.contains(hostedField)) {
+ if (includedFields.add(analysisField)) {
+ HostedField hostedField = hUniverse.lookup(analysisField);
reflectionMetadataEncoder.addReflectionFieldMetadata(hMetaAccess, hostedField, reflectField);
- includedFields.add(hostedField);
}
}));
configurationExecutables.forEach(((analysisMethod, reflectMethod) -> {
- HostedMethod method = hUniverse.lookup(analysisMethod);
- if (!includedMethods.contains(method)) {
+ if (includedMethods.add(analysisMethod)) {
+ HostedMethod method = hUniverse.lookup(analysisMethod);
Object accessor = reflectionSupport.getAccessor(analysisMethod);
reflectionMetadataEncoder.addReflectionExecutableMetadata(hMetaAccess, method, reflectMethod, accessor);
- includedMethods.add(method);
}
}));
for (Object field : reflectionSupport.getHidingReflectionFields()) {
- AnalysisField hidingField = (AnalysisField) field;
- HostedField hostedField = hUniverse.optionalLookup(hidingField);
- if (hostedField == null || !includedFields.contains(hostedField)) {
- HostedType declaringType = hUniverse.lookup(hidingField.getDeclaringClass());
- String name = hidingField.getName();
- HostedType type = hUniverse.lookup(hidingField.getType());
- int modifiers = hidingField.getModifiers();
- reflectionMetadataEncoder.addHidingFieldMetadata(hidingField, declaringType, name, type, modifiers);
- if (hostedField != null) {
- includedFields.add(hostedField);
- }
+ AnalysisField analysisField = (AnalysisField) field;
+ if (includedFields.add(analysisField)) {
+ HostedType declaringType = hUniverse.lookup(analysisField.getDeclaringClass());
+ String name = analysisField.getName();
+ HostedType type = hUniverse.lookup(analysisField.getType());
+ int modifiers = analysisField.getModifiers();
+ reflectionMetadataEncoder.addHidingFieldMetadata(analysisField, declaringType, name, type, modifiers);
}
}
for (Object method : reflectionSupport.getHidingReflectionMethods()) {
- AnalysisMethod hidingMethod = (AnalysisMethod) method;
- HostedMethod hostedMethod = hUniverse.optionalLookup(hidingMethod);
- if (hostedMethod == null || !includedMethods.contains(hostedMethod)) {
- HostedType declaringType = hUniverse.lookup(hidingMethod.getDeclaringClass());
- String name = hidingMethod.getName();
- JavaType[] analysisParameterTypes = hidingMethod.getSignature().toParameterTypes(null);
+ AnalysisMethod analysisMethod = (AnalysisMethod) method;
+ if (includedMethods.add(analysisMethod)) {
+ HostedType declaringType = hUniverse.lookup(analysisMethod.getDeclaringClass());
+ String name = analysisMethod.getName();
+ JavaType[] analysisParameterTypes = analysisMethod.getSignature().toParameterTypes(null);
HostedType[] parameterTypes = new HostedType[analysisParameterTypes.length];
for (int i = 0; i < analysisParameterTypes.length; ++i) {
parameterTypes[i] = hUniverse.lookup(analysisParameterTypes[i]);
}
- int modifiers = hidingMethod.getModifiers();
- HostedType returnType = hUniverse.lookup(hidingMethod.getSignature().getReturnType(null));
- reflectionMetadataEncoder.addHidingMethodMetadata(hidingMethod, declaringType, name, parameterTypes, modifiers, returnType);
- if (hostedMethod != null) {
- includedMethods.add(hostedMethod);
- }
+ int modifiers = analysisMethod.getModifiers();
+ HostedType returnType = hUniverse.lookup(analysisMethod.getSignature().getReturnType(null));
+ reflectionMetadataEncoder.addHidingMethodMetadata(analysisMethod, declaringType, name, parameterTypes, modifiers, returnType);
}
}
if (SubstrateOptions.IncludeMethodData.getValue()) {
for (HostedField field : hUniverse.getFields()) {
- if (field.isAccessed() && !includedFields.contains(field)) {
+ if (field.isAccessed() && !includedFields.contains(field.getWrapped())) {
reflectionMetadataEncoder.addReachableFieldMetadata(field);
}
}
for (HostedMethod method : hUniverse.getMethods()) {
- if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod() && !includedMethods.contains(method)) {
+ if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod() && !includedMethods.contains(method.getWrapped())) {
reflectionMetadataEncoder.addReachableExecutableMetadata(method);
}
}
}
+ if (throwMissingRegistrationErrors()) {
+ reflectionSupport.getNegativeFieldQueries().forEach((analysisType, fieldNames) -> {
+ HostedType hostedType = hUniverse.optionalLookup(analysisType);
+ if (hostedType != null) {
+ for (String fieldName : fieldNames) {
+ reflectionMetadataEncoder.addNegativeFieldQueryMetadata(hostedType, fieldName);
+ }
+ }
+ });
+
+ reflectionSupport.getNegativeMethodQueries().forEach((analysisType, methodSignatures) -> {
+ HostedType hostedType = hUniverse.optionalLookup(analysisType);
+ if (hostedType != null) {
+ for (AnalysisMethod.Signature methodSignature : methodSignatures) {
+ HostedType[] parameterTypes = hUniverse.optionalLookup(methodSignature.parameterTypes());
+ if (parameterTypes != null) {
+ reflectionMetadataEncoder.addNegativeMethodQueryMetadata(hostedType, methodSignature.name(), parameterTypes);
+ }
+ }
+ }
+ });
+
+ reflectionSupport.getNegativeConstructorQueries().forEach((analysisType, constructorSignatures) -> {
+ HostedType hostedType = hUniverse.optionalLookup(analysisType);
+ if (hostedType != null) {
+ for (AnalysisType[] analysisParameterTypes : constructorSignatures) {
+ HostedType[] parameterTypes = hUniverse.optionalLookup(analysisParameterTypes);
+ if (parameterTypes != null) {
+ reflectionMetadataEncoder.addNegativeConstructorQueryMetadata(hostedType, parameterTypes);
+ }
+ }
+ }
+ });
+ }
+
if (NativeImageOptions.PrintMethodHistogram.getValue()) {
System.out.println("encoded deopt entry points ; " + frameInfoCustomization.numDeoptEntryPoints);
System.out.println("encoded during call entry points ; " + frameInfoCustomization.numDuringCallEntryPoints);
@@ -661,6 +686,12 @@ public interface ReflectionMetadataEncoder extends EncodedReflectionMetadataSupp
void addReachableExecutableMetadata(HostedMethod method);
+ void addNegativeFieldQueryMetadata(HostedType declaringClass, String fieldName);
+
+ void addNegativeMethodQueryMetadata(HostedType declaringClass, String methodName, HostedType[] parameterTypes);
+
+ void addNegativeConstructorQueryMetadata(HostedType declaringClass, HostedType[] parameterTypes);
+
void encodeAllAndInstall();
Method getRoot = ReflectionUtil.lookupMethod(AccessibleObject.class, "getRoot");
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java
index 2bc43f9f91fb..3a8bbebe3c44 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java
@@ -348,6 +348,17 @@ public HostedType optionalLookup(JavaType type) {
return types.get(type);
}
+ public HostedType[] optionalLookup(JavaType... javaTypes) {
+ HostedType[] result = new HostedType[javaTypes.length];
+ for (int i = 0; i < javaTypes.length; ++i) {
+ result[i] = optionalLookup(javaTypes[i]);
+ if (result[i] == null) {
+ return null;
+ }
+ }
+ return result;
+ }
+
@Override
public HostedField lookup(JavaField field) {
JavaField result = lookupAllowUnresolved(field);
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 8a58beb19b08..12b3718b3aa5 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
@@ -24,6 +24,20 @@
*/
package com.oracle.svm.hosted.reflect;
+import static com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.throwMissingRegistrationErrors;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_CLASSES_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_CONSTRUCTORS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_DECLARED_CLASSES_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_DECLARED_CONSTRUCTORS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_DECLARED_FIELDS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_DECLARED_METHODS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_FIELDS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_METHODS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_NEST_MEMBERS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_PERMITTED_SUBCLASSES_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_RECORD_COMPONENTS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_SIGNERS_FLAG;
+
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
@@ -33,6 +47,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
+import java.lang.reflect.RecordComponent;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
@@ -91,6 +106,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl
// Reflection data
private final Map, Object[]> registeredRecordComponents = new ConcurrentHashMap<>();
private final Map, Set>> innerClasses = new ConcurrentHashMap<>();
+ private final Map, Integer> enabledQueriesFlags = new ConcurrentHashMap<>();
private final Map registeredFields = new ConcurrentHashMap<>();
private final Set hidingFields = ConcurrentHashMap.newKeySet();
private final Map registeredMethods = new ConcurrentHashMap<>();
@@ -102,9 +118,14 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl
private final Map heapFields = new ConcurrentHashMap<>();
private final Map heapMethods = new ConcurrentHashMap<>();
+ // Negative queries
+ private final Map> negativeFieldLookups = new ConcurrentHashMap<>();
+ private final Map> negativeMethodLookups = new ConcurrentHashMap<>();
+ private final Map> negativeConstructorLookups = new ConcurrentHashMap<>();
+
// Intermediate bookkeeping
private final Map> processedTypes = new ConcurrentHashMap<>();
- private final Map, Set> pendingRecordClasses = new ConcurrentHashMap<>();
+ private final Map, Set> pendingRecordClasses;
private final Set> pendingRegistrations = ConcurrentHashMap.newKeySet();
// Annotations handling
@@ -114,6 +135,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl
ReflectionDataBuilder(SubstrateAnnotationExtractor annotationExtractor) {
this.annotationExtractor = annotationExtractor;
+ pendingRecordClasses = !throwMissingRegistrationErrors() ? new ConcurrentHashMap<>() : null;
}
public void duringSetup(AnalysisMetaAccess analysisMetaAccess, AnalysisUniverse analysisUniverse) {
@@ -138,6 +160,10 @@ private void register(Consumer registrationCallback) {
}
}
+ private void setQueryFlag(Class> clazz, int flag) {
+ enabledQueriesFlags.compute(clazz, (key, oldValue) -> (oldValue == null) ? flag : (oldValue | flag));
+ }
+
@Override
public void register(ConfigurationCondition condition, boolean unsafeInstantiated, Class> clazz) {
checkNotSealed();
@@ -145,6 +171,20 @@ public void register(ConfigurationCondition condition, boolean unsafeInstantiate
() -> analysisUniverse.getBigbang().postTask(debug -> registerClass(clazz, unsafeInstantiated))));
}
+ @Override
+ public void registerAllClassesQuery(ConfigurationCondition condition, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_CLASSES_FLAG));
+ register(condition, clazz.getClasses());
+ }
+
+ @Override
+ public void registerAllDeclaredClassesQuery(ConfigurationCondition condition, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_CLASSES_FLAG));
+ register(condition, clazz.getDeclaredClasses());
+ }
+
private void registerClass(Class> clazz, boolean unsafeInstantiated) {
if (shouldExcludeClass(clazz)) {
return;
@@ -173,6 +213,52 @@ public void registerClassLookupException(ConfigurationCondition condition, Strin
registerConditionalConfiguration(condition, () -> ClassForNameSupport.registerExceptionForClass(typeName, t));
}
+ @Override
+ public void registerClassLookup(ConfigurationCondition condition, String typeName) {
+ checkNotSealed();
+ try {
+ register(condition, Class.forName(typeName));
+ } catch (ClassNotFoundException e) {
+ registerConditionalConfiguration(condition, () -> ClassForNameSupport.registerNegativeQuery(typeName));
+ } catch (Throwable t) {
+ registerClassLookupException(condition, typeName, t);
+ }
+ }
+
+ @Override
+ public void registerAllRecordComponentsQuery(ConfigurationCondition condition, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_RECORD_COMPONENTS_FLAG));
+ register(analysisUniverse -> registerConditionalConfiguration(condition,
+ () -> analysisUniverse.getBigbang().postTask(debug -> registerRecordComponents(clazz))));
+ }
+
+ @Override
+ public void registerAllPermittedSubclassesQuery(ConfigurationCondition condition, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_PERMITTED_SUBCLASSES_FLAG));
+ if (clazz.isSealed()) {
+ register(condition, clazz.getPermittedSubclasses());
+ }
+ }
+
+ @Override
+ public void registerAllNestMembersQuery(ConfigurationCondition condition, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_NEST_MEMBERS_FLAG));
+ for (Class> nestMember : clazz.getNestMembers()) {
+ if (nestMember != clazz) {
+ register(condition, nestMember);
+ }
+ }
+ }
+
+ @Override
+ public void registerAllSignersQuery(ConfigurationCondition condition, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_SIGNERS_FLAG));
+ }
+
@Override
public void register(ConfigurationCondition condition, boolean queriedOnly, Executable... executables) {
checkNotSealed();
@@ -183,6 +269,40 @@ public void register(ConfigurationCondition condition, boolean queriedOnly, Exec
}));
}
+ @Override
+ public void registerAllMethodsQuery(ConfigurationCondition condition, boolean queriedOnly, Class> clazz) {
+ checkNotSealed();
+ for (Class> current = clazz; current != null; current = current.getSuperclass()) {
+ final Class> currentLambda = current;
+ registerConditionalConfiguration(condition, () -> setQueryFlag(currentLambda, ALL_METHODS_FLAG));
+ }
+ register(condition, queriedOnly, clazz.getMethods());
+ }
+
+ @Override
+ public void registerAllDeclaredMethodsQuery(ConfigurationCondition condition, boolean queriedOnly, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_METHODS_FLAG));
+ register(condition, queriedOnly, clazz.getDeclaredMethods());
+ }
+
+ @Override
+ public void registerAllConstructorsQuery(ConfigurationCondition condition, boolean queriedOnly, Class> clazz) {
+ checkNotSealed();
+ for (Class> current = clazz; current != null; current = current.getSuperclass()) {
+ final Class> currentLambda = current;
+ registerConditionalConfiguration(condition, () -> setQueryFlag(currentLambda, ALL_CONSTRUCTORS_FLAG));
+ }
+ register(condition, queriedOnly, clazz.getConstructors());
+ }
+
+ @Override
+ public void registerAllDeclaredConstructorsQuery(ConfigurationCondition condition, boolean queriedOnly, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_CONSTRUCTORS_FLAG));
+ register(condition, queriedOnly, clazz.getDeclaredConstructors());
+ }
+
private void registerMethod(boolean queriedOnly, Executable reflectExecutable) {
if (SubstitutionReflectivityFilter.shouldExclude(reflectExecutable, metaAccess, universe)) {
return;
@@ -198,11 +318,14 @@ private void registerMethod(boolean queriedOnly, Executable reflectExecutable) {
* The image needs to know about subtypes shadowing methods registered for reflection to
* ensure the correctness of run-time reflection queries.
*/
- analysisAccess.registerSubtypeReachabilityHandler((access, subType) -> {
- universe.getBigbang().postTask(debug -> checkSubtypeForOverridingMethod(analysisMethod, metaAccess.lookupJavaType(subType)));
- }, declaringClass);
+ analysisAccess.registerSubtypeReachabilityHandler(
+ (access, subType) -> universe.getBigbang().postTask(debug -> checkSubtypeForOverridingMethod(analysisMethod, metaAccess.lookupJavaType(subType))), declaringClass);
- if (RecordSupport.singleton().isRecord(declaringClass)) {
+ if (declaringType.isAnnotation() && !analysisMethod.isConstructor()) {
+ processAnnotationMethod(queriedOnly, (Method) reflectExecutable);
+ }
+
+ if (!throwMissingRegistrationErrors() && RecordSupport.singleton().isRecord(declaringClass)) {
pendingRecordClasses.computeIfPresent(declaringClass, (clazz, unregisteredAccessors) -> {
if (unregisteredAccessors.remove(reflectExecutable) && unregisteredAccessors.isEmpty()) {
registerRecordComponents(declaringClass);
@@ -210,10 +333,6 @@ private void registerMethod(boolean queriedOnly, Executable reflectExecutable) {
return unregisteredAccessors;
});
}
-
- if (declaringType.isAnnotation() && !analysisMethod.isConstructor()) {
- processAnnotationMethod(queriedOnly, (Method) reflectExecutable);
- }
}
/*
@@ -229,9 +348,36 @@ private void registerMethod(boolean queriedOnly, Executable reflectExecutable) {
}
}
+ @Override
+ public void registerMethodLookup(ConfigurationCondition condition, Class> declaringClass, String methodName, Class>... parameterTypes) {
+ checkNotSealed();
+ try {
+ register(condition, true, declaringClass.getDeclaredMethod(methodName, parameterTypes));
+ } catch (NoSuchMethodException e) {
+ registerConditionalConfiguration(condition, () -> negativeMethodLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet())
+ .add(new AnalysisMethod.Signature(methodName, metaAccess.lookupJavaTypes(parameterTypes))));
+ }
+ }
+
+ @Override
+ public void registerConstructorLookup(ConfigurationCondition condition, Class> declaringClass, Class>... parameterTypes) {
+ checkNotSealed();
+ try {
+ register(condition, true, declaringClass.getDeclaredConstructor(parameterTypes));
+ } catch (NoSuchMethodException e) {
+ registerConditionalConfiguration(condition,
+ () -> negativeConstructorLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet())
+ .add(metaAccess.lookupJavaTypes(parameterTypes)));
+ }
+ }
+
@Override
public void register(ConfigurationCondition condition, boolean finalIsWritable, Field... fields) {
checkNotSealed();
+ registerInternal(condition, fields);
+ }
+
+ private void registerInternal(ConfigurationCondition condition, Field... fields) {
register(analysisUniverse -> registerConditionalConfiguration(condition, () -> {
for (Field field : fields) {
analysisUniverse.getBigbang().postTask(debug -> registerField(field));
@@ -239,6 +385,23 @@ public void register(ConfigurationCondition condition, boolean finalIsWritable,
}));
}
+ @Override
+ public void registerAllFieldsQuery(ConfigurationCondition condition, Class> clazz) {
+ checkNotSealed();
+ for (Class> current = clazz; current != null; current = current.getSuperclass()) {
+ final Class> currentLambda = current;
+ registerConditionalConfiguration(condition, () -> setQueryFlag(currentLambda, ALL_FIELDS_FLAG));
+ }
+ registerInternal(condition, clazz.getFields());
+ }
+
+ @Override
+ public void registerAllDeclaredFieldsQuery(ConfigurationCondition condition, Class> clazz) {
+ checkNotSealed();
+ registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_FIELDS_FLAG));
+ registerInternal(condition, clazz.getDeclaredFields());
+ }
+
private void registerField(Field reflectField) {
if (SubstitutionReflectivityFilter.shouldExclude(reflectField, metaAccess, universe)) {
return;
@@ -253,9 +416,9 @@ private void registerField(Field reflectField) {
* The image needs to know about subtypes shadowing fields registered for reflection to
* ensure the correctness of run-time reflection queries.
*/
- analysisAccess.registerSubtypeReachabilityHandler((access, subType) -> {
- universe.getBigbang().postTask(debug -> checkSubtypeForOverridingField(analysisField, metaAccess.lookupJavaType(subType)));
- }, declaringClass.getJavaClass());
+ analysisAccess.registerSubtypeReachabilityHandler(
+ (access, subType) -> universe.getBigbang().postTask(debug -> checkSubtypeForOverridingField(analysisField, metaAccess.lookupJavaType(subType))),
+ declaringClass.getJavaClass());
if (declaringClass.isAnnotation()) {
processAnnotationField(reflectField);
@@ -263,6 +426,16 @@ private void registerField(Field reflectField) {
}
}
+ @Override
+ public void registerFieldLookup(ConfigurationCondition condition, Class> declaringClass, String fieldName) {
+ checkNotSealed();
+ try {
+ registerInternal(condition, declaringClass.getDeclaredField(fieldName));
+ } catch (NoSuchFieldException e) {
+ registerConditionalConfiguration(condition, () -> negativeFieldLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet()).add(fieldName));
+ }
+ }
+
private void checkNotSealed() {
if (sealed) {
throw UserError.abort("Too late to add classes, methods, and fields for reflective access. Registration must happen in a Feature before the analysis has finished.");
@@ -359,15 +532,20 @@ private void registerTypesForClass(AnalysisType analysisType, Class> clazz) {
registerTypesForGenericSignature(queryGenericInfo(clazz::getGenericInterfaces));
registerTypesForEnclosingMethodInfo(clazz);
- maybeRegisterRecordComponents(clazz);
+ if (!throwMissingRegistrationErrors()) {
+ maybeRegisterRecordComponents(clazz);
+ }
registerTypesForAnnotations(analysisType);
registerTypesForTypeAnnotations(analysisType);
}
private void registerRecordComponents(Class> clazz) {
- Object[] recordComponents = RecordSupport.singleton().getRecordComponents(clazz);
- for (Object recordComponent : recordComponents) {
+ RecordComponent[] recordComponents = clazz.getRecordComponents();
+ if (recordComponents == null) {
+ return;
+ }
+ for (RecordComponent recordComponent : recordComponents) {
registerTypesForRecordComponent(recordComponent);
}
registeredRecordComponents.put(clazz, recordComponents);
@@ -528,9 +706,10 @@ private void registerTypesForGenericSignature(Type type, int dimension) {
}
}
- private void registerTypesForRecordComponent(Object recordComponent) {
- registerTypesForAnnotations((AnnotatedElement) recordComponent);
- registerTypesForTypeAnnotations((AnnotatedElement) recordComponent);
+ private void registerTypesForRecordComponent(RecordComponent recordComponent) {
+ register(ConfigurationCondition.alwaysTrue(), true, recordComponent.getAccessor());
+ registerTypesForAnnotations(recordComponent);
+ registerTypesForTypeAnnotations(recordComponent);
}
private void registerTypesForAnnotations(AnnotatedElement annotatedElement) {
@@ -683,7 +862,9 @@ private static void reportLinkingErrors(Class> clazz, List errors)
protected void afterAnalysis() {
sealed = true;
processedTypes.clear();
- pendingRecordClasses.clear();
+ if (!throwMissingRegistrationErrors()) {
+ pendingRecordClasses.clear();
+ }
}
@Override
@@ -692,6 +873,10 @@ public Map, Set>> getReflectionInnerClasses() {
return Collections.unmodifiableMap(innerClasses);
}
+ public int getEnabledReflectionQueries(Class> clazz) {
+ return enabledQueriesFlags.getOrDefault(clazz, 0);
+ }
+
@Override
public Map getReflectionFields() {
assert sealed;
@@ -780,6 +965,21 @@ public Map getHeapReflectionExecutables() {
return Collections.unmodifiableMap(heapMethods);
}
+ @Override
+ public Map> getNegativeFieldQueries() {
+ return Collections.unmodifiableMap(negativeFieldLookups);
+ }
+
+ @Override
+ public Map> getNegativeMethodQueries() {
+ return Collections.unmodifiableMap(negativeMethodLookups);
+ }
+
+ @Override
+ public Map> getNegativeConstructorQueries() {
+ return Collections.unmodifiableMap(negativeConstructorLookups);
+ }
+
private static final AnnotationValue[] NO_ANNOTATIONS = new AnnotationValue[0];
public AnnotationValue[] getAnnotationData(AnnotatedElement element) {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionHostedSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionHostedSupport.java
index 2ceebd93ae47..fe04ab4c66ab 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionHostedSupport.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionHostedSupport.java
@@ -32,6 +32,7 @@
import com.oracle.graal.pointsto.ObjectScanner.ScanReason;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
+import com.oracle.graal.pointsto.meta.AnalysisType;
public interface ReflectionHostedSupport {
Map, Set>> getReflectionInnerClasses();
@@ -68,6 +69,12 @@ public interface ReflectionHostedSupport {
Map getHeapReflectionExecutables();
+ Map> getNegativeFieldQueries();
+
+ Map> getNegativeMethodQueries();
+
+ Map> getNegativeConstructorQueries();
+
int getReflectionMethodsCount();
int getReflectionFieldsCount();
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionMetadata.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionMetadata.java
index 3e0e1a13b4f1..40c132d6458d 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionMetadata.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionMetadata.java
@@ -50,30 +50,36 @@ static class ClassMetadata extends AnnotatedElementMetadata {
final Object enclosingMethodInfo;
final RecordComponentMetadata[] recordComponents;
final HostedType[] permittedSubclasses;
- final int classAccessFlags;
+ final HostedType[] nestMembers;
+ final JavaConstant[] signers;
+ final int flags;
- ClassMetadata(HostedType[] classes, Object enclosingMethodInfo, RecordComponentMetadata[] recordComponents, HostedType[] permittedSubclasses, int classAccessFlags,
- AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations) {
+ ClassMetadata(HostedType[] classes, Object enclosingMethodInfo, RecordComponentMetadata[] recordComponents, HostedType[] permittedSubclasses, HostedType[] nestMembers, JavaConstant[] signers,
+ int flags, AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations) {
super(annotations, typeAnnotations);
this.classes = classes;
this.enclosingMethodInfo = enclosingMethodInfo;
this.recordComponents = recordComponents;
this.permittedSubclasses = permittedSubclasses;
- this.classAccessFlags = classAccessFlags;
+ this.nestMembers = nestMembers;
+ this.signers = signers;
+ this.flags = flags;
}
}
static class AccessibleObjectMetadata extends AnnotatedElementMetadata {
final boolean complete;
+ final boolean negative;
final JavaConstant heapObject;
final HostedType declaringType;
final int modifiers;
final String signature;
- AccessibleObjectMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, int modifiers, String signature, AnnotationValue[] annotations,
+ AccessibleObjectMetadata(boolean complete, boolean negative, JavaConstant heapObject, HostedType declaringType, int modifiers, String signature, AnnotationValue[] annotations,
TypeAnnotationValue[] typeAnnotations) {
super(annotations, typeAnnotations);
this.complete = complete;
+ this.negative = negative;
this.heapObject = heapObject;
this.declaringType = declaringType;
this.modifiers = modifiers;
@@ -89,9 +95,9 @@ static class FieldMetadata extends AccessibleObjectMetadata {
final int offset;
final String deletedReason;
- private FieldMetadata(boolean complete, boolean hiding, JavaConstant heapObject, HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature,
- AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations, int offset, String deletedReason) {
- super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations);
+ private FieldMetadata(boolean complete, boolean negative, boolean hiding, JavaConstant heapObject, HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal,
+ String signature, AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations, int offset, String deletedReason) {
+ super(complete, negative, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations);
this.hiding = hiding;
this.name = name;
this.type = type;
@@ -103,22 +109,22 @@ private FieldMetadata(boolean complete, boolean hiding, JavaConstant heapObject,
/* Field registered for reflection */
FieldMetadata(HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, AnnotationValue[] annotations,
TypeAnnotationValue[] typeAnnotations, int offset, String deletedReason) {
- this(true, false, null, declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason);
+ this(true, false, false, null, declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason);
}
/* Field in heap */
FieldMetadata(boolean registered, JavaConstant heapObject, AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations) {
- this(registered, false, heapObject, null, null, null, 0, false, null, annotations, typeAnnotations, LOC_UNINITIALIZED, null);
+ this(registered, false, false, heapObject, null, null, null, 0, false, null, annotations, typeAnnotations, LOC_UNINITIALIZED, null);
}
/* Hiding field */
FieldMetadata(HostedType declaringType, String name, HostedType type, int modifiers) {
- this(false, true, null, declaringType, name, type, modifiers, false, null, null, null, LOC_UNINITIALIZED, null);
+ this(false, false, true, null, declaringType, name, type, modifiers, false, null, null, null, LOC_UNINITIALIZED, null);
}
- /* Reachable field */
- FieldMetadata(HostedType declaringType, String name) {
- this(false, false, null, declaringType, name, null, 0, false, null, null, null, LOC_UNINITIALIZED, null);
+ /* Reachable or negative query field */
+ FieldMetadata(HostedType declaringType, String name, boolean negative) {
+ this(false, negative, false, null, declaringType, name, null, 0, false, null, null, null, LOC_UNINITIALIZED, null);
}
}
@@ -129,10 +135,10 @@ static class ExecutableMetadata extends AccessibleObjectMetadata {
final ReflectParameterMetadata[] reflectParameters;
final JavaConstant accessor;
- ExecutableMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, Object[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature,
+ ExecutableMetadata(boolean complete, boolean negative, JavaConstant heapObject, HostedType declaringType, Object[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature,
AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters,
JavaConstant accessor) {
- super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations);
+ super(complete, negative, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations);
this.parameterTypes = parameterTypes;
this.exceptionTypes = exceptionTypes;
this.parameterAnnotations = parameterAnnotations;
@@ -147,10 +153,11 @@ static class MethodMetadata extends ExecutableMetadata {
final HostedType returnType;
final AnnotationMemberValue annotationDefault;
- private MethodMetadata(boolean complete, boolean hiding, JavaConstant heapObject, HostedType declaringClass, String name, Object[] parameterTypes, int modifiers, HostedType returnType,
- HostedType[] exceptionTypes, String signature, AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, AnnotationMemberValue annotationDefault,
- TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) {
- super(complete, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor);
+ private MethodMetadata(boolean complete, boolean negative, boolean hiding, JavaConstant heapObject, HostedType declaringClass, String name, Object[] parameterTypes, int modifiers,
+ HostedType returnType, HostedType[] exceptionTypes, String signature, AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations,
+ AnnotationMemberValue annotationDefault, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) {
+ super(complete, negative, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters,
+ accessor);
this.hiding = hiding;
this.name = name;
this.returnType = returnType;
@@ -161,50 +168,61 @@ private MethodMetadata(boolean complete, boolean hiding, JavaConstant heapObject
MethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, HostedType[] exceptionTypes, String signature,
AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, AnnotationMemberValue annotationDefault, TypeAnnotationValue[] typeAnnotations,
ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) {
- this(true, false, null, declaringClass, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, parameterAnnotations, annotationDefault, typeAnnotations,
- reflectParameters, accessor);
+ this(true, false, false, null, declaringClass, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, parameterAnnotations, annotationDefault,
+ typeAnnotations, reflectParameters, accessor);
}
/* Method in heap */
MethodMetadata(boolean registered, JavaConstant heapObject, AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, AnnotationMemberValue annotationDefault,
TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters) {
- this(registered, false, heapObject, null, null, null, 0, null, null, null, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, null);
+ this(registered, false, false, heapObject, null, null, null, 0, null, null, null, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, null);
}
/* Hiding method */
MethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType) {
- this(false, true, null, declaringClass, name, parameterTypes, modifiers, returnType, null, null, null, null, null, null, null, null);
+ this(false, false, true, null, declaringClass, name, parameterTypes, modifiers, returnType, null, null, null, null, null, null, null, null);
}
/* Reachable method */
MethodMetadata(HostedType declaringClass, String name, String[] parameterTypeNames) {
- this(false, false, null, declaringClass, name, parameterTypeNames, 0, null, null, null, null, null, null, null, null, null);
+ this(false, false, false, null, declaringClass, name, parameterTypeNames, 0, null, null, null, null, null, null, null, null, null);
+ }
+
+ /* Negative query method */
+ MethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes) {
+ this(false, true, false, null, declaringClass, name, parameterTypes, 0, null, null, null, null, null, null, null, null, null);
}
}
static class ConstructorMetadata extends ExecutableMetadata {
- private ConstructorMetadata(boolean complete, JavaConstant heapObject, HostedType declaringClass, Object[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature,
- AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters,
+ private ConstructorMetadata(boolean complete, boolean negative, JavaConstant heapObject, HostedType declaringClass, Object[] parameterTypes, int modifiers, HostedType[] exceptionTypes,
+ String signature, AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters,
JavaConstant accessor) {
- super(complete, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor);
+ super(complete, negative, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters,
+ accessor);
}
/* Constructor registered for reflection */
ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, AnnotationValue[] annotations,
AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) {
- this(true, null, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor);
+ this(true, false, null, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor);
}
/* Constructor in heap */
ConstructorMetadata(boolean registered, JavaConstant heapObject, AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations,
ReflectParameterMetadata[] reflectParameters) {
- this(registered, heapObject, null, null, 0, null, null, annotations, parameterAnnotations, typeAnnotations, reflectParameters, null);
+ this(registered, false, heapObject, null, null, 0, null, null, annotations, parameterAnnotations, typeAnnotations, reflectParameters, null);
}
/* Reachable constructor */
ConstructorMetadata(HostedType declaringClass, String[] parameterTypeNames) {
- this(false, null, declaringClass, parameterTypeNames, 0, null, null, null, null, null, null, null);
+ this(false, false, null, declaringClass, parameterTypeNames, 0, null, null, null, null, null, null, null);
+ }
+
+ /* Negative query constructor */
+ ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes) {
+ this(false, true, null, declaringClass, parameterTypes, 0, null, null, null, null, null, null, null);
}
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionMetadataEncoderImpl.java
index 1e958c21cb01..9d7dab52faca 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionMetadataEncoderImpl.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionMetadataEncoderImpl.java
@@ -26,10 +26,13 @@
import static com.oracle.svm.core.reflect.ReflectionMetadataDecoder.NO_DATA;
import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_FLAGS_MASK;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_NEST_MEMBERS_FLAG;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_PERMITTED_SUBCLASSES_FLAG;
import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.COMPLETE_FLAG_MASK;
import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.FIRST_ERROR_INDEX;
import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.HIDING_FLAG_MASK;
import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.IN_HEAP_FLAG_MASK;
+import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.NEGATIVE_FLAG_MASK;
import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.NULL_OBJECT;
import static com.oracle.svm.hosted.reflect.ReflectionMetadata.AccessibleObjectMetadata;
import static com.oracle.svm.hosted.reflect.ReflectionMetadata.ClassMetadata;
@@ -60,6 +63,7 @@
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import org.graalvm.collections.Pair;
import org.graalvm.compiler.core.common.util.TypeConversion;
import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
@@ -252,7 +256,12 @@ public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Cla
Object enclosingMethodInfo = getEnclosingMethodInfo(javaClass);
RecordComponentMetadata[] recordComponents = getRecordComponents(metaAccess, type, javaClass);
Class>[] permittedSubclasses = getPermittedSubclasses(metaAccess, javaClass);
+ Class>[] nestMembers = getNestMembers(metaAccess, javaClass);
+ Object[] signers = javaClass.getSigners();
int classAccessFlags = Reflection.getClassAccessFlags(javaClass);
+ int enabledQueries = dataBuilder.getEnabledReflectionQueries(javaClass);
+ VMError.guarantee((classAccessFlags & enabledQueries) == 0);
+ int flags = classAccessFlags | enabledQueries;
/* Register string and class values in annotations */
encoders.sourceClasses.addObject(javaClass);
@@ -263,11 +272,20 @@ public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Cla
}
HostedType[] innerTypes = registerClassValues(metaAccess, innerClasses);
HostedType[] permittedSubtypes = (permittedSubclasses != null) ? registerClassValues(metaAccess, permittedSubclasses) : null;
+ HostedType[] nestMemberTypes = (nestMembers != null) ? registerClassValues(metaAccess, nestMembers) : null;
+ JavaConstant[] signerConstants = null;
+ if (signers != null) {
+ signerConstants = new JavaConstant[signers.length];
+ for (int i = 0; i < signers.length; ++i) {
+ signerConstants[i] = SubstrateObjectConstant.forObject(signers[i]);
+ encoders.objectConstants.addObject(signerConstants[i]);
+ }
+ }
AnalysisType analysisType = type.getWrapped();
AnnotationValue[] annotations = registerAnnotationValues(analysisType);
TypeAnnotationValue[] typeAnnotations = registerTypeAnnotationValues(analysisType);
- registerClass(type, new ClassMetadata(innerTypes, enclosingMethodInfo, recordComponents, permittedSubtypes, classAccessFlags, annotations, typeAnnotations));
+ registerClass(type, new ClassMetadata(innerTypes, enclosingMethodInfo, recordComponents, permittedSubtypes, nestMemberTypes, signerConstants, flags, annotations, typeAnnotations));
}
private void registerError(Throwable error) {
@@ -300,32 +318,41 @@ private void registerEnclosingMethodInfo(Object[] enclosingMethodInfo) {
private static final Method getPermittedSubclasses = ReflectionUtil.lookupMethod(true, Class.class, "getPermittedSubclasses");
- private static Class>[] getPermittedSubclasses(MetaAccessProvider metaAccess, Class> clazz) {
- if (JavaVersionUtil.JAVA_SPEC < 17) {
+ private Class>[] getPermittedSubclasses(MetaAccessProvider metaAccess, Class> clazz) {
+ if (JavaVersionUtil.JAVA_SPEC < 17 || (dataBuilder.getEnabledReflectionQueries(clazz) & ALL_PERMITTED_SUBCLASSES_FLAG) == 0) {
return null;
}
try {
Class>[] permittedSubclasses = (Class>[]) getPermittedSubclasses.invoke(clazz);
- if (permittedSubclasses == null) {
- return null;
- }
- Set> reachablePermittedSubclasses = new HashSet<>();
- for (Class> permittedSubclass : permittedSubclasses) {
- try {
- HostedType hostedType = ((HostedMetaAccess) metaAccess).optionalLookupJavaType(permittedSubclass).orElse(null);
- if (hostedType != null && hostedType.getWrapped().isReachable()) {
- reachablePermittedSubclasses.add(permittedSubclass);
- }
- } catch (DeletedElementException dee) {
- // permitted subclass has been deleted -> ignore
- }
- }
- return reachablePermittedSubclasses.toArray(new Class>[0]);
+ return filterDeletedClasses(metaAccess, permittedSubclasses);
} catch (IllegalAccessException | InvocationTargetException e) {
throw VMError.shouldNotReachHere(e);
}
}
+ private Class>[] getNestMembers(MetaAccessProvider metaAccess, Class> clazz) {
+ if (JavaVersionUtil.JAVA_SPEC < 17 || (dataBuilder.getEnabledReflectionQueries(clazz) & ALL_NEST_MEMBERS_FLAG) == 0) {
+ return null;
+ }
+ return filterDeletedClasses(metaAccess, clazz.getNestMembers());
+ }
+
+ private static Class>[] filterDeletedClasses(MetaAccessProvider metaAccess, Class>[] classes) {
+ if (classes == null) {
+ return null;
+ }
+ Set> reachableClasses = new HashSet<>();
+ for (Class> clazz : classes) {
+ try {
+ metaAccess.lookupJavaType(clazz);
+ reachableClasses.add(clazz);
+ } catch (DeletedElementException dee) {
+ // class has been deleted -> ignore
+ }
+ }
+ return reachableClasses.toArray(new Class>[0]);
+ }
+
@Override
public void addReflectionFieldMetadata(MetaAccessProvider metaAccess, HostedField hostedField, Field reflectField) {
HostedType declaringType = hostedField.getDeclaringClass();
@@ -545,7 +572,7 @@ public void addReachableFieldMetadata(HostedField field) {
/* Fill encoders with the necessary values. */
encoders.sourceMethodNames.addObject(name);
- registerField(declaringType, field, new FieldMetadata(declaringType, name));
+ registerField(declaringType, field, new FieldMetadata(declaringType, name, false));
}
@Override
@@ -570,6 +597,29 @@ public void addReachableExecutableMetadata(HostedMethod executable) {
}
}
+ @Override
+ public void addNegativeFieldQueryMetadata(HostedType declaringClass, String fieldName) {
+ encoders.sourceMethodNames.addObject(fieldName);
+ registerField(declaringClass, fieldName, new FieldMetadata(declaringClass, fieldName, true));
+ }
+
+ @Override
+ public void addNegativeMethodQueryMetadata(HostedType declaringClass, String methodName, HostedType[] parameterTypes) {
+ encoders.sourceMethodNames.addObject(methodName);
+ for (HostedType parameterType : parameterTypes) {
+ encoders.sourceClasses.addObject(parameterType.getJavaClass());
+ }
+ registerMethod(declaringClass, Pair.create(methodName, parameterTypes), new MethodMetadata(declaringClass, methodName, parameterTypes));
+ }
+
+ @Override
+ public void addNegativeConstructorQueryMetadata(HostedType declaringClass, HostedType[] parameterTypes) {
+ for (HostedType parameterType : parameterTypes) {
+ encoders.sourceClasses.addObject(parameterType.getJavaClass());
+ }
+ registerConstructor(declaringClass, parameterTypes, new ConstructorMetadata(declaringClass, parameterTypes));
+ }
+
private static HostedType[] getParameterTypes(HostedMethod method) {
HostedType[] parameterTypes = new HostedType[method.getSignature().getParameterCount(false)];
for (int i = 0; i < parameterTypes.length; ++i) {
@@ -689,17 +739,19 @@ public void encodeAllAndInstall() {
int typeAnnotationsIndex = addEncodedElement(buf, encodeTypeAnnotations(classMetadata.typeAnnotations));
int classesEncodingIndex = encodeAndAddCollection(buf, classMetadata.classes, this::encodeType, false);
int permittedSubclassesIndex = JavaVersionUtil.JAVA_SPEC >= 17 ? encodeAndAddCollection(buf, classMetadata.permittedSubclasses, this::encodeType, true) : NO_DATA;
- if (anySet(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesIndex)) {
- hub.setHubMetadata(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesIndex);
+ int nestMembersEncodingIndex = encodeAndAddCollection(buf, classMetadata.nestMembers, this::encodeType, true);
+ int signersEncodingIndex = encodeAndAddCollection(buf, classMetadata.signers, this::encodeObject, true);
+ if (anySet(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesIndex, nestMembersEncodingIndex, signersEncodingIndex)) {
+ hub.setHubMetadata(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesIndex, nestMembersEncodingIndex, signersEncodingIndex);
}
int fieldsIndex = encodeAndAddCollection(buf, getFields(declaringType), this::encodeField, false);
int methodsIndex = encodeAndAddCollection(buf, getMethods(declaringType), this::encodeExecutable, false);
int constructorsIndex = encodeAndAddCollection(buf, getConstructors(declaringType), this::encodeExecutable, false);
int recordComponentsIndex = JavaVersionUtil.JAVA_SPEC >= 17 ? encodeAndAddCollection(buf, classMetadata.recordComponents, this::encodeRecordComponent, true) : NO_DATA;
- int classAccessFlags = classMetadata.classAccessFlags;
- if (anySet(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex) || classAccessFlags != hub.getModifiers()) {
- hub.setReflectionMetadata(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex, classAccessFlags);
+ int classFlags = classMetadata.flags;
+ if (anySet(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex) || classFlags != hub.getModifiers()) {
+ hub.setReflectionMetadata(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex, classFlags);
}
}
for (AccessibleObjectMetadata metadata : heapData) {
@@ -781,6 +833,7 @@ private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) {
modifiers |= field.complete ? COMPLETE_FLAG_MASK : 0;
modifiers |= field.heapObject != null ? IN_HEAP_FLAG_MASK : 0;
modifiers |= field.hiding ? HIDING_FLAG_MASK : 0;
+ modifiers |= field.negative ? NEGATIVE_FLAG_MASK : 0;
buf.putUV(modifiers);
if (field.heapObject != null) {
encodeObject(buf, field.heapObject);
@@ -811,6 +864,7 @@ private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata exec
modifiers |= executable.complete ? COMPLETE_FLAG_MASK : 0;
modifiers |= executable.heapObject != null ? IN_HEAP_FLAG_MASK : 0;
modifiers |= isHiding ? HIDING_FLAG_MASK : 0;
+ modifiers |= executable.negative ? NEGATIVE_FLAG_MASK : 0;
buf.putUV(modifiers);
if (executable.heapObject != null) {
encodeObject(buf, executable.heapObject);
@@ -818,7 +872,7 @@ private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata exec
if (isMethod) {
encodeName(buf, ((MethodMetadata) executable).name);
}
- if (executable.complete || isHiding) {
+ if (executable.complete || isHiding || executable.negative) {
encodeArray(buf, (HostedType[]) executable.parameterTypes, parameterType -> encodeType(buf, parameterType));
} else {
encodeArray(buf, (String[]) executable.parameterTypes, parameterTypeName -> encodeName(buf, parameterTypeName));