diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java index c4d0f95765ea..6b4e7aeaacbe 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java @@ -56,9 +56,11 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry { Object getAccessor(Executable method); /* - * Returns the methods that shadow a superclass method registered for reflection, to be excluded - * from reflection queries. + * Returns the methods and fields that shadow a superclass element registered for reflection, to + * be excluded from reflection queries. */ + Set getHidingReflectionFields(); + Set getHidingReflectionMethods(); Object[] getRecordComponents(Class type); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index 1e87e39325f1..eddb7c13978f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -39,6 +39,7 @@ import java.lang.reflect.Field; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; @@ -49,6 +50,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; @@ -899,8 +901,20 @@ public T[] getAnnotationsByType(Class annotationClass) @KeepOriginal private native Constructor[] getConstructors(); - @KeepOriginal - private native Field getField(@SuppressWarnings("hiding") String name) throws NoSuchMethodException; + @Substitute + public Field getField(String fieldName) throws NoSuchFieldException, SecurityException { + Objects.requireNonNull(fieldName); + @SuppressWarnings("removal") + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true); + } + Field field = getField0(fieldName); + if (field == null || ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHiding(field.getModifiers())) { + throw new NoSuchFieldException(fieldName); + } + return getReflectionFactory().copyField(field); + } @KeepOriginal private native Method getMethod(@SuppressWarnings("hiding") String name, Class... parameterTypes) throws NoSuchMethodException; @@ -923,8 +937,23 @@ public T[] getAnnotationsByType(Class annotationClass) @KeepOriginal private native Constructor[] getDeclaredConstructors(); - @KeepOriginal - private native Field getDeclaredField(@SuppressWarnings("hiding") String name); + /** + * @see #filterHidingFields(Field...) + */ + @Substitute + public Field getDeclaredField(String fieldName) throws NoSuchFieldException, SecurityException { + Objects.requireNonNull(fieldName); + @SuppressWarnings("removal") + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true); + } + Field field = searchFields(privateGetDeclaredFields(false), fieldName); + if (field == null || ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHiding(field.getModifiers())) { + throw new NoSuchFieldException(fieldName); + } + return getReflectionFactory().copyField(field); + } @KeepOriginal private native Method getDeclaredMethod(@SuppressWarnings("hiding") String name, Class... parameterTypes); @@ -1009,8 +1038,19 @@ private static Method searchMethods(Method[] allMethods, String name, Class[] @KeepOriginal private static native boolean arrayContentsEq(Object[] a1, Object[] a2); - @KeepOriginal - private static native Field[] copyFields(Field[] arg); + /** + * @see #filterHidingFields(Field...) + */ + @Substitute + private static Field[] copyFields(Field[] original) { + Field[] arg = filterHidingFields(original); + Field[] out = new Field[arg.length]; + ReflectionFactory fact = getReflectionFactory(); + for (int i = 0; i < arg.length; i++) { + out[i] = fact.copyField(arg[i]); + } + return out; + } /** * @see #filterHidingMethods(Method...) @@ -1463,14 +1503,24 @@ private Class[] getPermittedSubclasses0() { private native boolean isDirectSubType(Class c); /* - * We need to filter out hiding methods at the last moment. This ensures that the JDK internals - * see them as regular methods and ensure the visibility of methods is correct, but they should - * not be returned to application code. + * We need to filter out hiding elements at the last moment. This ensures that the JDK internals + * see them as regular methods and fields and ensure their visibility is correct, but they + * should not be returned to application code. */ + private static Field[] filterHidingFields(Field... fields) { + List filtered = new ArrayList<>(); + for (Field field : fields) { + if (!ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHiding(field.getModifiers())) { + filtered.add(field); + } + } + return filtered.toArray(new Field[0]); + } + private static Method[] filterHidingMethods(Method... methods) { List filtered = new ArrayList<>(); for (Method method : methods) { - if (!ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHidingMethod(method.getModifiers())) { + if (!ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHiding(method.getModifiers())) { filtered.add(method); } } @@ -1687,7 +1737,7 @@ Method getMostSpecific() { } } /* Filter out hiding methods after the retursive lookup is done */ - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHidingMethod(m.getModifiers()) ? null : m; + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHiding(m.getModifiers()) ? null : m; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java index 8a8b5c1ee75d..538bfc4fe0b4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java @@ -51,7 +51,7 @@ public interface ReflectionMetadataDecoder { byte[] parseByteArray(int index); - boolean isHidingMethod(int modifiers); + boolean isHiding(int modifiers); long getMetadataByteLength(); } 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 9fcc81289c1a..3fbb818c8eb2 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 @@ -27,7 +27,6 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -60,6 +59,7 @@ import org.graalvm.word.WordFactory; import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.objectfile.ObjectFile; import com.oracle.svm.core.SubstrateOptions; @@ -255,17 +255,26 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code Set includedFields = new HashSet<>(); Set includedMethods = new HashSet<>(); + Set configurationFields = reflectionSupport.getReflectionFields(); + Set configurationExecutables = reflectionSupport.getReflectionExecutables(); for (AccessibleObject object : reflectionSupport.getHeapReflectionObjects()) { if (object instanceof Field) { - includedFields.add(hMetaAccess.lookupJavaField((Field) object)); - } else if (object instanceof Method || object instanceof Constructor) { - includedMethods.add(hMetaAccess.lookupJavaMethod((Executable) object)); + HostedField hostedField = hMetaAccess.lookupJavaField((Field) object); + if (!includedFields.contains(hostedField)) { + reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, object, configurationFields.contains(object)); + includedFields.add(hostedField); + } + } else if (object instanceof Executable) { + HostedMethod hostedMethod = hMetaAccess.lookupJavaMethod((Executable) object); + if (!includedMethods.contains(hostedMethod)) { + reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, object, configurationExecutables.contains(object)); + includedMethods.add(hostedMethod); + } } - reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, object); } - for (Field reflectField : reflectionSupport.getReflectionFields()) { + for (Field reflectField : configurationFields) { HostedField field = hMetaAccess.lookupJavaField(reflectField); if (!includedFields.contains(field)) { reflectionMetadataEncoder.addReflectionFieldMetadata(hMetaAccess, field, reflectField); @@ -273,7 +282,7 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code } } - for (Executable reflectMethod : reflectionSupport.getReflectionExecutables()) { + for (Executable reflectMethod : configurationExecutables) { HostedMethod method = hMetaAccess.lookupJavaMethod(reflectMethod); if (!includedMethods.contains(method)) { Object accessor = reflectionSupport.getAccessor(reflectMethod); @@ -282,6 +291,21 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code } } + 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); + } + } + } + for (Object method : reflectionSupport.getHidingReflectionMethods()) { AnalysisMethod hidingMethod = (AnalysisMethod) method; HostedMethod hostedMethod = hUniverse.optionalLookup(hidingMethod); @@ -622,7 +646,9 @@ public interface ReflectionMetadataEncoder { void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, HostedMethod sharedMethod, Executable reflectMethod, Object accessor); - void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object); + void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object, boolean registered); + + void addHidingFieldMetadata(AnalysisField analysisField, HostedType declType, String name, HostedType type, int modifiers); void addHidingMethodMetadata(AnalysisMethod analysisMethod, HostedType declType, String name, HostedType[] paramTypes, int modifiers, HostedType returnType); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java index 84942e4bbbf6..dfccce7b4c34 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java @@ -399,7 +399,10 @@ public T[] getAnnotationsByType(Class annotationClass) @Override public T getDeclaredAnnotation(Class annotationClass) { - return original.getDeclaredAnnotation(annotationClass); + if (annotationClass == LambdaFormHiddenMethod.class) { + return annotationClass.cast(LambdaFormHiddenMethod.Holder.INSTANCE); + } + return null; } @Override @@ -409,7 +412,7 @@ public T[] getDeclaredAnnotationsByType(Class annotati @Override public Annotation[] getDeclaredAnnotations() { - return original.getDeclaredAnnotations(); + return LambdaFormHiddenMethod.Holder.ARRAY; } public ResolvedJavaType getOriginal() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java index 6f90109e6d9e..51b0eb40393f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java @@ -33,6 +33,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.annotate.Delete; +import com.oracle.svm.core.annotate.InjectAccessors; import com.oracle.svm.core.annotate.TargetClass; import jdk.vm.ci.meta.ResolvedJavaType; @@ -86,7 +87,7 @@ public static boolean shouldExclude(Field field, AnalysisMetaAccess metaAccess, if (!universe.hostVM().platformSupported(aField)) { return true; } - if (aField.isAnnotationPresent(Delete.class)) { + if (aField.isAnnotationPresent(Delete.class) || aField.isAnnotationPresent(InjectAccessors.class)) { return true; // accesses would fail at runtime } } catch (UnsupportedFeatureException ignored) { diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java index b8db6b229a7d..ed1026c77117 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.reflect.hosted; +import static com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl.getAnnotationEncodingType; import static com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl.getTypeAnnotations; import java.lang.annotation.Annotation; @@ -35,6 +36,7 @@ import java.lang.reflect.GenericArrayType; import java.lang.reflect.InaccessibleObjectException; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.MalformedParameterizedTypeException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; @@ -79,6 +81,7 @@ import com.oracle.svm.util.ModuleSupport; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import sun.reflect.annotation.AnnotationType; @@ -96,6 +99,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl private final Map reflectionMethods = new ConcurrentHashMap<>(); private final Map methodAccessors = new ConcurrentHashMap<>(); private final Set reflectionFields = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Set hidingFields = ConcurrentHashMap.newKeySet(); private final Set hidingMethods = ConcurrentHashMap.newKeySet(); private final Set registeredMethods = ConcurrentHashMap.newKeySet(); private final Set registeredFields = ConcurrentHashMap.newKeySet(); @@ -107,6 +111,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl private final Set> processedClasses = new HashSet<>(); private final Set processedTypes = new HashSet<>(); private final Set processedDynamicHubs = new HashSet<>(); + private final Map> processedHidingFields = new HashMap<>(); private final Map> processedHidingMethods = new HashMap<>(); private final Set processedHeapReflectionObjects = new HashSet<>(); @@ -266,6 +271,7 @@ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { if (!registeredFields.contains(reflectField) && !SubstitutionReflectivityFilter.shouldExclude(reflectField, access.getMetaAccess(), access.getUniverse())) { AnalysisField analysisField = access.getMetaAccess().lookupJavaField(reflectField); registerTypesForField(access, analysisField, reflectField); + registerHidingSubTypeFields(access, analysisField, analysisField.getDeclaringClass()); registeredFields.add(reflectField); } } @@ -291,13 +297,18 @@ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { if (!processedHeapReflectionObjects.contains(object)) { if (object instanceof Field) { Field field = (Field) object; - AnalysisField analysisField = access.getMetaAccess().lookupJavaField(field); - registerTypesForField(access, analysisField, field); + if (!SubstitutionReflectivityFilter.shouldExclude(field, access.getMetaAccess(), access.getUniverse())) { + AnalysisField analysisField = access.getMetaAccess().lookupJavaField(field); + registerTypesForField(access, analysisField, field); + registerHidingSubTypeFields(access, analysisField, analysisField.getDeclaringClass()); + } } else if (object instanceof Executable) { Executable executable = (Executable) object; - AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(executable); - registerTypesForMethod(access, analysisMethod, executable); - registerHidingSubTypeMethods(access, analysisMethod, analysisMethod.getDeclaringClass()); + if (!SubstitutionReflectivityFilter.shouldExclude(executable, access.getMetaAccess(), access.getUniverse())) { + AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(executable); + registerTypesForMethod(access, analysisMethod, executable); + registerHidingSubTypeMethods(access, analysisMethod, analysisMethod.getDeclaringClass()); + } } processedHeapReflectionObjects.add(object); } @@ -316,6 +327,38 @@ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { } } + private void registerHidingSubTypeFields(DuringAnalysisAccess access, AnalysisField field, AnalysisType type) { + if (!type.equals(field.getDeclaringClass()) && type.isReachable()) { + if (!processedHidingFields.containsKey(field) || !processedHidingFields.get(field).contains(type)) { + processedHidingFields.computeIfAbsent(field, m -> ConcurrentHashMap.newKeySet()).add(type); + try { + AnalysisField[] subClassFields = field.isStatic() ? type.getStaticFields() : type.getInstanceFields(false); + for (AnalysisField subclassField : subClassFields) { + if (subclassField.getName().equals(field.getName())) { + hidingFields.add(subclassField); + } + } + /* + * Lookup can lead to the creation of new AnalysisField objects, so we need to + * run another analysis iteration. + */ + access.requireAnalysisIteration(); + + } catch (UnsupportedFeatureException | LinkageError e) { + /* + * A field that is not supposed to end up in the image is considered as being + * absent for reflection purposes. + */ + } + } + } + for (AnalysisType subType : type.getSubTypes()) { + if (!subType.equals(type)) { + registerHidingSubTypeFields(access, field, subType); + } + } + } + private void registerHidingSubTypeMethods(DuringAnalysisAccess access, AnalysisMethod method, AnalysisType type) { if (!type.equals(method.getDeclaringClass()) && type.isReachable()) { if (!processedHidingMethods.containsKey(method) || !processedHidingMethods.get(method).contains(type)) { @@ -376,7 +419,7 @@ private void registerTypesForClass(DuringAnalysisAccessImpl access, AnalysisType Executable enclosingMethod = enclosingMethodOrConstructor(clazz, errors); if (enclosingMethod != null) { makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(enclosingMethod.getDeclaringClass())); - RuntimeReflection.register(enclosingMethod); + RuntimeReflection.registerAsQueried(enclosingMethod); } reportLinkingErrors(clazz, errors); @@ -459,6 +502,12 @@ private void registerTypesForMethod(DuringAnalysisAccessImpl access, AnalysisMet registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); // Checkstyle: disallow direct annotation access } + if (reflectMethod instanceof Method) { + Object defaultValue = ((Method) reflectMethod).getDefaultValue(); + if (defaultValue != null) { + registerTypesForAnnotationValue(access, getAnnotationEncodingType(defaultValue), defaultValue); + } + } } private static void registerTypesForReachableField(DuringAnalysisAccessImpl access, AnalysisField analysisField) { @@ -470,7 +519,6 @@ private static void registerTypesForReachableMethod(DuringAnalysisAccessImpl acc for (JavaType paramType : analysisMethod.toParameterTypes()) { makeAnalysisTypeReachable(access, (AnalysisType) paramType); } - makeAnalysisTypeReachable(access, (AnalysisType) analysisMethod.getSignature().getReturnType(null)); } private void makeTypeReachable(DuringAnalysisAccessImpl access, Type type) { @@ -655,7 +703,7 @@ private void processClass(DuringAnalysisAccessImpl access, Class clazz) { private static T query(Callable callable, List errors) { try { return callable.call(); - } catch (TypeNotPresentException | LinkageError e) { + } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError e) { errors.add(e); } catch (Exception e) { throw VMError.shouldNotReachHere(e); @@ -710,7 +758,7 @@ public boolean requiresProcessing() { return !modifiedClasses.isEmpty(); } - private Executable enclosingMethodOrConstructor(Class clazz, List errors) { + private static Executable enclosingMethodOrConstructor(Class clazz, List errors) { Method enclosingMethod; Constructor enclosingConstructor; try { @@ -746,13 +794,7 @@ private Executable enclosingMethodOrConstructor(Class clazz, List throw VMError.shouldNotReachHere("Class has both an enclosingMethod and an enclosingConstructor: " + clazz + ", " + enclosingMethod + ", " + enclosingConstructor); } - Executable enclosingMethodOrConstructor = enclosingMethod != null ? enclosingMethod : enclosingConstructor; - - if (reflectionMethods.containsKey(enclosingMethodOrConstructor)) { - return enclosingMethodOrConstructor; - } else { - return null; - } + return enclosingMethod != null ? enclosingMethod : enclosingConstructor; } @Override @@ -779,6 +821,12 @@ public Object getAccessor(Executable method) { return methodAccessors.get(method); } + @Override + public Set getHidingReflectionFields() { + assert sealed; + return Collections.unmodifiableSet(hidingFields); + } + @Override public Set getHidingReflectionMethods() { assert sealed; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java index 6f1d6303d536..cf1075f86abf 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.phases.util.Providers; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CFunctionPointer; +import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import com.oracle.graal.pointsto.meta.AnalysisMethod; @@ -178,6 +179,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { analysisAccess = (FeatureImpl.BeforeAnalysisAccessImpl) access; /* duplicated to reduce the number of analysis iterations */ reflectionData.flushConditionalConfiguration(access); + /* Make sure array classes don't need to be registered for reflection. */ + RuntimeReflection.register(Object[].class.getMethods()); } @Override diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java index 5c72a4163b17..3d3a7fed1a79 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java @@ -97,15 +97,17 @@ static class AccessibleObjectMetadata extends AnnotatedElementMetadata { } static class FieldMetadata extends AccessibleObjectMetadata { + final boolean hiding; final String name; final HostedType type; final boolean trustedFinal; final int offset; final String deletedReason; - FieldMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, + private FieldMetadata(boolean complete, boolean hiding, JavaConstant heapObject, HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, Annotation[] annotations, TypeAnnotation[] typeAnnotations, int offset, String deletedReason) { super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations); + this.hiding = hiding; this.name = name; this.type = type; this.trustedFinal = trustedFinal; @@ -113,17 +115,25 @@ static class FieldMetadata extends AccessibleObjectMetadata { this.deletedReason = deletedReason; } + /* Field registered for reflection */ FieldMetadata(HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, Annotation[] annotations, TypeAnnotation[] typeAnnotations, int offset, String deletedReason) { - this(true, null, declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason); + this(true, false, null, declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason); + } + + /* Field in heap */ + FieldMetadata(boolean registered, JavaConstant heapObject, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + this(registered, false, heapObject, null, null, null, 0, false, null, annotations, typeAnnotations, LOC_UNINITIALIZED, null); } - FieldMetadata(JavaConstant heapObject, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { - this(true, 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); } + /* Reachable field */ FieldMetadata(HostedType declaringType, String name) { - this(false, null, declaringType, name, null, 0, false, null, null, null, LOC_UNINITIALIZED, null); + this(false, false, null, declaringType, name, null, 0, false, null, null, null, LOC_UNINITIALIZED, null); } } @@ -151,7 +161,7 @@ static class MethodMetadata extends ExecutableMetadata { final HostedType returnType; final Object annotationDefault; - MethodMetadata(boolean complete, boolean hiding, JavaConstant heapObject, HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, + private MethodMetadata(boolean complete, boolean hiding, JavaConstant heapObject, HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, HostedType[] exceptionTypes, String signature, Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { super(complete, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor); @@ -161,6 +171,7 @@ static class MethodMetadata extends ExecutableMetadata { this.annotationDefault = annotationDefault; } + /* Method registered for reflection */ MethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, HostedType[] exceptionTypes, String signature, Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { @@ -169,34 +180,45 @@ static class MethodMetadata extends ExecutableMetadata { accessor); } - MethodMetadata(JavaConstant heapObject, Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, + /* Method in heap */ + MethodMetadata(boolean registered, JavaConstant heapObject, Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters) { - this(true, false, heapObject, null, null, null, 0, null, null, null, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, null); + this(registered, false, heapObject, null, null, null, 0, null, null, null, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, null); } - MethodMetadata(boolean hiding, HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType) { - this(false, hiding, null, declaringClass, name, parameterTypes, modifiers, returnType, null, null, null, null, null, null, null, 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); + } + + /* Reachable method */ + MethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes) { + this(false, false, null, declaringClass, name, parameterTypes, 0, null, null, null, null, null, null, null, null, null); } } static class ConstructorMetadata extends ExecutableMetadata { - ConstructorMetadata(boolean complete, JavaConstant heapObject, HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, + private ConstructorMetadata(boolean complete, JavaConstant heapObject, HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { super(complete, 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, Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { this(true, null, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor); } - ConstructorMetadata(JavaConstant heapObject, Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters) { - this(true, heapObject, null, null, 0, null, null, annotations, parameterAnnotations, typeAnnotations, reflectParameters, null); + /* Constructor in heap */ + ConstructorMetadata(boolean registered, JavaConstant heapObject, Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, + ReflectParameterMetadata[] reflectParameters) { + this(registered, heapObject, null, null, 0, null, null, annotations, parameterAnnotations, typeAnnotations, reflectParameters, null); } - ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes, int modifiers) { - this(false, null, declaringClass, parameterTypes, modifiers, null, null, null, null, null, null, null); + /* Reachable constructor */ + ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes) { + this(false, null, declaringClass, parameterTypes, 0, null, null, null, null, null, null, null); } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java index e3b56aaa9aa9..f7fe80ccaaaa 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java @@ -25,6 +25,7 @@ package com.oracle.svm.reflect.hosted; import static com.oracle.svm.core.reflect.ReflectionMetadataDecoder.NO_DATA; +import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.ALL_FLAGS_MASK; import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.COMPLETE_FLAG_MASK; import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.HIDING_FLAG_MASK; import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.IN_HEAP_FLAG_MASK; @@ -62,6 +63,7 @@ import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.code.CodeInfoEncoder; @@ -400,7 +402,7 @@ private static boolean isTrustedFinal(Field field) { } @Override - public void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object) { + public void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object, boolean registered) { boolean isExecutable = object instanceof Executable; boolean isMethod = object instanceof Method; Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(object); @@ -431,13 +433,13 @@ public void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, Acces AccessibleObjectMetadata metadata; if (isMethod) { - metadata = new MethodMetadata(heapObjectConstant, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters); + metadata = new MethodMetadata(registered, heapObjectConstant, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters); registerMethod((HostedType) metaAccess.lookupJavaType(((Method) object).getDeclaringClass()), holder, (MethodMetadata) metadata); } else if (isExecutable) { - metadata = new ConstructorMetadata(heapObjectConstant, annotations, parameterAnnotations, typeAnnotations, reflectParameters); + metadata = new ConstructorMetadata(registered, heapObjectConstant, annotations, parameterAnnotations, typeAnnotations, reflectParameters); registerConstructor((HostedType) metaAccess.lookupJavaType(((Constructor) object).getDeclaringClass()), holder, (ConstructorMetadata) metadata); } else { - metadata = new FieldMetadata(heapObjectConstant, annotations, typeAnnotations); + metadata = new FieldMetadata(registered, heapObjectConstant, annotations, typeAnnotations); registerField((HostedType) metaAccess.lookupJavaType(((Field) object).getDeclaringClass()), holder, (FieldMetadata) metadata); } heapData.add(metadata); @@ -484,7 +486,7 @@ private HostedType[] registerClassValues(MetaAccessProvider metaAccess, Class private Annotation[] registerAnnotationValues(MetaAccessProvider metaAccess, Annotation... annotations) { Set includedAnnotations = new HashSet<>(); for (Annotation annotation : annotations) { - if (registerAnnotation(metaAccess, annotation)) { + if (annotation != null && registerAnnotation(metaAccess, annotation)) { includedAnnotations.add(annotation); } } @@ -497,7 +499,7 @@ private TypeAnnotation[] registerTypeAnnotationValues(MetaAccessProvider metaAcc // Checkstyle: allow direct annotation access Annotation annotation = typeAnnotation.getAnnotation(); // Checkstyle: disallow direct annotation access - if (registerAnnotation(metaAccess, annotation)) { + if (annotation != null && registerAnnotation(metaAccess, annotation)) { includedTypeAnnotations.add(typeAnnotation); } } @@ -567,6 +569,16 @@ private void registerAnnotationValue(Class type, Object value) { } } + @Override + public void addHidingFieldMetadata(AnalysisField analysisField, HostedType declaringType, String name, HostedType type, int modifiers) { + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + encoders.sourceClasses.addObject(type.getJavaClass()); + + sortedTypes.add(declaringType); + registerField(declaringType, analysisField, new FieldMetadata(declaringType, name, type, modifiers)); + } + @Override public void addHidingMethodMetadata(AnalysisMethod analysisMethod, HostedType declaringType, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType) { /* Fill encoders with the necessary values. */ @@ -577,7 +589,7 @@ public void addHidingMethodMetadata(AnalysisMethod analysisMethod, HostedType de encoders.sourceClasses.addObject(returnType.getJavaClass()); sortedTypes.add(declaringType); - registerMethod(declaringType, analysisMethod, new MethodMetadata(true, declaringType, name, parameterTypes, modifiers, returnType)); + registerMethod(declaringType, analysisMethod, new MethodMetadata(declaringType, name, parameterTypes, modifiers, returnType)); } @Override @@ -597,8 +609,6 @@ public void addReachableExecutableMetadata(HostedMethod executable) { HostedType declaringType = executable.getDeclaringClass(); String name = isMethod ? executable.getName() : null; HostedType[] parameterTypes = getParameterTypes(executable); - HostedType returnType = (HostedType) executable.getSignature().getReturnType(null); - int modifiers = executable.getModifiers(); /* Fill encoders with the necessary values. */ if (isMethod) { @@ -607,12 +617,11 @@ public void addReachableExecutableMetadata(HostedMethod executable) { for (HostedType parameterType : parameterTypes) { encoders.sourceClasses.addObject(parameterType.getJavaClass()); } - encoders.sourceClasses.addObject(returnType.getJavaClass()); if (isMethod) { - registerMethod(declaringType, executable, new MethodMetadata(false, declaringType, name, parameterTypes, modifiers, returnType)); + registerMethod(declaringType, executable, new MethodMetadata(declaringType, name, parameterTypes)); } else { - registerConstructor(declaringType, executable, new ConstructorMetadata(declaringType, parameterTypes, modifiers)); + registerConstructor(declaringType, executable, new ConstructorMetadata(declaringType, parameterTypes)); } } @@ -650,8 +659,9 @@ private RecordComponentMetadata[] getRecordComponents(MetaAccessProvider metaAcc if (recordComponents == null) { return null; } - Set metadata = new HashSet<>(); - for (Object recordComponent : recordComponents) { + RecordComponentMetadata[] metadata = new RecordComponentMetadata[recordComponents.length]; + for (int i = 0; i < recordComponents.length; ++i) { + Object recordComponent = recordComponents[i]; String name = getRecordComponentName(recordComponent); HostedType type = (HostedType) metaAccess.lookupJavaType(getRecordComponentType(recordComponent)); String signature = getRecordComponentSignature(recordComponent); @@ -672,9 +682,9 @@ private RecordComponentMetadata[] getRecordComponents(MetaAccessProvider metaAcc encoders.objectConstants.addObject(accessorConstant); } - metadata.add(new RecordComponentMetadata(declaringType, name, type, signature, accessorConstant, annotations, typeAnnotations)); + metadata[i] = new RecordComponentMetadata(declaringType, name, type, signature, accessorConstant, annotations, typeAnnotations); } - return metadata.toArray(new RecordComponentMetadata[0]); + return metadata; } private static final Class recordComponentClass; @@ -823,17 +833,20 @@ private static boolean anySet(int... indices) { private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) { /* Make sure we do not overwrite actual modifiers with our flags */ - assert (field.modifiers & COMPLETE_FLAG_MASK) == 0 && (field.modifiers & IN_HEAP_FLAG_MASK) == 0; + assert (field.modifiers & ALL_FLAGS_MASK) == 0; int modifiers = field.modifiers; modifiers |= field.complete ? COMPLETE_FLAG_MASK : 0; modifiers |= field.heapObject != null ? IN_HEAP_FLAG_MASK : 0; + modifiers |= field.hiding ? HIDING_FLAG_MASK : 0; buf.putUV(modifiers); if (field.heapObject != null) { encodeObject(buf, field.heapObject); } else { encodeName(buf, field.name); - if (field.complete) { + if (field.complete || field.hiding) { encodeType(buf, field.type); + } + if (field.complete) { if (JavaVersionUtil.JAVA_SPEC >= 17) { buf.putU1(field.trustedFinal ? 1 : 0); } @@ -848,12 +861,13 @@ private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) { private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata executable) { boolean isMethod = executable instanceof MethodMetadata; + boolean isHiding = isMethod && ((MethodMetadata) executable).hiding; /* Make sure we do not overwrite actual modifiers with our flags */ - assert (executable.modifiers & COMPLETE_FLAG_MASK) == 0 && (executable.modifiers & IN_HEAP_FLAG_MASK) == 0 && (executable.modifiers & HIDING_FLAG_MASK) == 0; + assert (executable.modifiers & ALL_FLAGS_MASK) == 0; int modifiers = executable.modifiers; modifiers |= executable.complete ? COMPLETE_FLAG_MASK : 0; modifiers |= executable.heapObject != null ? IN_HEAP_FLAG_MASK : 0; - modifiers |= isMethod && ((MethodMetadata) executable).hiding ? HIDING_FLAG_MASK : 0; + modifiers |= isHiding ? HIDING_FLAG_MASK : 0; buf.putUV(modifiers); if (executable.heapObject != null) { encodeObject(buf, executable.heapObject); @@ -862,7 +876,7 @@ private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata exec encodeName(buf, ((MethodMetadata) executable).name); } encodeArray(buf, executable.parameterTypes, parameterType -> encodeType(buf, parameterType)); - if (isMethod) { + if (isMethod && (executable.complete || isHiding)) { encodeType(buf, ((MethodMetadata) executable).returnType); } if (executable.complete) { @@ -1005,9 +1019,8 @@ private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) buf.putS4(encoders.sourceClasses.getIndex(annotation.annotationType())); AnnotationType type = AnnotationType.getInstance(annotation.annotationType()); buf.putU2(type.members().size()); - for (Map.Entry entry : type.members().entrySet()) { - String memberName = entry.getKey(); - Method valueAccessor = entry.getValue(); + for (String memberName : orderedAnnotationMemberNames(annotation)) { + Method valueAccessor = type.members().get(memberName); buf.putS4(encoders.sourceMethodNames.getIndex(memberName)); try { encodeValue(buf, valueAccessor.invoke(annotation), type.memberTypes().get(memberName)); @@ -1019,6 +1032,31 @@ private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) } } + private static final Field annotationInvocationHandlerMemberValues; + + static { + try { + annotationInvocationHandlerMemberValues = ReflectionUtil.lookupField(Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"), "memberValues"); + } catch (ClassNotFoundException e) { + throw GraalError.shouldNotReachHere(); + } + } + + /* + * The order of annotation values returned by AnnotationType.members() is random, so we use the + * LinkedHashMap stored in the invocation handler to ensure the annotation values are encoded in + * the right order. + */ + @SuppressWarnings("unchecked") + private static Set orderedAnnotationMemberNames(Annotation annotation) { + try { + Map memberValues = (Map) annotationInvocationHandlerMemberValues.get(Proxy.getInvocationHandler(annotation)); + return memberValues.keySet(); + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(); + } + } + private byte[] encodeMemberValue(Object value) { if (value == null) { return null; @@ -1158,7 +1196,7 @@ private static byte tag(Class type) { } } - private static Class getAnnotationEncodingType(Object value) { + static Class getAnnotationEncodingType(Object value) { Class type = value.getClass(); if (Proxy.isProxyClass(type)) { assert type.getInterfaces().length == 1; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java index 2d25bb594e28..fbdef9bc7df3 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java @@ -64,6 +64,7 @@ public class ReflectionMetadataDecoderImpl implements ReflectionMetadataDecoder public static final int IN_HEAP_FLAG_MASK = 1 << IN_HEAP_FLAG_INDEX; public static final int HIDING_FLAG_INDEX = 29; public static final int HIDING_FLAG_MASK = 1 << HIDING_FLAG_INDEX; + public static final int ALL_FLAGS_MASK = COMPLETE_FLAG_MASK | IN_HEAP_FLAG_MASK | HIDING_FLAG_MASK; static byte[] getEncoding() { return ImageSingletons.lookup(ReflectionMetadataEncoding.class).getEncoding(); @@ -181,7 +182,7 @@ public byte[] parseByteArray(int index) { } @Override - public boolean isHidingMethod(int modifiers) { + public boolean isHiding(int modifiers) { return (modifiers & HIDING_FLAG_MASK) != 0; } @@ -207,7 +208,7 @@ public long getMetadataByteLength() { * StringIndex deletedReason * } * - *

+ * * Heap field encoding. * *

@@ -216,12 +217,22 @@ public long getMetadataByteLength() {
      *     ObjectIndex fieldObject
      * }
      * 
- *

- * Partial field encoding. + * + * Hiding field encoding. * *

-     * PartialFieldMetadata : FieldMetadata {
-     *     int         modifiers
+     * HidingFieldMetadata : FieldMetadata {
+     *     int         modifiers (including HIDING flag)
+     *     StringIndex name
+     *     ClassIndex  type
+     * }
+     * 
+ * + * Reachable field encoding. + * + *
+     * ReachableFieldEncoding : FieldMetadata {
+     *     int         modifiers (always zero)
      *     StringIndex name
      * }
      * 
@@ -229,30 +240,40 @@ public long getMetadataByteLength() { private static Field decodeField(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { int modifiers = buf.getUVInt(); boolean inHeap = (modifiers & IN_HEAP_FLAG_MASK) != 0; + boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; if (inHeap) { Field field = (Field) decodeObject(buf, info); if (publicOnly && !Modifier.isPublic(field.getModifiers())) { return null; } + if (reflectOnly && !complete) { + return null; + } return field; } - boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; + boolean hiding = (modifiers & HIDING_FLAG_MASK) != 0; + assert !(complete && hiding); modifiers &= ~COMPLETE_FLAG_MASK; String name = decodeName(buf, info); + Class type = (complete || hiding) ? decodeType(buf, info) : null; if (!complete) { - if (reflectOnly) { + if (reflectOnly != hiding) { + /* + * 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. + */ return null; } Target_java_lang_reflect_Field field = new Target_java_lang_reflect_Field(); if (JavaVersionUtil.JAVA_SPEC >= 17) { - field.constructorJDK17OrLater(declaringClass, name, null, modifiers, false, -1, null, null); + field.constructorJDK17OrLater(declaringClass, name, type, modifiers, false, -1, null, null); } else { - field.constructorJDK11OrEarlier(declaringClass, name, null, modifiers, -1, null, null); + field.constructorJDK11OrEarlier(declaringClass, name, type, modifiers, -1, null, null); } return SubstrateUtil.cast(field, Field.class); } - Class type = decodeType(buf, info); boolean trustedFinal = (JavaVersionUtil.JAVA_SPEC >= 17) ? buf.getU1() == 1 : false; String signature = decodeName(buf, info); byte[] annotations = decodeByteArray(buf); @@ -303,16 +324,26 @@ private static Field decodeField(UnsafeArrayTypeReader buf, CodeInfo info, Class * } * * - * Partial method encoding. + * Hiding method encoding. * *
-     * PartialMethodMetadata : MethodMetadata {
-     *     int          modifiers
+     * HidingMethodMetadata : MethodMetadata {
+     *     int          modifiers      (including HIDING flag)
      *     StringIndex  name
      *     ClassIndex[] parameterTypes
      *     ClassIndex   returnType
      * }
      * 
+ * + * Reachable method encoding. + * + *
+     * ReachableMethodMetadata : MethodMetadata {
+     *     int          modifiers      (always zero)
+     *     StringIndex  name
+     *     ClassIndex[] parameterTypes
+     * }
+     * 
*/ private static Method decodeMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { return (Method) decodeExecutable(buf, info, declaringClass, publicOnly, reflectOnly, true); @@ -343,11 +374,11 @@ private static Method decodeMethod(UnsafeArrayTypeReader buf, CodeInfo info, Cla * } * * - * Partial constructor encoding. + * Reachable constructor encoding. * *
-     * PartialConstructorMetadata : ConstructorMetadata {
-     *     int          modifiers
+     * ReachableConstructorMetadata : ConstructorMetadata {
+     *     int          modifiers      (always zero)
      *     ClassIndex[] parameterTypes
      * }
      * 
@@ -359,21 +390,24 @@ private static Constructor decodeConstructor(UnsafeArrayTypeReader buf, CodeI private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly, boolean isMethod) { int modifiers = buf.getUVInt(); boolean inHeap = (modifiers & IN_HEAP_FLAG_MASK) != 0; + boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; if (inHeap) { Executable executable = (Executable) decodeObject(buf, info); if (publicOnly && !Modifier.isPublic(executable.getModifiers())) { return null; } + if (reflectOnly && !complete) { + return null; + } return executable; } - boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; boolean hiding = (modifiers & HIDING_FLAG_MASK) != 0; assert !(complete && hiding); modifiers &= ~COMPLETE_FLAG_MASK; String name = isMethod ? decodeName(buf, info) : null; Class[] parameterTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf, info)); - Class returnType = isMethod ? decodeType(buf, info) : null; + Class returnType = isMethod && (complete || hiding) ? decodeType(buf, info) : null; if (!complete) { if (reflectOnly != hiding) { /*