From 18b155896c357b22af5f3e60335cf5b85d83c910 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Mon, 17 Jan 2022 16:27:53 +0100 Subject: [PATCH 1/3] Create reflection objects at runtime using metadata --- .../common/util/UnsafeArrayTypeWriter.java | 40 +- .../compiler/replacements/BoxingSnippets.java | 7 +- .../impl/RuntimeReflectionSupport.java | 20 +- substratevm/mx.substratevm/suite.py | 2 + .../graal/pointsto/heap/ImageHeapScanner.java | 2 +- .../com/oracle/svm/core/SubstrateOptions.java | 4 - .../oracle/svm/core/code/CodeInfoEncoder.java | 2 +- .../com/oracle/svm/core/hub/DynamicHub.java | 844 ++++++++++-------- .../svm/core/hub/DynamicHubCompanion.java | 127 +-- .../svm/core/jdk/SunMiscSubstitutions.java | 4 - .../svm/core/meta/ReadableJavaField.java | 4 +- .../core/reflect/MethodMetadataDecoder.java | 37 +- ...et_java_lang_reflect_RecordComponent.java} | 37 +- ...get_jdk_internal_reflect_ConstantPool.java | 2 +- .../svm/hosted/ClassNewInstanceFeature.java | 8 - .../oracle/svm/hosted/ProgressReporter.java | 7 +- .../analysis/DynamicHubInitializer.java | 220 +---- .../ConfigurableClassInitialization.java | 14 - .../svm/hosted/heap/SVMImageHeapScanner.java | 12 + .../hosted/image/NativeImageCodeCache.java | 85 +- .../UnsafeAutomaticSubstitutionProcessor.java | 4 +- .../svm/reflect/hosted/MethodMetadata.java | 89 -- .../hosted/MethodMetadataEncoderImpl.java | 812 ++++++++++++++--- .../reflect/hosted/ReflectionDataBuilder.java | 610 ++++++------- .../svm/reflect/hosted/ReflectionFeature.java | 6 +- .../reflect/hosted/ReflectionMetadata.java | 231 +++++ .../hosted/ReflectionObjectReplacer.java | 116 +-- .../reflect/proxy/DynamicProxySupport.java | 21 +- .../proxy/hosted/DynamicProxyFeature.java | 2 +- .../target/MethodMetadataDecoderImpl.java | 320 ++++--- ...et_java_lang_reflect_AccessibleObject.java | 28 +- .../Target_java_lang_reflect_Constructor.java | 104 +-- .../Target_java_lang_reflect_Executable.java | 281 +----- .../Target_java_lang_reflect_Field.java | 75 +- .../Target_java_lang_reflect_Method.java | 81 +- .../Target_java_lang_reflect_Parameter.java | 12 +- ...arget_java_lang_reflect_ReflectAccess.java | 44 +- ...n_reflect_annotation_AnnotationParser.java | 109 +-- ...flect_annotation_TypeAnnotationParser.java | 1 + .../svm/truffle/TruffleBaseFeature.java | 6 + 40 files changed, 2310 insertions(+), 2120 deletions(-) rename substratevm/src/{com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java => com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_java_lang_reflect_RecordComponent.java} (59%) rename substratevm/src/{com.oracle.svm.reflect/src/com/oracle/svm/reflect/target => com.oracle.svm.core/src/com/oracle/svm/core/reflect}/Target_jdk_internal_reflect_ConstantPool.java (99%) delete mode 100644 substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java create mode 100644 substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java index fbe7c9ce94a4..bcb42968c32a 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java @@ -74,7 +74,13 @@ protected Chunk(int arrayLength) { protected int totalSize; public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess) { - if (supportsUnalignedMemoryAccess) { + return create(supportsUnalignedMemoryAccess, false); + } + + public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess, boolean bigEndian) { + if (bigEndian) { + return new BigEndianUnsafeArrayTypeWriter(); + } else if (supportsUnalignedMemoryAccess) { return new UnalignedUnsafeArrayTypeWriter(); } else { return new AlignedUnsafeArrayTypeWriter(); @@ -287,3 +293,35 @@ protected void putS8(long value, Chunk chunk, long offset) { UNSAFE.putByte(chunk.data, offset + 7, (byte) (value >> 56)); } } + +final class BigEndianUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter { + private static final Unsafe UNSAFE = getUnsafe(); + + @Override + protected void putS2(long value, Chunk chunk, long offset) { + assert TypeConversion.isS2(value); + UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 8)); + UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 0)); + } + + @Override + protected void putS4(long value, Chunk chunk, long offset) { + assert TypeConversion.isS4(value); + UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 24)); + UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 16)); + UNSAFE.putByte(chunk.data, offset + 2, (byte) (value >> 8)); + UNSAFE.putByte(chunk.data, offset + 3, (byte) (value >> 0)); + } + + @Override + protected void putS8(long value, Chunk chunk, long offset) { + UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 56)); + UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 48)); + UNSAFE.putByte(chunk.data, offset + 2, (byte) (value >> 40)); + UNSAFE.putByte(chunk.data, offset + 3, (byte) (value >> 32)); + UNSAFE.putByte(chunk.data, offset + 4, (byte) (value >> 24)); + UNSAFE.putByte(chunk.data, offset + 5, (byte) (value >> 16)); + UNSAFE.putByte(chunk.data, offset + 6, (byte) (value >> 8)); + UNSAFE.putByte(chunk.data, offset + 7, (byte) (value >> 0)); + } +} diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java index 47f1d3d4afa3..64b961a5322a 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java @@ -221,7 +221,12 @@ private static LocationIdentity getCacheLocation(CoreProviders providers, JavaKi if (innerClasses == null || innerClasses.length == 0) { throw GraalError.shouldNotReachHere("Inner classes must exist"); } - return new FieldLocationIdentity(providers.getMetaAccess().lookupJavaField(innerClasses[0].getDeclaredField("cache"))); + for (Class innerClass : innerClasses) { + if (innerClass.getName().endsWith("Cache")) { + return new FieldLocationIdentity(providers.getMetaAccess().lookupJavaField(innerClass.getDeclaredField("cache"))); + } + } + throw GraalError.shouldNotReachHere("No cache inner class found"); } catch (Throwable e) { throw GraalError.shouldNotReachHere(e); } 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 7f5f5d0e8e53..8d8bd0481988 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 @@ -40,18 +40,32 @@ */ package org.graalvm.nativeimage.impl; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.util.Map; import java.util.Set; public interface RuntimeReflectionSupport extends ReflectionRegistry { - // specific to java.lang.reflect reflection - Set getQueriedOnlyMethods(); + Map, Set>> getReflectionInnerClasses(); + + Set getReflectionFields(); + + Set getReflectionExecutables(); + + Object getAccessor(Executable method); /* * Returns the methods that shadow a superclass method registered for reflection, to be excluded * from reflection queries. */ - Set getHidingMethods(); + Set getHidingReflectionMethods(); + + Object[] getRecordComponents(Class type); + + void registerHeapReflectionObject(AccessibleObject object); + + Set getHeapReflectionObjects(); int getReflectionClassesCount(); diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index dde0dd16cf7b..0586015359ad 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -194,7 +194,9 @@ "sun.invoke.util", "sun.net", "sun.reflect.annotation", + "sun.reflect.generics.factory", "sun.reflect.generics.reflectiveObjects", + "sun.reflect.generics.repository", "sun.reflect.generics.tree", "sun.security.jca", "sun.security.ssl", diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index 6d95bffa8b08..492823af7c11 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -400,7 +400,7 @@ private boolean notifyAnalysis(JavaConstant array, AnalysisType arrayType, JavaC return analysisModified; } - void onObjectReachable(ImageHeapObject imageHeapObject) { + protected void onObjectReachable(ImageHeapObject imageHeapObject) { AnalysisType objectType = metaAccess.lookupJavaType(imageHeapObject.getObject()); imageHeap.add(objectType, imageHeapObject); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index b195673ca765..061f85611a99 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -720,10 +720,6 @@ protected void onValueUpdate(EconomicMap, Object> values, String ol } }; - @APIOption(name = "configure-reflection-metadata")// - @Option(help = "Enable runtime instantiation of reflection objects for non-invoked methods.", type = OptionType.Expert)// - public static final HostedOptionKey ConfigureReflectionMetadata = new HostedOptionKey<>(true); - @Option(help = "Include a list of methods included in the image for runtime inspection.", type = OptionType.Expert)// public static final HostedOptionKey IncludeMethodData = new HostedOptionKey<>(true); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java index 2b91ee63e9aa..f6517ab6b705 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java @@ -106,7 +106,7 @@ public void addToReferenceMapSize(long size) { } public static final class Encoders { - final FrequencyEncoder objectConstants; + public final FrequencyEncoder objectConstants; public final FrequencyEncoder> sourceClasses; public final FrequencyEncoder sourceMethodNames; final FrequencyEncoder names; 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 a0d10a71879e..c2e8a6ce01f2 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 @@ -29,10 +29,10 @@ import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; import java.lang.ref.Reference; +import java.lang.ref.SoftReference; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.InvocationTargetException; @@ -42,12 +42,17 @@ import java.lang.reflect.TypeVariable; import java.net.URL; import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.StringJoiner; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.SuppressFBWarnings; +import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunctionPointer; @@ -58,7 +63,9 @@ import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.Hybrid; +import com.oracle.svm.core.annotate.InjectAccessors; import com.oracle.svm.core.annotate.KeepOriginal; +import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; @@ -66,17 +73,25 @@ import com.oracle.svm.core.annotate.UnknownObjectField; import com.oracle.svm.core.classinitialization.ClassInitializationInfo; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; +import com.oracle.svm.core.jdk.JDK11OrEarlier; import com.oracle.svm.core.jdk.JDK17OrLater; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.meta.SharedType; +import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent; +import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.LazyFinalReference; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; -import jdk.internal.reflect.ConstantPool; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.Reflection; +import jdk.internal.reflect.ReflectionFactory; import jdk.vm.ci.meta.JavaKind; +import sun.reflect.annotation.AnnotationType; +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.repository.ClassRepository; @Hybrid(canHybridFieldsBeDuplicated = false) @Substitute @@ -253,13 +268,6 @@ public final class DynamicHub implements JavaKind.FormatWithToString, AnnotatedE */ private final String sourceFileName; - /** - * The annotations of this class. This field holds either null (no annotations), an Annotation - * (one annotation), or an Annotation[] array (more than one annotation). This eliminates the - * need for an array for the case that there are less than two annotations. - */ - private Object annotationsEncoding; - /** * Metadata for running class initializers at run time. Refers to a singleton marker object for * classes/interfaces already initialized during image generation, i.e., this field is never @@ -276,9 +284,6 @@ public final class DynamicHub implements JavaKind.FormatWithToString, AnnotatedE @Hybrid.Array private CFunctionPointer[] vtable; - private GenericInfo genericInfo; - private AnnotatedSuperInfo annotatedSuperInfo; - /** * Field used for module information access at run-time. */ @@ -297,6 +302,46 @@ public void setModule(Module module) { private final DynamicHubCompanion companion; + private String signature; + + @Substitute @InjectAccessors(ReflectionDataAccessors.class) // + private SoftReference> reflectionData; + + @Substitute @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // + private int classRedefinedCount; + + @Substitute @InjectAccessors(AnnotationDataAccessors.class) // + private Target_java_lang_Class_AnnotationData annotationData; + + @Substitute @InjectAccessors(AnnotationTypeAccessors.class) // + private AnnotationType annotationType; + + @Substitute @InjectAccessors(CachedConstructorAccessors.class) // + private Constructor cachedConstructor; + + @Substitute @InjectAccessors(NewInstanceCallerCacheAccessors.class) // + @TargetElement(onlyWith = JDK11OrEarlier.class) // + private Class newInstanceCallerCache; + + @UnknownObjectField(types = {byte[].class}) private byte[] enclosingMethodInfo; + + @UnknownObjectField(types = {byte[].class}) private byte[] annotations; + + @UnknownObjectField(types = {byte[].class}) private byte[] typeAnnotations; + + @UnknownObjectField(types = {byte[].class}) byte[] fieldsEncoding; + + @UnknownObjectField(types = {byte[].class}) byte[] methodsEncoding; + + @UnknownObjectField(types = {byte[].class}) byte[] constructorsEncoding; + + @UnknownObjectField(types = {byte[].class}) byte[] classesEncoding; + + @TargetElement(onlyWith = JDK17OrLater.class)// + @UnknownObjectField(types = {byte[].class}) byte[] recordComponentsEncoding; + + @UnknownObjectField(types = {byte[].class}) byte[] permittedSubclassesEncoding; + @Platforms(Platform.HOSTED_ONLY.class) public DynamicHub(Class hostedJavaClass, String name, HubType hubType, ReferenceType referenceType, Object isLocalClass, Object isAnonymousClass, DynamicHub superType, DynamicHub componentHub, String sourceFileName, int modifiers, ClassLoader classLoader, boolean isHidden, boolean isRecord, Class nestHost, boolean assertionStatus, @@ -376,26 +421,6 @@ public void setEnclosingClass(DynamicHub enclosingClass) { this.enclosingClass = enclosingClass; } - @Platforms(Platform.HOSTED_ONLY.class) - public void setGenericInfo(GenericInfo genericInfo) { - this.genericInfo = genericInfo; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public GenericInfo getGenericInfo() { - return genericInfo; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setAnnotatedSuperInfo(AnnotatedSuperInfo annotatedSuperInfo) { - this.annotatedSuperInfo = annotatedSuperInfo; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public AnnotatedSuperInfo getAnnotatedSuperInfo() { - return annotatedSuperInfo; - } - @Platforms(Platform.HOSTED_ONLY.class) public void setInterfacesEncoding(Object interfacesEncoding) { this.interfacesEncoding = interfacesEncoding; @@ -406,18 +431,6 @@ public Object getInterfacesEncoding() { return interfacesEncoding; } - @Platforms(Platform.HOSTED_ONLY.class) - public boolean setAnnotationsEncoding(Object annotationsEncoding) { - boolean result = this.annotationsEncoding != annotationsEncoding; - this.annotationsEncoding = annotationsEncoding; - return result; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public Object getAnnotationsEncoding() { - return annotationsEncoding; - } - @Platforms(Platform.HOSTED_ONLY.class) public boolean shouldInitEnumConstants() { return enumConstantsReference == null; @@ -451,6 +464,61 @@ public void initEnumConstantsAtRuntime(Class enumClass) { } } + @Platforms(Platform.HOSTED_ONLY.class) + public void setSignature(String signature) { + this.signature = signature; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public String getSignature() { + return signature; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setFieldsEncoding(byte[] encoding) { + this.fieldsEncoding = encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setMethodsEncoding(byte[] encoding) { + this.methodsEncoding = encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setConstructorsEncoding(byte[] encoding) { + this.constructorsEncoding = encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setClassesEncoding(byte[] encoding) { + this.classesEncoding = encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setRecordComponentsEncoding(byte[] encoding) { + this.recordComponentsEncoding = encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setPermittedSubclassesEncoding(byte[] encoding) { + this.permittedSubclassesEncoding = encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setAnnotationsEncoding(byte[] encoding) { + this.annotations = encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setTypeAnnotationsEncoding(byte[] encoding) { + this.typeAnnotations = encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setEnclosingMethodInfo(byte[] encoding) { + this.enclosingMethodInfo = encoding; + } + /** Executed at runtime. */ private static Object initEnumConstantsAtRuntime(Method values) { try { @@ -707,20 +775,14 @@ void setClassLoaderAtRuntime(ClassLoader loader) { companion.setClassLoader(loader); } - @Substitute - private String getSimpleName() { - return getSimpleName0(); - } + @KeepOriginal + private native String getSimpleName(); @KeepOriginal // private native String getSimpleName0(); - @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel string comparison") - @Substitute - private String getCanonicalName() { - String canonicalName = getCanonicalName0(); - return canonicalName == Target_java_lang_Class_ReflectionData.NULL_SENTINEL ? null : canonicalName; - } + @KeepOriginal + private native String getCanonicalName(); @KeepOriginal private native String getCanonicalName0(); @@ -805,86 +867,20 @@ private Object getDeclaringClassInternal() { } } - @Substitute - public DynamicHub[] getInterfaces() { - return getInterfaces(this, true); - } - - @Substitute - private DynamicHub[] getInterfaces(boolean cloneArray) { - return getInterfaces(this, cloneArray); - } - - private static DynamicHub[] getInterfaces(DynamicHub hub, boolean cloneArray) { - if (hub.interfacesEncoding == null) { - return new DynamicHub[0]; - } else if (hub.interfacesEncoding instanceof DynamicHub) { - return new DynamicHub[]{(DynamicHub) hub.interfacesEncoding}; - } else { - /* The caller is allowed to modify the array, so we have to make a copy. */ - return cloneArray ? ((DynamicHub[]) hub.interfacesEncoding).clone() : (DynamicHub[]) hub.interfacesEncoding; - } - } - - @Substitute - public Object newInstance() throws Throwable { - /* - * The JavaDoc for the original method states that "The class is initialized if it has not - * already been initialized". However, it doesn't specify if the absence of a nullary - * constructor will result in an InstantiationException before the class is initialized. We - * eagerly initialize it to conform with JCK tests. - */ - ensureInitialized(); - final Constructor nullaryConstructor = rd.nullaryConstructor; - if (nullaryConstructor == null) { - throw new InstantiationException("Type `" + this.getCanonicalName() + - "` can not be instantiated reflectively as it does not have a no-parameter constructor or the no-parameter constructor has not been added explicitly to the native image."); - } - try { - return nullaryConstructor.newInstance((Object[]) null); - } catch (InvocationTargetException e) { - throw e.getCause(); - } - } - - /** - * Used for reporting errors related to reflective instantiation ({@code Class.newInstance}). - * This method handles: i) abstract classes, ii) interfaces, and iii) primitive types. - * - * @param instance Allocated instance of a type. The instance is used to report errors with a - * proper type name. - * @return Always nothing. - * @throws InstantiationException always. - */ - private static Object newInstanceInstantiationError(Object instance) throws InstantiationException { - if (instance == null) { - throw VMError.shouldNotReachHere("This case should be handled by the `DynamicNewInstance` lowering."); - } else { - throw new InstantiationException("Type `" + instance.getClass().getCanonicalName() + "` can not be instantiated reflectively as it does not have a no-parameter constructor."); - } + @KeepOriginal + public native DynamicHub[] getInterfaces(); - } + @KeepOriginal + private native DynamicHub[] getInterfaces(boolean cloneArray); - /** - * Used for reporting errors related to reflective instantiation ({@code Class.newInstance}) - * when a constructor is removed by reachability analysis. - * - * @param instance Allocated instance of a type. The instance is used to report errors with a - * proper type name. - * @return Always nothing. - */ - private static Object newInstanceReachableError(Object instance) { - throw new RuntimeException("Constructor of `" + instance.getClass().getCanonicalName() + - "` was removed by reachability analysis. Use `Feature.BeforeAnalysisAccess.registerForReflectiveInstantiation` to register the type for reflective instantiation."); - } + @KeepOriginal + public native Object newInstance() throws Throwable; // Checkstyle: allow direct annotation access (false positives) - @Substitute + @KeepOriginal @Override - public T getAnnotation(Class annotationClass) { - return AnnotationsEncoding.decodeAnnotations(annotationsEncoding).getAnnotation(annotationClass); - } + public native T getAnnotation(Class annotationClass); @Substitute @Override @@ -892,11 +888,9 @@ public boolean isAnnotationPresent(Class annotationClass) return getAnnotation(annotationClass) != null; } - @Substitute + @KeepOriginal @Override - public Annotation[] getAnnotations() { - return AnnotationsEncoding.decodeAnnotations(annotationsEncoding).getAnnotations(); - } + public native Annotation[] getAnnotations(); @Substitute @Override @@ -923,11 +917,9 @@ public T[] getAnnotationsByType(Class annotationClass) return result; } - @Substitute + @KeepOriginal @Override - public Annotation[] getDeclaredAnnotations() { - return AnnotationsEncoding.decodeAnnotations(annotationsEncoding).getDeclaredAnnotations(); - } + public native Annotation[] getDeclaredAnnotations(); /** * In JDK this method uses a lazily computed map of annotations. @@ -935,95 +927,16 @@ public Annotation[] getDeclaredAnnotations() { * In SVM we have a pre-initialized array so we use a less efficient implementation from * {@link AnnotatedElement} that does the same. */ - @Substitute + @KeepOriginal @Override - public A[] getDeclaredAnnotationsByType(Class annotationClass) { - return GenericDeclaration.super.getDeclaredAnnotationsByType(annotationClass); - } + public native A[] getDeclaredAnnotationsByType(Class annotationClass); - @Substitute + @KeepOriginal @Override - public T getDeclaredAnnotation(Class annotationClass) { - return AnnotationsEncoding.decodeAnnotations(annotationsEncoding).getDeclaredAnnotation(annotationClass); - } + public native T getDeclaredAnnotation(Class annotationClass); // Checkstyle: disallow direct annotation access - /** - * This class stores similar information as the non-public class java.lang.Class.ReflectionData. - */ - public static final class ReflectionData { - static final ReflectionData EMPTY = new ReflectionData(new Field[0], new Field[0], new Field[0], new Method[0], new Method[0], new Constructor[0], new Constructor[0], null, new Field[0], - new Method[0], new Class[0], null, new Class[0], null, null); - - public static ReflectionData get(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods, - Constructor[] declaredConstructors, Constructor[] publicConstructors, Constructor nullaryConstructor, Field[] declaredPublicFields, - Method[] declaredPublicMethods, Class[] declaredClasses, Class[] permittedSubclasses, Class[] publicClasses, Executable enclosingMethodOrConstructor, - Object[] recordComponents) { - - if (z(declaredFields) && z(publicFields) && z(publicUnhiddenFields) && z(declaredMethods) && z(publicMethods) && z(declaredConstructors) && - z(publicConstructors) && nullaryConstructor == null && z(declaredPublicFields) && z(declaredPublicMethods) && z(declaredClasses) && - permittedSubclasses == null && z(publicClasses) && enclosingMethodOrConstructor == null && (recordComponents == null || z(recordComponents))) { - return EMPTY; // avoid redundant objects in image heap - } - return new ReflectionData(declaredFields, publicFields, publicUnhiddenFields, declaredMethods, publicMethods, declaredConstructors, publicConstructors, nullaryConstructor, - declaredPublicFields, declaredPublicMethods, declaredClasses, permittedSubclasses, publicClasses, enclosingMethodOrConstructor, recordComponents); - } - - private static boolean z(Object[] array) { // for better readability above - return array.length == 0; - } - - final Field[] declaredFields; - final Field[] publicFields; - final Field[] publicUnhiddenFields; - final Method[] declaredMethods; - final Method[] publicMethods; - final Constructor[] declaredConstructors; - final Constructor[] publicConstructors; - final Constructor nullaryConstructor; - final Field[] declaredPublicFields; - final Method[] declaredPublicMethods; - final Class[] declaredClasses; - final Class[] permittedSubclasses; - final Class[] publicClasses; - final Object[] recordComponents; - - /** - * The result of {@link Class#getEnclosingMethod()} or - * {@link Class#getEnclosingConstructor()}. - */ - final Executable enclosingMethodOrConstructor; - - ReflectionData(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods, Constructor[] declaredConstructors, - Constructor[] publicConstructors, Constructor nullaryConstructor, Field[] declaredPublicFields, Method[] declaredPublicMethods, Class[] declaredClasses, - Class[] permittedSubclasses, Class[] publicClasses, Executable enclosingMethodOrConstructor, - Object[] recordComponents) { - this.declaredFields = declaredFields; - this.publicFields = publicFields; - this.publicUnhiddenFields = publicUnhiddenFields; - this.declaredMethods = declaredMethods; - this.publicMethods = publicMethods; - this.declaredConstructors = declaredConstructors; - this.publicConstructors = publicConstructors; - this.nullaryConstructor = nullaryConstructor; - this.declaredPublicFields = declaredPublicFields; - this.declaredPublicMethods = declaredPublicMethods; - this.declaredClasses = declaredClasses; - this.permittedSubclasses = permittedSubclasses; - this.publicClasses = publicClasses; - this.enclosingMethodOrConstructor = enclosingMethodOrConstructor; - this.recordComponents = recordComponents; - } - } - - ReflectionData rd = ReflectionData.EMPTY; - - @Platforms(Platform.HOSTED_ONLY.class) - public void setReflectionData(ReflectionData rd) { - this.rd = rd; - } - @KeepOriginal private native Field[] getFields(); @@ -1033,61 +946,20 @@ public void setReflectionData(ReflectionData rd) { @KeepOriginal private native Constructor[] getConstructors(); - @Substitute - private Field getField(@SuppressWarnings("hiding") String name) throws NoSuchFieldException { - /* - * The original code of getField() does a recursive search to avoid creating objects for all - * public fields. We prepare them during the image build and can just iterate here. - * - * Note that we only search those fields which are not hidden by other fields which are - * possibly not registered for reflective access. For example: - * - * class A { public int field; public static int staticField; } // both registered - * - * class B extends A { public int field; public static int staticField; } // not registered - * - * Here, we do not want B.class.getField("field") to return A.field; same applies to - * staticField. Note that hidden fields of A are still returned by B.class.getFields(). - */ - for (Field field : rd.publicUnhiddenFields) { - if (field.getName().equals(name)) { - return field; - } - } - throw new NoSuchFieldException(name); - } + @KeepOriginal + private native Field getField(@SuppressWarnings("hiding") String name) throws NoSuchMethodException; - @Substitute - private Method getMethod(@SuppressWarnings("hiding") String name, Class... parameterTypes) throws NoSuchMethodException { - /* - * The original code of getMethods() does a recursive search to avoid creating objects for - * all public methods. We prepare them during the image build and can just iterate here. - */ - Method method = searchMethods(companion.getCompleteReflectionData(this).publicMethods, name, parameterTypes); - if (method == null) { - throw new NoSuchMethodException(describeMethod(getName() + "." + name + "(", parameterTypes, ")")); - } - return method; - } + @KeepOriginal + private native Method getMethod(@SuppressWarnings("hiding") String name, Class... parameterTypes) throws NoSuchMethodException; @KeepOriginal private native Constructor getConstructor(Class... parameterTypes); - @Substitute - private Class[] getDeclaredClasses() { - for (Class clazz : rd.declaredClasses) { - PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); - } - return rd.declaredClasses; - } + @KeepOriginal + private native Class[] getDeclaredClasses(); - @Substitute - private Class[] getClasses() { - for (Class clazz : rd.publicClasses) { - PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); - } - return rd.publicClasses; - } + @KeepOriginal + private native Class[] getClasses(); @KeepOriginal private native Field[] getDeclaredFields(); @@ -1107,32 +979,20 @@ private Class[] getClasses() { @KeepOriginal private native Constructor getDeclaredConstructor(Class... parameterTypes); - @Substitute - private Constructor[] privateGetDeclaredConstructors(boolean publicOnly) { - ReflectionData reflectionData = companion.getCompleteReflectionData(this); - return publicOnly ? reflectionData.publicConstructors : reflectionData.declaredConstructors; - } + @KeepOriginal + private native Constructor[] privateGetDeclaredConstructors(boolean publicOnly); - @Substitute - private Field[] privateGetDeclaredFields(boolean publicOnly) { - return publicOnly ? rd.declaredPublicFields : rd.declaredFields; - } + @KeepOriginal + private native Field[] privateGetDeclaredFields(boolean publicOnly); - @Substitute - private Method[] privateGetDeclaredMethods(boolean publicOnly) { - ReflectionData reflectionData = companion.getCompleteReflectionData(this); - return publicOnly ? reflectionData.declaredPublicMethods : reflectionData.declaredMethods; - } + @KeepOriginal + private native Method[] privateGetDeclaredMethods(boolean publicOnly); - @Substitute - private Field[] privateGetPublicFields() { - return rd.publicFields; - } + @KeepOriginal + private native Field[] privateGetPublicFields(); - @Substitute - Method[] privateGetPublicMethods() { - return companion.getCompleteReflectionData(this).publicMethods; - } + @KeepOriginal + native Method[] privateGetPublicMethods(); @KeepOriginal @TargetElement(onlyWith = JDK17OrLater.class) @@ -1141,30 +1001,17 @@ Method[] privateGetPublicMethods() { @Substitute @TargetElement(onlyWith = JDK17OrLater.class) private Target_java_lang_reflect_RecordComponent[] getRecordComponents0() { - Object[] result = rd.recordComponents; - if (result == null) { + if (recordComponentsEncoding == null) { /* See ReflectionDataBuilder.buildRecordComponents() for details. */ throw VMError.unsupportedFeature("Record components not available for record class " + getTypeName() + ". " + "All record component accessor methods of this record class must be included in the reflection configuration at image build time, then this method can be called."); } - return (Target_java_lang_reflect_RecordComponent[]) result; + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseRecordComponents(this, recordComponentsEncoding); } - @Substitute + @KeepOriginal @TargetElement(onlyWith = JDK17OrLater.class) - private Class[] getPermittedSubclasses() { - /* - * We make several assumptions here: we precompute this value by using the cached value from - * image build time, agent run / custom reflection configuration is required, we ignore all - * classloader checks, and assume that cached result would be valid. - */ - if (rd.permittedSubclasses != null) { - for (Class clazz : rd.permittedSubclasses) { - PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); - } - } - return rd.permittedSubclasses; - } + private native Class[] getPermittedSubclasses(); @Substitute @SuppressWarnings("unused") @@ -1179,15 +1026,26 @@ private void checkPackageAccess(SecurityManager sm, ClassLoader ccl, boolean che } @Substitute - private static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactory() { + private static ReflectionFactory getReflectionFactory() { return Target_jdk_internal_reflect_ReflectionFactory.getReflectionFactory(); } @KeepOriginal private static native Field searchFields(Field[] fields, String name); - @KeepOriginal - private static native Method searchMethods(Method[] methods, String name, Class[] parameterTypes); + @Substitute + private static Method searchMethods(Method[] allMethods, String name, Class[] parameterTypes) { + Method[] methods = filterHidingMethods(allMethods); + ReflectionFactory fact = getReflectionFactory(); + Method res = null; + for (Method m : methods) { + if (m.getName().equals(name) && arrayContentsEq(parameterTypes, fact.getExecutableSharedParameterTypes(m)) && + (res == null || (res.getReturnType() != m.getReturnType() && res.getReturnType().isAssignableFrom(m.getReturnType())))) { + res = m; + } + } + return res; + } @KeepOriginal private native Constructor getConstructor0(Class[] parameterTypes, int which); @@ -1198,55 +1056,41 @@ private static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactor @KeepOriginal private static native Field[] copyFields(Field[] arg); - @KeepOriginal - private static native Method[] copyMethods(Method[] arg); + @Substitute + private static Method[] copyMethods(Method[] original) { + Method[] arg = filterHidingMethods(original); + Method[] out = new Method[arg.length]; + ReflectionFactory fact = getReflectionFactory(); + for (int i = 0; i < arg.length; i++) { + out[i] = fact.copyMethod(arg[i]); + } + return out; + } @KeepOriginal private static native Constructor[] copyConstructors(Constructor[] arg); - @Substitute + @KeepOriginal @Override - public TypeVariable[] getTypeParameters() { - return genericInfo.getTypeParameters(); - } + public native TypeVariable[] getTypeParameters(); - @Substitute - public Type[] getGenericInterfaces() { - return genericInfo.hasGenericInterfaces() ? genericInfo.getGenericInterfaces() : getInterfaces(); - } + @KeepOriginal + public native Type[] getGenericInterfaces(); - @Substitute - public Type getGenericSuperclass() { - return genericInfo.hasGenericSuperClass() ? genericInfo.getGenericSuperClass() : getSuperHub(); - } + @KeepOriginal + public native Type getGenericSuperclass(); - @Substitute - public AnnotatedType getAnnotatedSuperclass() { - return annotatedSuperInfo.getAnnotatedSuperclass(); - } + @KeepOriginal + public native AnnotatedType getAnnotatedSuperclass(); - @Substitute - public AnnotatedType[] getAnnotatedInterfaces() { - return annotatedSuperInfo.getAnnotatedInterfaces(); - } + @KeepOriginal + public native AnnotatedType[] getAnnotatedInterfaces(); - @Substitute - private Method getEnclosingMethod() { - if (rd.enclosingMethodOrConstructor instanceof Method) { - PredefinedClassesSupport.throwIfUnresolvable(rd.enclosingMethodOrConstructor.getDeclaringClass(), getClassLoader0()); - return (Method) rd.enclosingMethodOrConstructor; - } - return null; - } + @KeepOriginal + private native Method getEnclosingMethod(); - @Substitute - private Constructor getEnclosingConstructor() { - if (rd.enclosingMethodOrConstructor instanceof Constructor) { - PredefinedClassesSupport.throwIfUnresolvable(rd.enclosingMethodOrConstructor.getDeclaringClass(), getClassLoader0()); - return (Constructor) rd.enclosingMethodOrConstructor; - } - return null; - } + @KeepOriginal + private native Constructor getEnclosingConstructor(); @Substitute public static Class forName(String className) throws ClassNotFoundException { @@ -1310,11 +1154,9 @@ String computePackageName() { return pn; } + @KeepOriginal @Override - @Substitute - public String toString() { - return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName(); - } + public native String toString(); @KeepOriginal public native String toGenericString(); @@ -1367,10 +1209,8 @@ private static String describeMethod(String prefix, Class[] argTypes, String return sj.toString(); } - @Substitute // - private Target_java_lang_Class_ReflectionData reflectionData() { - throw VMError.unsupportedFeature("DynamicHub.reflectionData()"); - } + @KeepOriginal // + private native Target_java_lang_Class_ReflectionData reflectionData(); @KeepOriginal private native boolean isTopLevelClass(); @@ -1394,9 +1234,19 @@ private String getSimpleBinaryName0() { /* See open/src/hotspot/share/prims/jvm.cpp#1522. */ } - @KeepOriginal // + @Substitute // @SuppressWarnings({"unused"}) - private native List getDeclaredPublicMethods(String nameArg, Class... parameterTypes); + List getDeclaredPublicMethods(String methodName, Class... parameterTypes) { + Method[] methods = filterHidingMethods(privateGetDeclaredMethods(/* publicOnly */ true)); + ReflectionFactory factory = getReflectionFactory(); + List result = new ArrayList<>(); + for (Method method : methods) { + if (method.getName().equals(methodName) && Arrays.equals(factory.getExecutableSharedParameterTypes(method), parameterTypes)) { + result.add(factory.copyMethod(method)); + } + } + return result; + } @Substitute public Class getNestHost() { @@ -1477,11 +1327,28 @@ public Optional describeConstable() { @Delete static native Class getPrimitiveClass(String name); - @Delete - private native Object[] getEnclosingMethod0(); + @Substitute + private Object[] getEnclosingMethod0() { + if (enclosingMethodInfo == null) { + return null; + } + Object[] enclosingMethod = ImageSingletons.lookup(MethodMetadataDecoder.class).parseEnclosingMethod(enclosingMethodInfo); + if (enclosingMethod != null) { + PredefinedClassesSupport.throwIfUnresolvable((Class) enclosingMethod[0], getClassLoader0()); + } + return enclosingMethod; + } - @Delete - private native Class[] getInterfaces0(); + @Substitute + private DynamicHub[] getInterfaces0() { + if (interfacesEncoding == null) { + return new DynamicHub[0]; + } else if (interfacesEncoding instanceof DynamicHub) { + return new DynamicHub[]{(DynamicHub) interfacesEncoding}; + } else { + return (DynamicHub[]) interfacesEncoding; + } + } @Substitute private void setSigners(@SuppressWarnings("unused") Object[] signers) { @@ -1491,29 +1358,49 @@ private void setSigners(@SuppressWarnings("unused") Object[] signers) { @Delete private native java.security.ProtectionDomain getProtectionDomain0(); - @Delete - private native String getGenericSignature0(); + @Substitute + private String getGenericSignature0() { + return signature; + } - @Delete - native byte[] getRawAnnotations(); + @Substitute + byte[] getRawAnnotations() { + return annotations; + } - @Delete - native byte[] getRawTypeAnnotations(); + @Substitute + byte[] getRawTypeAnnotations() { + return typeAnnotations; + } - @Delete - native ConstantPool getConstantPool(); + @Substitute + Target_jdk_internal_reflect_ConstantPool getConstantPool() { + return new Target_jdk_internal_reflect_ConstantPool(); + } - @Delete - private native Field[] getDeclaredFields0(boolean publicOnly); + @Substitute + private Field[] getDeclaredFields0(boolean publicOnly) { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseFields(this, fieldsEncoding, publicOnly, true); + } - @Delete - private native Method[] getDeclaredMethods0(boolean publicOnly); + @Substitute + private Method[] getDeclaredMethods0(boolean publicOnly) { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseMethods(this, methodsEncoding, publicOnly, true); + } - @Delete - private native Constructor[] getDeclaredConstructors0(boolean publicOnly); + @Substitute + private Constructor[] getDeclaredConstructors0(boolean publicOnly) { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseConstructors(this, constructorsEncoding, publicOnly, true); + } - @Delete - private native Class[] getDeclaredClasses0(); + @Substitute + private Class[] getDeclaredClasses0() { + Class[] declaredClasses = ImageSingletons.lookup(MethodMetadataDecoder.class).parseClasses(classesEncoding); + for (Class clazz : declaredClasses) { + PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); + } + return declaredClasses; + } @Delete private static native boolean desiredAssertionStatus0(Class clazz); @@ -1526,6 +1413,134 @@ private void setSigners(@SuppressWarnings("unused") Object[] signers) { @Delete private native String initClassName(); + + @KeepOriginal + private static native Class toClass(Type o); + + @Substitute + private ClassRepository getGenericInfo() { + return companion.getGenericInfo(this); + } + + ClassRepository computeGenericInfo() { + String genericSignature = getGenericSignature0(); + if (genericSignature == null) { + return ClassRepository.NONE; + } else { + return ClassRepository.make(genericSignature, getFactory()); + } + } + + @KeepOriginal + private native Target_java_lang_Class_EnclosingMethodInfo getEnclosingMethodInfo(); + + @KeepOriginal + private native Target_java_lang_Class_ReflectionData newReflectionData(SoftReference> oldReflectionData, + int redefinitionCount); + + @KeepOriginal + private native Target_java_lang_Class_AnnotationData annotationData(); + + @KeepOriginal + private native Target_java_lang_Class_AnnotationData createAnnotationData(int redefinitionCount); + + @Substitute + @TargetElement(onlyWith = JDK17OrLater.class) + private Class[] getPermittedSubclasses0() { + if (permittedSubclassesEncoding.length == 0) { + return null; + } + Class[] permittedSubclasses = ImageSingletons.lookup(MethodMetadataDecoder.class).parseClasses(permittedSubclassesEncoding); + for (Class clazz : permittedSubclasses) { + PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); + } + return permittedSubclasses; + } + + @KeepOriginal + private native GenericsFactory getFactory(); + + @KeepOriginal + private native Method getMethod0(String methodName, Class[] parameterTypes); + + @KeepOriginal + private static native void addAll(Collection c, Field[] o); + + @KeepOriginal + private native Target_java_lang_PublicMethods_MethodList getMethodsRecursive(String methodName, Class[] parameterTypes, boolean includeStatic); + + @KeepOriginal + private native Field getField0(String fieldName); + + @KeepOriginal + native AnnotationType getAnnotationType(); + + private static Method[] filterHidingMethods(Method... methods) { + List filtered = new ArrayList<>(); + for (Method method : methods) { + if (!ImageSingletons.lookup(MethodMetadataDecoder.class).isHidingMethod(method.getModifiers())) { + filtered.add(method); + } + } + return filtered.toArray(new Method[0]); + } + + private static class ReflectionDataAccessors { + @SuppressWarnings("unused") + private static SoftReference> getReflectionData(DynamicHub that) { + return that.companion.getReflectionData(); + } + } + + private static class AnnotationDataAccessors { + @SuppressWarnings("unused") + private static Target_java_lang_Class_AnnotationData getAnnotationData(DynamicHub that) { + return that.companion.getAnnotationData(); + } + } + + private static class AnnotationTypeAccessors { + @SuppressWarnings("unused") + private static AnnotationType getAnnotationType(DynamicHub that) { + return that.companion.getAnnotationType(); + } + } + + private static class CachedConstructorAccessors { + @SuppressWarnings("unused") + private static Constructor getCachedConstructor(DynamicHub that) { + return that.companion.getCachedConstructor(); + } + + @SuppressWarnings("unused") + private static void setCachedConstructor(DynamicHub that, Constructor value) { + that.companion.setCachedConstructor(value); + } + } + + private static class NewInstanceCallerCacheAccessors { + @SuppressWarnings("unused") + private static Class getNewInstanceCallerCache(DynamicHub that) { + return that.companion.getNewInstanceCallerCache(); + } + + @SuppressWarnings("unused") + private static void setNewInstanceCallerCache(DynamicHub that, Class value) { + that.companion.setNewInstanceCallerCache(value); + } + } + + public Field[] getReachableFields() { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseFields(this, fieldsEncoding, false, false); + } + + public Method[] getReachableMethods() { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseMethods(this, methodsEncoding, false, false); + } + + public Constructor[] getReachableConstructors() { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseConstructors(this, constructorsEncoding, false, false); + } } /** @@ -1583,18 +1598,83 @@ final class Target_java_lang_Class_ReflectionData { final class Target_jdk_internal_reflect_ReflectionFactory { @Alias // - private static Target_jdk_internal_reflect_ReflectionFactory soleInstance; + private static ReflectionFactory soleInstance; /** * This substitution eliminates the SecurityManager check in the original method, which would * make some build-time verifications fail. */ @Substitute - public static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactory() { + public static ReflectionFactory getReflectionFactory() { return soleInstance; } } -@TargetClass(className = "java.lang.reflect.RecordComponent", onlyWith = JDK17OrLater.class) -final class Target_java_lang_reflect_RecordComponent { +@TargetClass(className = "java.lang.Class", innerClass = "EnclosingMethodInfo") +final class Target_java_lang_Class_EnclosingMethodInfo { +} + +@TargetClass(className = "java.lang.Class", innerClass = "AnnotationData") +final class Target_java_lang_Class_AnnotationData { +} + +@TargetClass(className = "java.lang.PublicMethods", innerClass = "MethodList") +final class Target_java_lang_PublicMethods_MethodList { + @Alias // + Method method; + + @Alias // + Target_java_lang_PublicMethods_MethodList next; + + @Substitute + Method getMostSpecific() { + Method m = method; + Class rt = m.getReturnType(); + for (Target_java_lang_PublicMethods_MethodList ml = next; ml != null; ml = ml.next) { + Method m2 = ml.method; + Class rt2 = m2.getReturnType(); + if (rt2 != rt && rt.isAssignableFrom(rt2)) { + // found more specific return type + m = m2; + rt = rt2; + } + } + /* Filter out hiding methods after the retursive lookup is done */ + return ImageSingletons.lookup(MethodMetadataDecoder.class).isHidingMethod(m.getModifiers()) ? null : m; + } +} + +@TargetClass(className = "java.lang.Class", innerClass = "Atomic") +final class Target_java_lang_Class_Atomic { + @Delete static Unsafe unsafe; + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FieldOffset, declClass = DynamicHubCompanion.class, name = "reflectionData") // + private static long reflectionDataOffset; + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FieldOffset, declClass = DynamicHubCompanion.class, name = "annotationType") // + private static long annotationTypeOffset; + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FieldOffset, declClass = DynamicHubCompanion.class, name = "annotationData") // + private static long annotationDataOffset; + + @Substitute + static boolean casReflectionData(DynamicHub clazz, + SoftReference> oldData, + SoftReference> newData) { + return GraalUnsafeAccess.getUnsafe().compareAndSwapObject(clazz.getCompanion(), reflectionDataOffset, oldData, newData); + } + + @Substitute + static boolean casAnnotationType(DynamicHub clazz, + AnnotationType oldType, + AnnotationType newType) { + return GraalUnsafeAccess.getUnsafe().compareAndSwapObject(clazz.getCompanion(), annotationTypeOffset, oldType, newType); + } + + @Substitute + static boolean casAnnotationData(DynamicHub clazz, + Target_java_lang_Class_AnnotationData oldData, + Target_java_lang_Class_AnnotationData newData) { + return GraalUnsafeAccess.getUnsafe().compareAndSwapObject(clazz.getCompanion(), annotationDataOffset, oldData, newData); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java index 42594d0c45a1..ef1544e34b35 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java @@ -24,26 +24,19 @@ */ package com.oracle.svm.core.hub; +import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.graalvm.collections.Pair; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import com.oracle.svm.core.hub.DynamicHub.ReflectionData; import com.oracle.svm.core.jdk.ProtectionDomainSupport; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; -import com.oracle.svm.core.reflect.MethodMetadataDecoder.MethodDescriptor; import com.oracle.svm.core.util.VMError; +import sun.reflect.annotation.AnnotationType; +import sun.reflect.generics.repository.ClassRepository; + /** * The mutable parts of a {@link DynamicHub} instance. */ @@ -59,7 +52,12 @@ public final class DynamicHubCompanion { */ private Object classLoader; private ProtectionDomain protectionDomain; - private ReflectionData completeReflectionData; + private ClassRepository genericInfo; + private SoftReference> reflectionData; + private AnnotationType annotationType; + private Target_java_lang_Class_AnnotationData annotationData; + private Constructor cachedConstructor; + private Class newInstanceCallerCache; @Platforms(Platform.HOSTED_ONLY.class) DynamicHubCompanion(Class hostedJavaClass, ClassLoader classLoader) { @@ -100,83 +98,38 @@ void setProtectionDomain(ProtectionDomain domain) { protectionDomain = domain; } - ReflectionData getCompleteReflectionData(DynamicHub hub) { - if (completeReflectionData == null) { - List newDeclaredMethods = new ArrayList<>(Arrays.asList(hub.rd.declaredMethods)); - List newPublicMethods = new ArrayList<>(Arrays.asList(hub.rd.publicMethods)); - List> newDeclaredConstructors = new ArrayList<>(Arrays.asList(hub.rd.declaredConstructors)); - List> newPublicConstructors = new ArrayList<>(Arrays.asList(hub.rd.publicConstructors)); - List newDeclaredPublicMethods = new ArrayList<>(Arrays.asList(hub.rd.declaredPublicMethods)); - - Pair queriedAndHidingMethods = ImageSingletons.lookup(MethodMetadataDecoder.class).getQueriedAndHidingMethods(hub); - Executable[] queriedMethods = queriedAndHidingMethods.getLeft(); - MethodDescriptor[] hidingMethods = queriedAndHidingMethods.getRight(); - newMethods: for (Executable method : queriedMethods) { - if (method instanceof Constructor) { - Constructor c = (Constructor) method; - for (Constructor c2 : hub.rd.declaredConstructors) { - if (Arrays.equals(c.getParameterTypes(), c2.getParameterTypes())) { - continue newMethods; - } - } - newDeclaredConstructors.add(c); - if (Modifier.isPublic(c.getModifiers())) { - newPublicConstructors.add(c); - } - } else { - Method m = (Method) method; - for (Method m2 : hub.rd.declaredMethods) { - if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) { - continue newMethods; - } - } - newDeclaredMethods.add(m); - if (Modifier.isPublic(m.getModifiers())) { - newPublicMethods.add(m); - newDeclaredPublicMethods.add(m); - } - } - } - - /* Recursively add public superclass methods to the public methods list */ - DynamicHub superHub = hub.getSuperHub(); - if (superHub != null) { - addInheritedPublicMethods(newPublicMethods, superHub.privateGetPublicMethods(), hidingMethods, true); - } - for (DynamicHub interfaceHub : hub.getInterfaces()) { - addInheritedPublicMethods(newPublicMethods, interfaceHub.privateGetPublicMethods(), hidingMethods, hub.isInterface()); - } - - completeReflectionData = new ReflectionData(hub.rd.declaredFields, hub.rd.publicFields, hub.rd.publicUnhiddenFields, newDeclaredMethods.toArray(new Method[0]), - newPublicMethods.toArray(new Method[0]), - newDeclaredConstructors.toArray(new Constructor[0]), newPublicConstructors.toArray(new Constructor[0]), hub.rd.nullaryConstructor, hub.rd.declaredPublicFields, - newDeclaredPublicMethods.toArray(new Method[0]), hub.rd.declaredClasses, hub.rd.permittedSubclasses, hub.rd.publicClasses, hub.rd.enclosingMethodOrConstructor, - hub.rd.recordComponents); + public ClassRepository getGenericInfo(DynamicHub hub) { + if (genericInfo == null) { + genericInfo = hub.computeGenericInfo(); } - return completeReflectionData; + return (genericInfo != ClassRepository.NONE) ? genericInfo : null; } - private static void addInheritedPublicMethods(List newPublicMethods, Method[] parentMethods, MethodDescriptor[] hidingMethods, boolean includeStaticMethods) { - parentMethods: for (Method m : parentMethods) { - if (!includeStaticMethods && Modifier.isStatic(m.getModifiers())) { - continue; - } - for (Method m2 : newPublicMethods) { - if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) { - if (m.getDeclaringClass() != m2.getDeclaringClass() && m2.getDeclaringClass().isAssignableFrom(m.getDeclaringClass())) { - /* Need to store the more specific method */ - newPublicMethods.remove(m2); - newPublicMethods.add(m); - } - continue parentMethods; - } - } - for (MethodDescriptor hidingMethod : hidingMethods) { - if (m.getName().equals(hidingMethod.getName()) && Arrays.equals(m.getParameterTypes(), hidingMethod.getParameterTypes())) { - continue parentMethods; - } - } - newPublicMethods.add(m); - } + SoftReference> getReflectionData() { + return reflectionData; + } + + AnnotationType getAnnotationType() { + return annotationType; + } + + Target_java_lang_Class_AnnotationData getAnnotationData() { + return annotationData; + } + + Constructor getCachedConstructor() { + return cachedConstructor; + } + + void setCachedConstructor(Constructor constructor) { + cachedConstructor = constructor; + } + + Class getNewInstanceCallerCache() { + return newInstanceCallerCache; + } + + void setNewInstanceCallerCache(Class constructor) { + newInstanceCallerCache = constructor; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java index 45ab0cc89786..16687d21aae1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java @@ -220,10 +220,6 @@ private static Target_jdk_internal_access_JavaAWTAccess getJavaAWTAccess() { final class Target_jdk_internal_access_JavaAWTAccess { } -@TargetClass(classNameProvider = Package_jdk_internal_access.class, className = "JavaLangAccess") -final class Target_jdk_internal_access_JavaLangAccess { -} - @TargetClass(className = "sun.reflect.misc.MethodUtil") final class Target_sun_reflect_misc_MethodUtil { @Substitute diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java index 3da4274f6beb..b642e0319653 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java @@ -34,7 +34,9 @@ public interface ReadableJavaField extends ResolvedJavaField { static JavaConstant readFieldValue(MetaAccessProvider metaAccess, ConstantReflectionProvider originalConstantReflection, ResolvedJavaField javaField, JavaConstant javaConstant) { if (javaField instanceof ReadableJavaField) { ReadableJavaField readableField = (ReadableJavaField) javaField; - assert readableField.isValueAvailable(); + if (!readableField.isValueAvailable()) { + return SubstrateObjectConstant.forObject(null); + } return readableField.readValue(metaAccess, javaConstant); } else { return originalConstantReflection.readFieldValue(javaField, javaConstant); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java index 4487a6dab020..0d7442fe974a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java @@ -24,41 +24,28 @@ */ package com.oracle.svm.core.reflect; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; - -import org.graalvm.collections.Pair; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import com.oracle.svm.core.hub.DynamicHub; public interface MethodMetadataDecoder { - Pair getQueriedAndHidingMethods(DynamicHub declaringType); - - MethodDescriptor[] getAllReachableMethods(); + Field[] parseFields(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly); - long getMetadataByteLength(); + Method[] parseMethods(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly); - class MethodDescriptor { - private final Class declaringClass; - private final String name; - private final Class[] parameterTypes; + Constructor[] parseConstructors(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly); - public MethodDescriptor(Class declaringClass, String name, Class[] parameterTypes) { - this.declaringClass = declaringClass; - this.name = name; - this.parameterTypes = parameterTypes; - } + Class[] parseClasses(byte[] encoding); - public Class getDeclaringClass() { - return declaringClass; - } + Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, byte[] encoding); - public String getName() { - return name; - } + Parameter[] parseReflectParameters(Executable executable, byte[] encoding); - public Class[] getParameterTypes() { - return parameterTypes; - } - } + Object[] parseEnclosingMethod(byte[] encoding); + boolean isHidingMethod(int modifiers); } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_java_lang_reflect_RecordComponent.java similarity index 59% rename from substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_java_lang_reflect_RecordComponent.java index f5945a6e2989..2e31c3e0ed62 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_java_lang_reflect_RecordComponent.java @@ -22,32 +22,27 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.reflect.target; +package com.oracle.svm.core.reflect; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; +import java.lang.reflect.Method; -import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.jdk.JDK17OrLater; -public class MethodMetadataEncoding { - @UnknownObjectField(types = {byte[].class}) private byte[] methodsEncoding; - @UnknownObjectField(types = {byte[].class}) private byte[] indexEncoding; +@TargetClass(className = "java.lang.reflect.RecordComponent", onlyWith = JDK17OrLater.class) +public final class Target_java_lang_reflect_RecordComponent { + @Alias public Class clazz; - public byte[] getMethodsEncoding() { - return methodsEncoding; - } + @Alias public String name; - @Platforms(Platform.HOSTED_ONLY.class) - public void setMethodsEncoding(byte[] methodsEncoding) { - this.methodsEncoding = methodsEncoding; - } + @Alias public Class type; - public byte[] getIndexEncoding() { - return indexEncoding; - } + @Alias public Method accessor; - @Platforms(Platform.HOSTED_ONLY.class) - public void setIndexEncoding(byte[] indexEncoding) { - this.indexEncoding = indexEncoding; - } + @Alias public String signature; + + @Alias public byte[] annotations; + + @Alias public byte[] typeAnnotations; } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_jdk_internal_reflect_ConstantPool.java similarity index 99% rename from substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_jdk_internal_reflect_ConstantPool.java index 722282ca1dcb..b2d41f496288 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_jdk_internal_reflect_ConstantPool.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.reflect.target; +package com.oracle.svm.core.reflect; import static com.oracle.svm.core.util.VMError.unimplemented; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassNewInstanceFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassNewInstanceFeature.java index e88ab1edcc94..d392a1cb533d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassNewInstanceFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassNewInstanceFeature.java @@ -27,7 +27,6 @@ import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; @AutomaticFeature @@ -36,14 +35,7 @@ public class ClassNewInstanceFeature implements Feature { @Override public void beforeAnalysis(BeforeAnalysisAccess a) { BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) a; - access.registerAsCompiled(Object.class.getDeclaredConstructors()[0], true); - try { - access.registerAsCompiled(DynamicHub.class.getDeclaredMethod("newInstanceInstantiationError", Object.class), true); - access.registerAsCompiled(DynamicHub.class.getDeclaredMethod("newInstanceReachableError", Object.class), true); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index d7629e1e6eb0..f4ec9fdf0ec3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -69,7 +69,6 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.option.HostedOptionValues; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.c.codegen.CCompilerInvoker; import com.oracle.svm.hosted.code.CompileQueue.CompileTask; @@ -106,6 +105,7 @@ public class ProgressReporter { private GCStats lastGCStats = GCStats.getCurrent(); private long numRuntimeCompiledMethods = -1; private long graphEncodingByteLength = 0; + private long metadataByteLength = 0; private int numJNIClasses = -1; private int numJNIFields = -1; private int numJNIMethods = -1; @@ -207,6 +207,10 @@ public void setGraphEncodingByteLength(int value) { graphEncodingByteLength = value; } + public void setMetadataByteLength(int value) { + metadataByteLength = value; + } + public void setJNIInfo(int numClasses, int numFields, int numMethods) { numJNIClasses = numClasses; numJNIFields = numFields; @@ -503,7 +507,6 @@ private Map calculateHeapBreakdown(Collection heapObje classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("code metadata", "#glossary-code-metadata"), codeInfoSize); remainingBytes -= codeInfoSize; } - long metadataByteLength = ImageSingletons.lookup(MethodMetadataDecoder.class).getMetadataByteLength(); if (metadataByteLength > 0) { classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("method metadata", "#glossary-method-metadata"), metadataByteLength); remainingBytes -= metadataByteLength; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java index 0f35bf6d8726..8f648aab2837 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java @@ -24,17 +24,14 @@ */ package com.oracle.svm.hosted.analysis; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedType; import java.lang.reflect.Field; -import java.lang.reflect.MalformedParameterizedTypeException; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import org.graalvm.compiler.debug.GraalError; import org.graalvm.nativeimage.c.function.CFunctionPointer; import com.oracle.graal.pointsto.BigBang; @@ -48,9 +45,7 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.classinitialization.ClassInitializationInfo; -import com.oracle.svm.core.hub.AnnotatedSuperInfo; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.hub.GenericInfo; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.util.VMError; @@ -61,7 +56,6 @@ import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaType; public class DynamicHubInitializer { @@ -71,18 +65,13 @@ public class DynamicHubInitializer { private final UnsupportedFeatures unsupportedFeatures; private final ConstantReflectionProvider constantReflection; - private final Map genericInterfacesMap; - private final Map annotatedInterfacesMap; private final Map interfacesEncodings; private final Field dynamicHubClassInitializationInfoField; private final Field dynamicHubArrayHubField; private final Field dynamicHubEnclosingClassField; private final Field dynamicHubInterfacesEncodingField; - private final Field dynamicHubAnnotationsEncodingField; private final Field dynamicHubAnnotationsEnumConstantsReferenceField; - private final Field dynamicHubAnnotatedSuperInfoField; - private final Field dynamicHubGenericInfoField; public DynamicHubInitializer(BigBang bb) { this.bb = bb; @@ -91,18 +80,13 @@ public DynamicHubInitializer(BigBang bb) { this.unsupportedFeatures = bb.getUnsupportedFeatures(); this.constantReflection = bb.getConstantReflectionProvider(); - this.genericInterfacesMap = new ConcurrentHashMap<>(); - this.annotatedInterfacesMap = new ConcurrentHashMap<>(); this.interfacesEncodings = new ConcurrentHashMap<>(); dynamicHubClassInitializationInfoField = ReflectionUtil.lookupField(DynamicHub.class, "classInitializationInfo"); dynamicHubArrayHubField = ReflectionUtil.lookupField(DynamicHub.class, "arrayHub"); dynamicHubEnclosingClassField = ReflectionUtil.lookupField(DynamicHub.class, "enclosingClass"); dynamicHubInterfacesEncodingField = ReflectionUtil.lookupField(DynamicHub.class, "interfacesEncoding"); - dynamicHubAnnotationsEncodingField = ReflectionUtil.lookupField(DynamicHub.class, "annotationsEncoding"); dynamicHubAnnotationsEnumConstantsReferenceField = ReflectionUtil.lookupField(DynamicHub.class, "enumConstantsReference"); - dynamicHubAnnotatedSuperInfoField = ReflectionUtil.lookupField(DynamicHub.class, "annotatedSuperInfo"); - dynamicHubGenericInfoField = ReflectionUtil.lookupField(DynamicHub.class, "genericInfo"); } public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) { @@ -118,11 +102,8 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) if (hub.getClassInitializationInfo() == null) { buildClassInitializationInfo(heapScanner, type, hub); } - if (hub.getGenericInfo() == null) { - fillGenericInfo(heapScanner, type, hub); - } - if (hub.getAnnotatedSuperInfo() == null) { - fillAnnotatedSuperInfo(heapScanner, type, hub); + if (hub.getSignature() == null) { + fillSignature(type, hub); } if (type.getJavaKind() == JavaKind.Object) { @@ -146,28 +127,6 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) heapScanner.rescanField(hub, dynamicHubInterfacesEncodingField); } - /* - * Support for Java annotations. - * - * The annotation encodings must be updated after each analysis iteration since only the - * annotation types marked as reachable are included. - */ - try { - /* - * Get the annotations from the wrapped type since AnalysisType.getAnnotations() - * defends against JDK-7183985, and we want to get the original behavior. - */ - Annotation[] annotations = type.getWrappedWithoutResolve().getAnnotations(); - Annotation[] declared = type.getWrappedWithoutResolve().getDeclaredAnnotations(); - Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(metaAccess, annotations, declared, hub.getAnnotationsEncoding()); - if (hub.setAnnotationsEncoding(annotationsEncoding)) { - heapScanner.rescanField(hub, dynamicHubAnnotationsEncodingField); - } - } catch (ArrayStoreException e) { - /* If we hit JDK-7183985 just encode the exception. */ - hub.setAnnotationsEncoding(e); - } - /* * Support for Java enumerations. */ @@ -274,172 +233,17 @@ private void registerAsCompiled(AnalysisMethod aMethod) { CompilationInfoSupport.singleton().registerForcedCompilation(aMethod); } - static class GenericInterfacesEncodingKey { - final Type[] interfaces; - - GenericInterfacesEncodingKey(Type[] aInterfaces) { - this.interfaces = aInterfaces; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof GenericInterfacesEncodingKey && Arrays.equals(interfaces, ((GenericInterfacesEncodingKey) obj).interfaces); - } - - @Override - public int hashCode() { - return Arrays.hashCode(interfaces); - } - } - - /** Modified copy of {@link Arrays#equals(Object[], Object[])}. */ - private static boolean shallowEquals(Object[] a, Object[] a2) { - if (a == a2) { - return true; - } else if (a == null || a2 == null) { - return false; - } - int length = a.length; - if (a2.length != length) { - return false; - } - for (int i = 0; i < length; i++) { - /* Modification: use reference equality. */ - if (a[i] != a2[i]) { - return false; - } - } - return true; - } - - /** Modified copy of {@link Arrays#hashCode(Object[])}. */ - private static int shallowHashCode(Object[] a) { - if (a == null) { - return 0; - } - int result = 1; - - for (Object element : a) { - /* Modification: use identity hash code. */ - result = 31 * result + System.identityHashCode(element); - } - return result; - } - - static class AnnotatedInterfacesEncodingKey { - final AnnotatedType[] interfaces; - - AnnotatedInterfacesEncodingKey(AnnotatedType[] aInterfaces) { - this.interfaces = aInterfaces; - } - - /* - * After JDK 11, the implementation of hashCode() and equals() for the implementation - * classes of annotated types can lead to the reification of generic bounds, which can lead - * to TypeNotPresentException when the class path is incomplete. Therefore, we use shallow - * implementations that only depend on the identity hash code and reference equality. This - * is the same behavior as on JDK 8 and JDK 11 anyway. - */ - - @Override - public boolean equals(Object obj) { - return obj instanceof AnnotatedInterfacesEncodingKey && shallowEquals(interfaces, ((AnnotatedInterfacesEncodingKey) obj).interfaces); - } + private static final Method getSignature = ReflectionUtil.lookupMethod(Class.class, "getGenericSignature0"); - @Override - public int hashCode() { - return shallowHashCode(interfaces); - } - } - - private void fillGenericInfo(ImageHeapScanner heapScanner, AnalysisType type, DynamicHub hub) { - Class javaClass = type.getJavaClass(); - - TypeVariable[] typeParameters = javaClass.getTypeParameters(); - - Type[] allGenericInterfaces; - try { - allGenericInterfaces = javaClass.getGenericInterfaces(); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Loading generic interfaces can fail due to missing types. Ignore the exception and - * return an empty array. - */ - allGenericInterfaces = new Type[0]; - } - - Type[] genericInterfaces = Arrays.stream(allGenericInterfaces).filter(this::isTypeAllowed).toArray(Type[]::new); - Type[] cachedGenericInterfaces; - try { - cachedGenericInterfaces = genericInterfacesMap.computeIfAbsent(new GenericInterfacesEncodingKey(genericInterfaces), k -> genericInterfaces); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Computing the hash code of generic interfaces can fail due to missing types. Ignore - * the exception and proceed without caching. De-duplication of generic interfaces is an - * optimization and not necessary for correctness. - */ - cachedGenericInterfaces = genericInterfaces; - } - - Type genericSuperClass; - try { - genericSuperClass = javaClass.getGenericSuperclass(); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Loading the generic super class can fail due to missing types. Ignore the exception - * and return null. - */ - genericSuperClass = null; - } - if (!isTypeAllowed(genericSuperClass)) { - genericSuperClass = null; - } - hub.setGenericInfo(GenericInfo.factory(typeParameters, cachedGenericInterfaces, genericSuperClass)); - heapScanner.rescanField(hub, dynamicHubGenericInfoField); - } - - private void fillAnnotatedSuperInfo(ImageHeapScanner heapScanner, AnalysisType type, DynamicHub hub) { + private static void fillSignature(AnalysisType type, DynamicHub hub) { Class javaClass = type.getJavaClass(); - - AnnotatedType annotatedSuperclass; + String signature; try { - annotatedSuperclass = javaClass.getAnnotatedSuperclass(); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Loading the annotated super class can fail due to missing types. Ignore the exception - * and return null. - */ - annotatedSuperclass = null; - } - if (annotatedSuperclass != null && !isTypeAllowed(annotatedSuperclass.getType())) { - annotatedSuperclass = null; - } - - AnnotatedType[] allAnnotatedInterfaces; - try { - allAnnotatedInterfaces = javaClass.getAnnotatedInterfaces(); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Loading annotated interfaces can fail due to missing types. Ignore the exception and - * return an empty array. - */ - allAnnotatedInterfaces = new AnnotatedType[0]; - } - - AnnotatedType[] annotatedInterfaces = Arrays.stream(allAnnotatedInterfaces) - .filter(ai -> isTypeAllowed(ai.getType())).toArray(AnnotatedType[]::new); - AnnotatedType[] cachedAnnotatedInterfaces = annotatedInterfacesMap.computeIfAbsent( - new AnnotatedInterfacesEncodingKey(annotatedInterfaces), k -> annotatedInterfaces); - hub.setAnnotatedSuperInfo(AnnotatedSuperInfo.factory(annotatedSuperclass, cachedAnnotatedInterfaces)); - heapScanner.rescanField(hub, dynamicHubAnnotatedSuperInfoField); - } - - private boolean isTypeAllowed(Type t) { - if (t instanceof Class) { - Optional resolved = metaAccess.optionalLookupJavaType((Class) t); - return resolved.isPresent() && hostVM.platformSupported(resolved.get()); + signature = (String) getSignature.invoke(javaClass); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); } - return true; + hub.setSignature(signature); } class InterfacesEncodingKey { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java index 09e3d014b293..d74cda5c45be 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java @@ -579,20 +579,6 @@ InitKind computeInitKindAndMaybeInitializeClass(Class clazz, boolean memoize, return existing; } - /* Initialize all annotations because we don't support parsing at run-time. */ - if (clazz.isAnnotation()) { - forceInitializeHosted(clazz, "all annotations are initialized", false); - return InitKind.BUILD_TIME; - } - - /* Well, and enums that got initialized while annotations are parsed. */ - if (clazz.isEnum() && !Unsafe.getUnsafe().shouldBeInitialized(clazz)) { - if (memoize) { - forceInitializeHosted(clazz, "enums referred in annotations must be initialized", false); - } - return InitKind.BUILD_TIME; - } - if (clazz.isPrimitive()) { forceInitializeHosted(clazz, "primitive types are initialized at build time", false); return InitKind.BUILD_TIME; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java index 72326a6e641e..146a034e884c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java @@ -24,12 +24,14 @@ */ package com.oracle.svm.hosted.heap; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.util.function.Consumer; import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import com.oracle.graal.pointsto.ObjectScanner.ScanReason; import com.oracle.graal.pointsto.ObjectScanningObserver; @@ -135,4 +137,14 @@ protected void rescanEconomicMap(EconomicMap map) { } } + + @Override + protected void onObjectReachable(ImageHeapObject imageHeapObject) { + super.onObjectReachable(imageHeapObject); + + Object object = SubstrateObjectConstant.asObject(imageHeapObject.getObject()); + if (object instanceof AccessibleObject) { + ImageSingletons.lookup(RuntimeReflectionSupport.class).registerHeapReflectionObject((AccessibleObject) object); + } + } } 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 fe31a58fe5a3..626b8e6841e5 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 @@ -26,17 +26,24 @@ 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; import java.nio.ByteBuffer; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableMap; +import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ForkJoinPool; import java.util.stream.Collectors; @@ -81,6 +88,7 @@ import com.oracle.svm.hosted.code.CompilationInfoSupport.DeoptSourceFrameInfo; import com.oracle.svm.hosted.code.HostedImageHeapConstantPatch; import com.oracle.svm.hosted.image.NativeImage.NativeTextSectionImpl; +import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; @@ -230,13 +238,42 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code } MethodMetadataEncoder methodMetadataEncoder = ImageSingletons.lookup(MethodMetadataEncoderFactory.class).create(encoders); - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - for (Executable queriedMethod : ImageSingletons.lookup(RuntimeReflectionSupport.class).getQueriedOnlyMethods()) { - HostedMethod method = imageHeap.getMetaAccess().lookupJavaMethod(queriedMethod); - methodMetadataEncoder.addReflectionMethodMetadata(imageHeap.getMetaAccess(), method, queriedMethod); + for (HostedType type : imageHeap.getUniverse().getTypes()) { + Map, Set>> innerClasses = ImageSingletons.lookup(RuntimeReflectionSupport.class).getReflectionInnerClasses(); + if (type.getWrapped().isReachable()) { + methodMetadataEncoder.addClassMetadata(imageHeap.getMetaAccess(), type, innerClasses.getOrDefault(type.getJavaClass(), Collections.emptySet()).toArray(new Class[0])); + } + } + Set includedFields = new HashSet<>(); + Set includedMethods = new HashSet<>(); + for (AccessibleObject object : ImageSingletons.lookup(RuntimeReflectionSupport.class).getHeapReflectionObjects()) { + if (object instanceof Field) { + includedFields.add(imageHeap.getMetaAccess().lookupJavaField((Field) object)); + } + if (object instanceof Method || object instanceof Constructor) { + includedMethods.add(imageHeap.getMetaAccess().lookupJavaMethod((Executable) object)); + } + methodMetadataEncoder.addHeapObjectMetadata(imageHeap.getMetaAccess(), object); + } + for (Field reflectField : ImageSingletons.lookup(RuntimeReflectionSupport.class).getReflectionFields()) { + HostedField field = imageHeap.getMetaAccess().lookupJavaField(reflectField); + if (!includedFields.contains(field)) { + methodMetadataEncoder.addReflectionFieldMetadata(imageHeap.getMetaAccess(), field, reflectField); + includedFields.add(field); } - for (Object method : ImageSingletons.lookup(RuntimeReflectionSupport.class).getHidingMethods()) { - AnalysisMethod hidingMethod = (AnalysisMethod) method; + } + for (Executable reflectMethod : ImageSingletons.lookup(RuntimeReflectionSupport.class).getReflectionExecutables()) { + HostedMethod method = imageHeap.getMetaAccess().lookupJavaMethod(reflectMethod); + if (!includedMethods.contains(method)) { + Object accessor = ImageSingletons.lookup(RuntimeReflectionSupport.class).getAccessor(reflectMethod); + methodMetadataEncoder.addReflectionExecutableMetadata(imageHeap.getMetaAccess(), method, reflectMethod, accessor); + includedMethods.add(method); + } + } + for (Object method : ImageSingletons.lookup(RuntimeReflectionSupport.class).getHidingReflectionMethods()) { + AnalysisMethod hidingMethod = (AnalysisMethod) method; + HostedMethod hostedMethod = imageHeap.getUniverse().optionalLookup(hidingMethod); + if (hostedMethod == null || !includedMethods.contains(hostedMethod)) { HostedType declaringType = imageHeap.getUniverse().lookup(hidingMethod.getDeclaringClass()); String name = hidingMethod.getName(); JavaType[] analysisParameterTypes = hidingMethod.getSignature().toParameterTypes(null); @@ -244,12 +281,22 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code for (int i = 0; i < analysisParameterTypes.length; ++i) { parameterTypes[i] = imageHeap.getUniverse().lookup(analysisParameterTypes[i]); } - methodMetadataEncoder.addHidingMethodMetadata(declaringType, name, parameterTypes); + int modifiers = hidingMethod.getModifiers(); + HostedType returnType = imageHeap.getUniverse().lookup(hidingMethod.getSignature().getReturnType(null)); + methodMetadataEncoder.addHidingMethodMetadata(declaringType, name, parameterTypes, modifiers, returnType); + if (hostedMethod != null) { + includedMethods.add(hostedMethod); + } } } if (SubstrateOptions.IncludeMethodData.getValue()) { + for (HostedField field : imageHeap.getUniverse().getFields()) { + if (field.isAccessed() && !includedFields.contains(field)) { + methodMetadataEncoder.addReachableFieldMetadata(field); + } + } for (HostedMethod method : imageHeap.getUniverse().getMethods()) { - if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod()) { + if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod() && !includedMethods.contains(method)) { methodMetadataEncoder.addReachableMethodMetadata(method); } } @@ -555,13 +602,31 @@ protected boolean isDeoptEntry(ResolvedJavaMethod method, Infopoint infopoint) { } public interface MethodMetadataEncoder { - void addReflectionMethodMetadata(MetaAccessProvider metaAccess, HostedMethod sharedMethod, Executable reflectMethod); + void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Class[] reflectionClasses); + + void addReflectionFieldMetadata(MetaAccessProvider metaAccess, HostedField sharedField, Field reflectField); + + void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, HostedMethod sharedMethod, Executable reflectMethod, Object accessor); + + void addHeapObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object); + + void addHidingMethodMetadata(HostedType declType, String name, HostedType[] paramTypes, int modifiers, HostedType returnType); - void addHidingMethodMetadata(HostedType declType, String name, HostedType[] paramTypes); + void addReachableFieldMetadata(HostedField field); void addReachableMethodMetadata(HostedMethod method); void encodeAllAndInstall(); + + byte[] getAnnotationsEncoding(AccessibleObject object); + + byte[] getParameterAnnotationsEncoding(Executable object); + + byte[] getAnnotationDefaultEncoding(Method object); + + byte[] getTypeAnnotationsEncoding(AccessibleObject object); + + byte[] getReflectParametersEncoding(Executable object); } public interface MethodMetadataEncoderFactory { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java index 3cada6d5aee3..6f1cd9289eb1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java @@ -900,7 +900,9 @@ private boolean tryAutomaticRecomputation(ResolvedJavaField field, Kind kind, Su if (substitutionField instanceof ComputedValueField) { ComputedValueField computedSubstitutionField = (ComputedValueField) substitutionField; if (computedSubstitutionField.getRecomputeValueKind().equals(kind)) { - reportUnnecessarySubstitution(substitutionField, computedSubstitutionField); + if (computedSubstitutionField.getTargetField().equals(computedSubstitutionField.getJavaField())) { + reportUnnecessarySubstitution(substitutionField, computedSubstitutionField); + } return false; } else if (computedSubstitutionField.getRecomputeValueKind().equals(Kind.None)) { /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java deleted file mode 100644 index da61ae7c6f93..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, 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.reflect.hosted; - -import java.lang.annotation.Annotation; - -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.hosted.Feature; - -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; -import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; -import com.oracle.svm.hosted.meta.HostedType; -import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl; -import com.oracle.svm.reflect.target.MethodMetadataEncoding; - -import sun.reflect.annotation.TypeAnnotation; - -@AutomaticFeature -class MethodMetadataFeature implements Feature { - @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(MethodMetadataEncoderFactory.class, new MethodMetadataEncoderImpl.Factory()); - ImageSingletons.add(MethodMetadataDecoder.class, new MethodMetadataDecoderImpl()); - ImageSingletons.add(MethodMetadataEncoding.class, new MethodMetadataEncoding()); - } -} - -public class MethodMetadata { - final HostedType declaringType; - final String name; - final HostedType[] parameterTypes; - - MethodMetadata(HostedType declaringType, String name, HostedType[] parameterTypes) { - this.declaringType = declaringType; - this.name = name; - this.parameterTypes = parameterTypes; - } - - static class ReflectionMethodMetadata extends MethodMetadata { - final int modifiers; - final HostedType returnType; - final HostedType[] exceptionTypes; - final String signature; - final Annotation[] annotations; - final Annotation[][] parameterAnnotations; - final TypeAnnotation[] typeAnnotations; - final boolean hasRealParameterData; - final MethodMetadataDecoderImpl.ReflectParameterDescriptor[] reflectParameters; - - ReflectionMethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, HostedType[] exceptionTypes, String signature, - Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, boolean hasRealParameterData, - MethodMetadataDecoderImpl.ReflectParameterDescriptor[] reflectParameters) { - super(declaringClass, name, parameterTypes); - this.modifiers = modifiers; - this.returnType = returnType; - this.exceptionTypes = exceptionTypes; - this.signature = signature; - this.annotations = annotations; - this.parameterAnnotations = parameterAnnotations; - this.typeAnnotations = typeAnnotations; - this.hasRealParameterData = hasRealParameterData; - this.reflectParameters = reflectParameters; - } - } - -} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java index 276a118393d9..7fafb0e45ed3 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java @@ -24,7 +24,13 @@ */ package com.oracle.svm.reflect.hosted; +import static com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.COMPLETE_FLAG_MASK; +import static com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.HIDING_FLAG_MASK; +import static com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.IN_HEAP_FLAG_MASK; +import static com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.NULL_OBJECT; + import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; @@ -32,6 +38,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; +import java.lang.reflect.Proxy; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -39,35 +46,56 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; +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.debug.GraalError; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.util.GuardedAnnotationAccess; -import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.code.CodeInfoEncoder; -import com.oracle.svm.core.hub.DynamicHubSupport; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.meta.SharedField; +import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.ByteArrayReader; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.ProgressReporter; import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoder; import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; +import com.oracle.svm.hosted.meta.HostedField; +import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; -import com.oracle.svm.reflect.hosted.MethodMetadata.ReflectionMethodMetadata; +import com.oracle.svm.hosted.substitute.DeletedElementException; +import com.oracle.svm.reflect.hosted.ReflectionMetadata.AccessibleObjectMetadata; +import com.oracle.svm.reflect.hosted.ReflectionMetadata.ClassMetadata; +import com.oracle.svm.reflect.hosted.ReflectionMetadata.ConstructorMetadata; +import com.oracle.svm.reflect.hosted.ReflectionMetadata.ExecutableMetadata; +import com.oracle.svm.reflect.hosted.ReflectionMetadata.FieldMetadata; +import com.oracle.svm.reflect.hosted.ReflectionMetadata.MethodMetadata; +import com.oracle.svm.reflect.hosted.ReflectionMetadata.RecordComponentMetadata; +import com.oracle.svm.reflect.hosted.ReflectionMetadata.ReflectParameterMetadata; import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl; -import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.ReflectParameterDescriptor; -import com.oracle.svm.reflect.target.MethodMetadataEncoding; -import com.oracle.svm.reflect.target.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.reflect.target.Target_sun_reflect_annotation_AnnotationParser; import com.oracle.svm.util.ReflectionUtil; +import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.MetaAccessProvider; import sun.invoke.util.Wrapper; import sun.reflect.annotation.AnnotationType; +import sun.reflect.annotation.EnumConstantNotPresentExceptionProxy; +import sun.reflect.annotation.ExceptionProxy; import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; +import sun.reflect.annotation.TypeNotPresentExceptionProxy; /** * The method metadata encoding puts data in the image for three distinct types of methods. @@ -84,7 +112,7 @@ * * Emitting the metadata happens in two phases. In the first phase, the string and class encoders * are filled with the necessary values (in the {@code add*MethodMetadata} functions). In a second - * phase, the values are encoded as byte arrays and stored in {@link MethodMetadataEncoding} (see + * phase, the values are encoded as byte arrays and stored in {@link DynamicHub} arrays (see * {@link #encodeAllAndInstall()}). */ public class MethodMetadataEncoderImpl implements MethodMetadataEncoder { @@ -97,33 +125,164 @@ public MethodMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { } private final CodeInfoEncoder.Encoders encoders; - private final TreeSet sortedTypes; - private Map> queriedMethodData; - private Map> reachableMethodData; - private Map> hidingMethodData; + private final Map, JavaConstant> annotationExceptionProxies = new HashMap<>(); + private final TreeSet sortedTypes = new TreeSet<>(Comparator.comparingLong(t -> t.getHub().getTypeID())); + private final Map classData = new HashMap<>(); + private final Map> fieldData = new HashMap<>(); + private final Map> methodData = new HashMap<>(); + private final Map> constructorData = new HashMap<>(); + + private final Set heapData = new HashSet<>(); - private byte[] methodDataEncoding; - private byte[] methodDataIndexEncoding; + private final Map annotationsEncodings = new HashMap<>(); + private final Map parameterAnnotationsEncodings = new HashMap<>(); + private final Map annotationDefaultEncodings = new HashMap<>(); + private final Map typeAnnotationsEncodings = new HashMap<>(); + private final Map reflectParametersEncodings = new HashMap<>(); public MethodMetadataEncoderImpl(CodeInfoEncoder.Encoders encoders) { this.encoders = encoders; - this.sortedTypes = new TreeSet<>(Comparator.comparingLong(t -> t.getHub().getTypeID())); - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - this.queriedMethodData = new HashMap<>(); - this.hidingMethodData = new HashMap<>(); + } + + private void registerClass(HostedType type, ClassMetadata metadata) { + sortedTypes.add(type); + classData.put(type, metadata); + } + + private void registerField(HostedType declaringType, FieldMetadata metadata) { + sortedTypes.add(declaringType); + fieldData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(metadata); + } + + private void registerMethod(HostedType declaringType, MethodMetadata metadata) { + sortedTypes.add(declaringType); + methodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(metadata); + } + + private void registerConstructor(HostedType declaringType, ConstructorMetadata metadata) { + sortedTypes.add(declaringType); + constructorData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(metadata); + } + + @Override + public byte[] getAnnotationsEncoding(AccessibleObject object) { + return annotationsEncodings.get(object); + } + + @Override + public byte[] getParameterAnnotationsEncoding(Executable object) { + return parameterAnnotationsEncodings.get(object); + } + + @Override + public byte[] getAnnotationDefaultEncoding(Method object) { + return annotationDefaultEncodings.get(object); + } + + @Override + public byte[] getTypeAnnotationsEncoding(AccessibleObject object) { + return typeAnnotationsEncodings.get(object); + } + + @Override + public byte[] getReflectParametersEncoding(Executable object) { + return reflectParametersEncodings.get(object); + } + + @Override + public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Class[] innerClasses) { + Class javaClass = type.getHub().getHostedJavaClass(); + Object[] enclosingMethodInfo = getEnclosingMethodInfo(javaClass); + HostedType enclosingMethodDeclaringClass = enclosingMethodInfo != null ? ((HostedMetaAccess) metaAccess).lookupJavaType((Class) enclosingMethodInfo[0]) : null; + String enclosingMethodName = enclosingMethodInfo != null ? (String) enclosingMethodInfo[1] : null; + String enclosingMethodDescriptor = enclosingMethodInfo != null ? (String) enclosingMethodInfo[2] : null; + RecordComponentMetadata[] recordComponents = getRecordComponents(metaAccess, type, javaClass); + Class[] permittedSubclasses = getPermittedSubclasses(metaAccess, javaClass); + Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(type); + TypeAnnotation[] typeAnnotations = getTypeAnnotations(javaClass); + + /* Register string and class values in annotations */ + if (enclosingMethodInfo != null) { + encoders.sourceClasses.addObject(enclosingMethodDeclaringClass.getJavaClass()); + encoders.sourceMethodNames.addObject(enclosingMethodName); + encoders.sourceMethodNames.addObject(enclosingMethodDescriptor); } - if (SubstrateOptions.IncludeMethodData.getValue()) { - this.reachableMethodData = new HashMap<>(); + HostedType[] innerTypes = registerClassValues(metaAccess, innerClasses); + HostedType[] permittedSubtypes = (permittedSubclasses != null) ? registerClassValues(metaAccess, permittedSubclasses) : null; + annotations = registerAnnotationValues(metaAccess, annotations); + typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + + registerClass(type, new ClassMetadata(innerTypes, enclosingMethodDeclaringClass, enclosingMethodName, enclosingMethodDescriptor, recordComponents, permittedSubtypes, annotations, + typeAnnotations)); + } + + private static final Method getEnclosingMethodInfo = ReflectionUtil.lookupMethod(Class.class, "getEnclosingMethod0"); + + private static Object[] getEnclosingMethodInfo(Class clazz) { + try { + return (Object[]) getEnclosingMethodInfo.invoke(clazz); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); } } - private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); - private static final Method hasRealParameterData = ReflectionUtil.lookupMethod(Executable.class, "hasRealParameterData"); + private static final Method getPermittedSubclasses = ReflectionUtil.lookupMethod(true, Class.class, "getPermittedSubclasses"); + + private static Class[] getPermittedSubclasses(MetaAccessProvider metaAccess, Class clazz) { + if (JavaVersionUtil.JAVA_SPEC < 17) { + return null; + } + try { + Class[] permittedSubclasses = (Class[]) getPermittedSubclasses.invoke(clazz); + if (permittedSubclasses == null) { + return null; + } + Set> reachablePermittedSubclasses = new HashSet<>(); + for (Class permittedSubclass : permittedSubclasses) { + HostedType hostedType = ((HostedMetaAccess) metaAccess).optionalLookupJavaType(permittedSubclass).orElse(null); + if (hostedType != null && hostedType.getWrapped().isReachable()) { + reachablePermittedSubclasses.add(permittedSubclass); + } + } + return reachablePermittedSubclasses.toArray(new Class[0]); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } @Override - public void addReflectionMethodMetadata(MetaAccessProvider metaAccess, HostedMethod hostedMethod, Executable reflectMethod) { + public void addReflectionFieldMetadata(MetaAccessProvider metaAccess, HostedField hostedField, Field reflectField) { + HostedType declaringType = hostedField.getDeclaringClass(); + String name = hostedField.getName(); + HostedType type = hostedField.getType(); + /* Reflect method because substitution of Object.hashCode() is private */ + int modifiers = reflectField.getModifiers(); + boolean trustedFinal = isTrustedFinal(reflectField); + String signature = getSignature(reflectField); + Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(hostedField); + TypeAnnotation[] typeAnnotations = getTypeAnnotations(reflectField); + int offset = hostedField.wrapped.isUnsafeAccessed() ? hostedField.getOffset() : SharedField.LOC_UNINITIALIZED; + Delete deleteAnnotation = GuardedAnnotationAccess.getAnnotation(hostedField, Delete.class); + String deletedReason = (deleteAnnotation != null) ? deleteAnnotation.value() : null; + + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + encoders.sourceClasses.addObject(type.getJavaClass()); + encoders.sourceMethodNames.addObject(signature); + encoders.sourceMethodNames.addObject(deletedReason); + /* Register string and class values in annotations */ + annotations = registerAnnotationValues(metaAccess, annotations); + typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + + registerField(declaringType, new FieldMetadata(declaringType, name, type, modifiers, trustedFinal, signature, + annotations, typeAnnotations, offset, deletedReason)); + } + + @Override + public void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, HostedMethod hostedMethod, Executable reflectMethod, Object accessor) { + boolean isMethod = !hostedMethod.isConstructor(); HostedType declaringType = hostedMethod.getDeclaringClass(); - String name = hostedMethod.isConstructor() ? "" : hostedMethod.getName(); + String name = isMethod ? hostedMethod.getName() : null; HostedType[] parameterTypes = getParameterTypes(hostedMethod); /* Reflect method because substitution of Object.hashCode() is private */ int modifiers = reflectMethod.getModifiers(); @@ -132,50 +291,179 @@ public void addReflectionMethodMetadata(MetaAccessProvider metaAccess, HostedMet String signature = getSignature(reflectMethod); Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(hostedMethod); Annotation[][] parameterAnnotations = reflectMethod.getParameterAnnotations(); - TypeAnnotation[] typeAnnotations; - boolean reflectParameterDataPresent; - try { - typeAnnotations = (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, reflectMethod); - reflectParameterDataPresent = (boolean) hasRealParameterData.invoke(reflectMethod); - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); - } - ReflectParameterDescriptor[] reflectParameterDescriptors = reflectParameterDataPresent ? getReflectParameters(reflectMethod) : new ReflectParameterDescriptor[0]; + Object annotationDefault = isMethod ? ((Method) reflectMethod).getDefaultValue() : null; + TypeAnnotation[] typeAnnotations = getTypeAnnotations(reflectMethod); + ReflectParameterMetadata[] reflectParameters = getReflectParameters(reflectMethod); /* Fill encoders with the necessary values. */ - encoders.sourceMethodNames.addObject(name); + if (isMethod) { + encoders.sourceMethodNames.addObject(name); + encoders.sourceClasses.addObject(returnType.getJavaClass()); + } for (HostedType parameterType : parameterTypes) { encoders.sourceClasses.addObject(parameterType.getJavaClass()); } - encoders.sourceClasses.addObject(returnType.getJavaClass()); for (HostedType exceptionType : exceptionTypes) { encoders.sourceClasses.addObject(exceptionType.getJavaClass()); } encoders.sourceMethodNames.addObject(signature); /* Register string and class values in annotations */ - registerAnnotationValues(annotations); - for (Annotation[] parameterAnnotation : parameterAnnotations) { - registerAnnotationValues(parameterAnnotation); + annotations = registerAnnotationValues(metaAccess, annotations); + for (int i = 0; i < parameterAnnotations.length; ++i) { + parameterAnnotations[i] = registerAnnotationValues(metaAccess, parameterAnnotations[i]); } - for (TypeAnnotation typeAnnotation : typeAnnotations) { - // Checkstyle: allow direct annotation access - registerAnnotationValues(typeAnnotation.getAnnotation()); - // Checkstyle: disallow direct annotation access + if (isMethod && annotationDefault != null) { + registerAnnotationValue(annotationDefault.getClass(), annotationDefault); + } + typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + if (reflectParameters != null) { + for (ReflectParameterMetadata parameter : reflectParameters) { + encoders.sourceMethodNames.addObject(parameter.name); + } } - for (ReflectParameterDescriptor parameter : reflectParameterDescriptors) { - encoders.sourceMethodNames.addObject(parameter.getName()); + JavaConstant accessorConstant = null; + if (accessor != null) { + accessorConstant = SubstrateObjectConstant.forObject(accessor); + encoders.objectConstants.addObject(accessorConstant); } - sortedTypes.add(declaringType); - queriedMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(new ReflectionMethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, - annotations, parameterAnnotations, typeAnnotations, reflectParameterDataPresent, reflectParameterDescriptors)); + if (isMethod) { + registerMethod(declaringType, new MethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, + annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, accessorConstant)); + } else { + registerConstructor(declaringType, new ConstructorMetadata(declaringType, parameterTypes, modifiers, exceptionTypes, signature, annotations, + parameterAnnotations, typeAnnotations, reflectParameters, accessorConstant)); + } + } + + private static final Method isFieldTrustedFinal = ReflectionUtil.lookupMethod(true, Field.class, "isTrustedFinal"); + + private static boolean isTrustedFinal(Field field) { + if (JavaVersionUtil.JAVA_SPEC < 17) { + return false; + } + try { + return (boolean) isFieldTrustedFinal.invoke(field); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + @Override + public void addHeapObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object) { + boolean isExecutable = object instanceof Executable; + boolean isMethod = object instanceof Method; + Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(object); + Annotation[][] parameterAnnotations = isExecutable ? ((Executable) object).getParameterAnnotations() : null; + Object annotationDefault = isMethod ? ((Method) object).getDefaultValue() : null; + TypeAnnotation[] typeAnnotations = getTypeAnnotations(object); + ReflectParameterMetadata[] reflectParameters = isExecutable ? getReflectParameters((Executable) object) : null; + + /* Register string and class values in annotations */ + annotations = registerAnnotationValues(metaAccess, annotations); + typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + if (isExecutable) { + for (int i = 0; i < parameterAnnotations.length; ++i) { + parameterAnnotations[i] = registerAnnotationValues(metaAccess, parameterAnnotations[i]); + } + if (isMethod && annotationDefault != null) { + registerAnnotationValue(annotationDefault.getClass(), annotationDefault); + } + if (reflectParameters != null) { + for (ReflectParameterMetadata parameter : reflectParameters) { + encoders.sourceMethodNames.addObject(parameter.name); + } + } + } + JavaConstant heapObjectConstant = SubstrateObjectConstant.forObject(getHolder(object)); + encoders.objectConstants.addObject(heapObjectConstant); + + AccessibleObjectMetadata metadata; + if (isMethod) { + metadata = new MethodMetadata(heapObjectConstant, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters); + registerMethod((HostedType) metaAccess.lookupJavaType(((Method) object).getDeclaringClass()), (MethodMetadata) metadata); + } else if (isExecutable) { + metadata = new ConstructorMetadata(heapObjectConstant, annotations, parameterAnnotations, typeAnnotations, reflectParameters); + registerConstructor((HostedType) metaAccess.lookupJavaType(((Constructor) object).getDeclaringClass()), (ConstructorMetadata) metadata); + } else { + metadata = new FieldMetadata(heapObjectConstant, annotations, typeAnnotations); + registerField((HostedType) metaAccess.lookupJavaType(((Field) object).getDeclaringClass()), (FieldMetadata) metadata); + } + heapData.add(metadata); + } + + private static final Method getRoot = ReflectionUtil.lookupMethod(AccessibleObject.class, "getRoot"); + + private static AccessibleObject getHolder(AccessibleObject accessibleObject) { + try { + AccessibleObject root = (AccessibleObject) getRoot.invoke(accessibleObject); + return root == null ? accessibleObject : root; + } catch (InvocationTargetException | IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); + + static TypeAnnotation[] getTypeAnnotations(AnnotatedElement annotatedElement) { + try { + return (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, annotatedElement); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private HostedType[] registerClassValues(MetaAccessProvider metaAccess, Class[] classes) { + Set includedClasses = new HashSet<>(); + for (Class clazz : classes) { + HostedType type; + try { + type = ((HostedMetaAccess) metaAccess).optionalLookupJavaType(clazz).orElse(null); + } catch (DeletedElementException e) { + type = null; + } + if (type != null && type.getWrapped().isReachable()) { + encoders.sourceClasses.addObject(type.getJavaClass()); + includedClasses.add(type); + } + } + return includedClasses.toArray(new HostedType[0]); } - private void registerAnnotationValues(Annotation... annotations) { + private Annotation[] registerAnnotationValues(MetaAccessProvider metaAccess, Annotation... annotations) { + Set includedAnnotations = new HashSet<>(); for (Annotation annotation : annotations) { + if (registerAnnotation(metaAccess, annotation)) { + includedAnnotations.add(annotation); + } + } + return includedAnnotations.toArray(new Annotation[0]); + } + + private TypeAnnotation[] registerTypeAnnotationValues(MetaAccessProvider metaAccess, TypeAnnotation... typeAnnotations) { + Set includedTypeAnnotations = new HashSet<>(); + for (TypeAnnotation typeAnnotation : typeAnnotations) { + // Checkstyle: allow direct annotation access + Annotation annotation = typeAnnotation.getAnnotation(); + // Checkstyle: disallow direct annotation access + if (registerAnnotation(metaAccess, annotation)) { + includedTypeAnnotations.add(typeAnnotation); + } + } + return includedTypeAnnotations.toArray(new TypeAnnotation[0]); + } + + private boolean registerAnnotation(MetaAccessProvider metaAccess, Annotation annotation) { + /* + * Only include annotations types that have a chance to be queried at runtime. + */ + HostedType annotationType = ((HostedMetaAccess) metaAccess).optionalLookupJavaType(annotation.annotationType()).orElse(null); + if (annotationType != null && annotationType.getWrapped().isReachable()) { encoders.sourceClasses.addObject(annotation.annotationType()); registerAnnotationValue(annotation.annotationType(), annotation); + return true; } + return false; } @SuppressWarnings("unchecked") @@ -193,10 +481,23 @@ private void registerAnnotationValue(Class type, Object value) { Object annotationValue; try { annotationValue = getAnnotationValue.invoke(annotation); - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); + registerAnnotationValue(valueType, annotationValue); + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + ExceptionProxy exceptionProxy; + if (targetException instanceof TypeNotPresentException) { + exceptionProxy = new TypeNotPresentExceptionProxy(((TypeNotPresentException) targetException).typeName(), targetException.getCause()); + } else if (targetException instanceof EnumConstantNotPresentException) { + EnumConstantNotPresentException enumException = (EnumConstantNotPresentException) targetException; + exceptionProxy = new EnumConstantNotPresentExceptionProxy((Class>) enumException.enumType(), enumException.constantName()); + } else { + throw GraalError.shouldNotReachHere(e); + } + JavaConstant javaConstant = annotationExceptionProxies.computeIfAbsent(Pair.create(annotation, valueName), (ignored) -> SubstrateObjectConstant.forObject(exceptionProxy)); + encoders.objectConstants.addObject(javaConstant); } - registerAnnotationValue(valueType, annotationValue); } } else if (type.isArray()) { Class componentType = type.getComponentType(); @@ -216,35 +517,50 @@ private void registerAnnotationValue(Class type, Object value) { } @Override - public void addHidingMethodMetadata(HostedType declaringType, String name, HostedType[] parameterTypes) { + public void addHidingMethodMetadata(HostedType declaringType, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType) { /* Fill encoders with the necessary values. */ encoders.sourceMethodNames.addObject(name); for (HostedType parameterType : parameterTypes) { encoders.sourceClasses.addObject(parameterType.getJavaClass()); } + encoders.sourceClasses.addObject(returnType.getJavaClass()); sortedTypes.add(declaringType); - hidingMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(new MethodMetadata(declaringType, name, parameterTypes)); + registerMethod(declaringType, new MethodMetadata(true, declaringType, name, parameterTypes, modifiers, returnType)); + } + + @Override + public void addReachableFieldMetadata(HostedField field) { + HostedType declaringType = field.getDeclaringClass(); + String name = field.getName(); + + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + + registerField(declaringType, new FieldMetadata(declaringType, name)); } @Override public void addReachableMethodMetadata(HostedMethod method) { + boolean isMethod = !method.isConstructor(); HostedType declaringType = method.getDeclaringClass(); - String name = method.getName(); + String name = isMethod ? method.getName() : null; HostedType[] parameterTypes = getParameterTypes(method); + int modifiers = method.getModifiers(); /* Fill encoders with the necessary values. */ - encoders.sourceMethodNames.addObject(method.getName()); + if (isMethod) { + encoders.sourceMethodNames.addObject(name); + } for (HostedType parameterType : parameterTypes) { encoders.sourceClasses.addObject(parameterType.getJavaClass()); } - sortedTypes.add(declaringType); - reachableMethodData.computeIfAbsent(declaringType, t -> { - /* The declaring class is encoded once for all methods */ - encoders.sourceClasses.addObject(declaringType.getJavaClass()); - return new HashSet<>(); - }).add(new MethodMetadata(declaringType, name, parameterTypes)); + if (isMethod) { + registerMethod(declaringType, new MethodMetadata(false, declaringType, name, parameterTypes, modifiers, null)); + } else { + registerConstructor(declaringType, new ConstructorMetadata(declaringType, parameterTypes, modifiers)); + } } private static HostedType[] getParameterTypes(HostedMethod method) { @@ -264,94 +580,224 @@ private static HostedType[] getExceptionTypes(MetaAccessProvider metaAccess, Exe return exceptionTypes; } - private static ReflectParameterDescriptor[] getReflectParameters(Executable reflectMethod) { - Parameter[] reflectParameters = reflectMethod.getParameters(); - ReflectParameterDescriptor[] reflectParameterDescriptors = new ReflectParameterDescriptor[reflectParameters.length]; - for (int i = 0; i < reflectParameters.length; ++i) { - reflectParameterDescriptors[i] = new ReflectParameterDescriptor(reflectParameters[i].getName(), reflectParameters[i].getModifiers()); + private static ReflectParameterMetadata[] getReflectParameters(Executable reflectMethod) { + Parameter[] rawParameters = getRawParameters(reflectMethod); + if (rawParameters == null) { + return null; + } + ReflectParameterMetadata[] reflectParameters = new ReflectParameterMetadata[rawParameters.length]; + for (int i = 0; i < rawParameters.length; ++i) { + reflectParameters[i] = new ReflectParameterMetadata(rawParameters[i].getName(), rawParameters[i].getModifiers()); } - return reflectParameterDescriptors; + return reflectParameters; } - @Override - public void encodeAllAndInstall() { - encodeMethodMetadata(); - ImageSingletons.lookup(MethodMetadataEncoding.class).setMethodsEncoding(methodDataEncoding); - ImageSingletons.lookup(MethodMetadataEncoding.class).setIndexEncoding(methodDataIndexEncoding); + private RecordComponentMetadata[] getRecordComponents(MetaAccessProvider metaAccess, HostedType declaringType, Class clazz) { + Object[] recordComponents = ImageSingletons.lookup(RuntimeReflectionSupport.class).getRecordComponents(clazz); + if (recordComponents == null) { + return null; + } + Set metadata = new HashSet<>(); + for (Object recordComponent : recordComponents) { + String name = getRecordComponentName(recordComponent); + HostedType type = (HostedType) metaAccess.lookupJavaType(getRecordComponentType(recordComponent)); + String signature = getRecordComponentSignature(recordComponent); + Method accessor = getRecordComponentAccessor(recordComponent); + Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations((AnnotatedElement) recordComponent); + TypeAnnotation[] typeAnnotations = getTypeAnnotations((AnnotatedElement) recordComponent); + + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + encoders.sourceClasses.addObject(type.getJavaClass()); + encoders.sourceMethodNames.addObject(signature); + /* Register string and class values in annotations */ + annotations = registerAnnotationValues(metaAccess, annotations); + typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + JavaConstant accessorConstant = null; + if (accessor != null) { + accessorConstant = SubstrateObjectConstant.forObject(accessor); + encoders.objectConstants.addObject(accessorConstant); + } + + metadata.add(new RecordComponentMetadata(declaringType, name, type, signature, accessorConstant, annotations, typeAnnotations)); + } + return metadata.toArray(new RecordComponentMetadata[0]); + } + + private static final Class recordComponentClass; + + static { + try { + recordComponentClass = (JavaVersionUtil.JAVA_SPEC >= 17) ? Class.forName("java.lang.reflect.RecordComponent") : null; + } catch (ClassNotFoundException e) { + throw VMError.shouldNotReachHere(e); + } + } + + private static final Method getRecordComponentName = (JavaVersionUtil.JAVA_SPEC >= 17) ? ReflectionUtil.lookupMethod(recordComponentClass, "getName") : null; + private static final Method getRecordComponentType = (JavaVersionUtil.JAVA_SPEC >= 17) ? ReflectionUtil.lookupMethod(recordComponentClass, "getType") : null; + private static final Method getRecordComponentSignature = (JavaVersionUtil.JAVA_SPEC >= 17) ? ReflectionUtil.lookupMethod(recordComponentClass, "getGenericSignature") : null; + private static final Method getRecordComponentAccessor = (JavaVersionUtil.JAVA_SPEC >= 17) ? ReflectionUtil.lookupMethod(recordComponentClass, "getAccessor") : null; + + private static String getRecordComponentName(Object recordComponent) { + try { + return (String) getRecordComponentName.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static Class getRecordComponentType(Object recordComponent) { + try { + return (Class) getRecordComponentType.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static String getRecordComponentSignature(Object recordComponent) { + try { + return (String) getRecordComponentSignature.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static Method getRecordComponentAccessor(Object recordComponent) { + try { + return (Method) getRecordComponentAccessor.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } } /** * See {@link MethodMetadataDecoderImpl} for the encoding format description. */ - private void encodeMethodMetadata() { - UnsafeArrayTypeWriter dataEncodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - UnsafeArrayTypeWriter indexEncodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - - long nextTypeId = 0; + @Override + public void encodeAllAndInstall() { + int metadataByteLength = 0; for (HostedType declaringType : sortedTypes) { - long typeID = declaringType.getHub().getTypeID(); - assert typeID >= nextTypeId; - for (; nextTypeId < typeID; nextTypeId++) { - indexEncodingBuffer.putS4(MethodMetadataDecoderImpl.NO_METHOD_METADATA); + DynamicHub hub = declaringType.getHub(); + ClassMetadata classMetadata = classData.get(declaringType); + metadataByteLength += encodeAndInstallCollection(classMetadata.classes, this::encodeType, hub::setClassesEncoding, false); + if (classMetadata.enclosingMethodDeclaringClass != null || classMetadata.enclosingMethodName != null || classMetadata.enclosingMethodDescriptor != null) { + metadataByteLength += encodeAndInstall(new Object[]{classMetadata.enclosingMethodDeclaringClass, classMetadata.enclosingMethodName, classMetadata.enclosingMethodDescriptor}, + this::encodeEnclosingMethod, hub::setEnclosingMethodInfo); } - assert nextTypeId == typeID; - - long index = dataEncodingBuffer.getBytesWritten(); - indexEncodingBuffer.putS4(index); - nextTypeId++; - - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - Set queriedMethods = queriedMethodData.getOrDefault(declaringType, Collections.emptySet()); - encodeArray(dataEncodingBuffer, queriedMethods.toArray(new ReflectionMethodMetadata[0]), method -> { - assert method.declaringType.equals(declaringType); - encodeReflectionMethod(dataEncodingBuffer, method); - }); - - Set hidingMethods = hidingMethodData.getOrDefault(declaringType, Collections.emptySet()); - encodeArray(dataEncodingBuffer, hidingMethods.toArray(new MethodMetadata[0]), hidingMethod -> encodeSimpleMethod(dataEncodingBuffer, hidingMethod)); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + metadataByteLength += encodeAndInstallCollection(classMetadata.recordComponents, this::encodeRecordComponent, hub::setRecordComponentsEncoding, true); + metadataByteLength += encodeAndInstallCollection(classMetadata.permittedSubclasses, this::encodeType, hub::setPermittedSubclassesEncoding, true); } - - if (SubstrateOptions.IncludeMethodData.getValue()) { - Set reachableMethods = reachableMethodData.get(declaringType); - if (reachableMethods != null) { - encodeType(dataEncodingBuffer, declaringType); - encodeArray(dataEncodingBuffer, reachableMethods.toArray(new MethodMetadata[0]), reachableMethod -> encodeSimpleMethod(dataEncodingBuffer, reachableMethod)); - } else { - dataEncodingBuffer.putSV(MethodMetadataDecoderImpl.NO_METHOD_METADATA); + metadataByteLength += encodeAndInstall(classMetadata.annotations, this::encodeAnnotations, hub::setAnnotationsEncoding); + metadataByteLength += encodeAndInstall(classMetadata.typeAnnotations, this::encodeTypeAnnotations, + hub::setTypeAnnotationsEncoding); + metadataByteLength += encodeAndInstallCollection(fieldData.getOrDefault(declaringType, Collections.emptySet()).toArray(new FieldMetadata[0]), this::encodeField, hub::setFieldsEncoding, + false); + metadataByteLength += encodeAndInstallCollection(methodData.getOrDefault(declaringType, Collections.emptySet()).toArray(new MethodMetadata[0]), this::encodeExecutable, + hub::setMethodsEncoding, false); + metadataByteLength += encodeAndInstallCollection(constructorData.getOrDefault(declaringType, Collections.emptySet()).toArray(new ConstructorMetadata[0]), this::encodeExecutable, + hub::setConstructorsEncoding, false); + } + for (AccessibleObjectMetadata metadata : heapData) { + AccessibleObject heapObject = (AccessibleObject) SubstrateObjectConstant.asObject(metadata.heapObject); + metadataByteLength += encodeAndInstall(metadata.annotations, this::encodeAnnotations, (array) -> annotationsEncodings.put(heapObject, array)); + metadataByteLength += encodeAndInstall(metadata.typeAnnotations, this::encodeTypeAnnotations, (array) -> typeAnnotationsEncodings.put(heapObject, array)); + if (metadata instanceof ExecutableMetadata) { + metadataByteLength += encodeAndInstall(((ExecutableMetadata) metadata).parameterAnnotations, this::encodeParameterAnnotations, + (array) -> parameterAnnotationsEncodings.put((Executable) heapObject, array)); + if (((ExecutableMetadata) metadata).reflectParameters != null) { + metadataByteLength += encodeAndInstall(((ExecutableMetadata) metadata).reflectParameters, this::encodeReflectParameters, + (array) -> reflectParametersEncodings.put((Executable) heapObject, array)); + } + if (metadata instanceof MethodMetadata && ((Method) SubstrateObjectConstant.asObject(metadata.heapObject)).getDeclaringClass().isAnnotation() && + ((MethodMetadata) metadata).annotationDefault != null) { + metadataByteLength += encodeAndInstall(((MethodMetadata) metadata).annotationDefault, this::encodeMemberValue, + (array) -> annotationDefaultEncodings.put((Method) heapObject, array)); } } } - for (; nextTypeId <= ImageSingletons.lookup(DynamicHubSupport.class).getMaxTypeId(); nextTypeId++) { - indexEncodingBuffer.putS4(MethodMetadataDecoderImpl.NO_METHOD_METADATA); - } + /* Enable field recomputers in reflection objects to see the computed values */ + ImageSingletons.add(MethodMetadataEncoder.class, this); + ProgressReporter.singleton().setMetadataByteLength(metadataByteLength); + } + + private static int encodeAndInstallCollection(T[] data, BiConsumer encodeCallback, Consumer saveCallback, boolean canBeNull) { + UnsafeArrayTypeWriter encodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + encodeArray(encodingBuffer, data, element -> encodeCallback.accept(encodingBuffer, element), canBeNull); + return install(encodingBuffer, saveCallback); + } - methodDataEncoding = new byte[TypeConversion.asS4(dataEncodingBuffer.getBytesWritten())]; - dataEncodingBuffer.toArray(methodDataEncoding); - methodDataIndexEncoding = new byte[TypeConversion.asS4(indexEncodingBuffer.getBytesWritten())]; - indexEncodingBuffer.toArray(methodDataIndexEncoding); + private static int encodeAndInstall(T data, Function encodeCallback, Consumer saveCallback) { + UnsafeArrayTypeWriter encodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + encodeBytes(encodingBuffer, encodeCallback.apply(data)); + return install(encodingBuffer, saveCallback); } - private void encodeReflectionMethod(UnsafeArrayTypeWriter buf, ReflectionMethodMetadata method) { - encodeSimpleMethod(buf, method); - buf.putUV(method.modifiers); - encodeType(buf, method.returnType); - encodeArray(buf, method.exceptionTypes, exceptionType -> encodeType(buf, exceptionType)); - encodeName(buf, method.signature); - encodeByteArray(buf, encodeAnnotations(method.annotations)); - encodeByteArray(buf, encodeParameterAnnotations(method.parameterAnnotations)); - encodeByteArray(buf, encodeTypeAnnotations(method.typeAnnotations)); - buf.putU1(method.hasRealParameterData ? 1 : 0); - if (method.hasRealParameterData) { - encodeArray(buf, method.reflectParameters, reflectParameter -> { - encodeName(buf, reflectParameter.getName()); - buf.putS4(reflectParameter.getModifiers()); - }); + private static int install(UnsafeArrayTypeWriter encodingBuffer, Consumer saveCallback) { + int encodingSize = TypeConversion.asS4(encodingBuffer.getBytesWritten()); + byte[] dataEncoding = new byte[encodingSize]; + saveCallback.accept(encodingBuffer.toArray(dataEncoding)); + return encodingSize; + } + + private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) { + assert (field.modifiers & COMPLETE_FLAG_MASK) == 0 && (field.modifiers & IN_HEAP_FLAG_MASK) == 0; + int modifiers = field.modifiers; + modifiers |= field.complete ? COMPLETE_FLAG_MASK : 0; + modifiers |= field.heapObject != null ? IN_HEAP_FLAG_MASK : 0; + buf.putUV(modifiers); + if (field.heapObject != null) { + encodeObject(buf, field.heapObject); + } else { + encodeName(buf, field.name); + if (field.complete) { + encodeType(buf, field.type); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + buf.putU1(field.trustedFinal ? 1 : 0); + } + encodeName(buf, field.signature); + encodeByteArray(buf, encodeAnnotations(field.annotations)); + encodeByteArray(buf, encodeTypeAnnotations(field.typeAnnotations)); + buf.putSV(field.offset); + encodeName(buf, field.deletedReason); + } } } - private void encodeSimpleMethod(UnsafeArrayTypeWriter buf, MethodMetadata method) { - encodeName(buf, method.name); - encodeArray(buf, method.parameterTypes, parameterType -> encodeType(buf, parameterType)); + private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata executable) { + boolean isMethod = executable instanceof MethodMetadata; + assert (executable.modifiers & COMPLETE_FLAG_MASK) == 0 && (executable.modifiers & IN_HEAP_FLAG_MASK) == 0 && (executable.modifiers & HIDING_FLAG_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; + buf.putUV(modifiers); + if (executable.heapObject != null) { + encodeObject(buf, executable.heapObject); + } else { + if (isMethod) { + encodeName(buf, ((MethodMetadata) executable).name); + } + encodeArray(buf, executable.parameterTypes, parameterType -> encodeType(buf, parameterType)); + if (isMethod && (executable.complete || ((MethodMetadata) executable).hiding)) { + encodeType(buf, ((MethodMetadata) executable).returnType); + } + if (executable.complete) { + encodeArray(buf, executable.exceptionTypes, exceptionType -> encodeType(buf, exceptionType)); + encodeName(buf, executable.signature); + encodeByteArray(buf, encodeAnnotations(executable.annotations)); + encodeByteArray(buf, encodeParameterAnnotations(executable.parameterAnnotations)); + if (isMethod && executable.declaringType.getHub().getHostedJavaClass().isAnnotation()) { + encodeByteArray(buf, encodeMemberValue(((MethodMetadata) executable).annotationDefault)); + } else { + assert !isMethod || ((MethodMetadata) executable).annotationDefault == null; + } + encodeByteArray(buf, encodeTypeAnnotations(executable.typeAnnotations)); + encodeByteArray(buf, encodeReflectParameters(executable.reflectParameters)); + encodeObject(buf, executable.accessor); + } + } } private void encodeType(UnsafeArrayTypeWriter buf, HostedType type) { @@ -362,7 +808,22 @@ private void encodeName(UnsafeArrayTypeWriter buf, String name) { buf.putSV(encoders.sourceMethodNames.getIndex(name)); } + private void encodeObject(UnsafeArrayTypeWriter buf, JavaConstant object) { + if (object == null) { + buf.putSV(NULL_OBJECT); + } else { + buf.putSV(encoders.objectConstants.getIndex(object)); + } + } + private static void encodeArray(UnsafeArrayTypeWriter buf, T[] array, Consumer elementEncoder) { + encodeArray(buf, array, elementEncoder, false); + } + + private static void encodeArray(UnsafeArrayTypeWriter buf, T[] array, Consumer elementEncoder, boolean canBeNull) { + if (canBeNull && array == null) { + return; + } buf.putUV(array.length); for (T elem : array) { elementEncoder.accept(elem); @@ -371,19 +832,41 @@ private static void encodeArray(UnsafeArrayTypeWriter buf, T[] array, Consum private static void encodeByteArray(UnsafeArrayTypeWriter buf, byte[] array) { buf.putUV(array.length); - for (byte b : array) { + encodeBytes(buf, array); + } + + private static void encodeBytes(UnsafeArrayTypeWriter buf, byte[] bytes) { + for (byte b : bytes) { buf.putS1(b); } } + private static final Method getFieldSignature = ReflectionUtil.lookupMethod(Field.class, "getGenericSignature"); private static final Method getMethodSignature = ReflectionUtil.lookupMethod(Method.class, "getGenericSignature"); private static final Method getConstructorSignature = ReflectionUtil.lookupMethod(Constructor.class, "getSignature"); + private static final Method getExecutableParameters = ReflectionUtil.lookupMethod(Executable.class, "getParameters0"); - private static String getSignature(Executable method) { + private static String getSignature(Field field) { try { - return (String) (method instanceof Method ? getMethodSignature.invoke(method) : getConstructorSignature.invoke(method)); + return (String) getFieldSignature.invoke(field); } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); + throw GraalError.shouldNotReachHere(e); + } + } + + private static String getSignature(Executable executable) { + try { + return (String) (executable instanceof Method ? getMethodSignature.invoke(executable) : getConstructorSignature.invoke(executable)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static Parameter[] getRawParameters(Executable executable) { + try { + return (Parameter[]) getExecutableParameters.invoke(executable); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); } } @@ -404,7 +887,7 @@ private static String getSignature(Executable method) { * {@link Target_sun_reflect_annotation_AnnotationParser}) */ public byte[] encodeAnnotations(Annotation[] annotations) { - UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); buf.putU2(annotations.length); for (Annotation annotation : annotations) { encodeAnnotation(buf, annotation); @@ -413,7 +896,7 @@ public byte[] encodeAnnotations(Annotation[] annotations) { } private byte[] encodeParameterAnnotations(Annotation[][] annotations) { - UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); buf.putU1(annotations.length); for (Annotation[] parameterAnnotations : annotations) { buf.putU2(parameterAnnotations.length); @@ -434,12 +917,28 @@ private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) buf.putS4(encoders.sourceMethodNames.getIndex(memberName)); try { encodeValue(buf, valueAccessor.invoke(annotation), type.memberTypes().get(memberName)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); + } catch (InvocationTargetException e) { + encodeValue(buf, annotationExceptionProxies.get(Pair.create(annotation, memberName)), Throwable.class); + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); } } } + private byte[] encodeMemberValue(Object value) { + if (value == null) { + return new byte[0]; + } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); + Class type = value.getClass(); + if (Proxy.isProxyClass(type)) { + assert type.getInterfaces().length == 1; + type = type.getInterfaces()[0]; + } + encodeValue(buf, value, type); + return buf.toArray(); + } + private void encodeValue(UnsafeArrayTypeWriter buf, Object value, Class type) { buf.putU1(tag(type)); if (type.isAnnotation()) { @@ -483,6 +982,8 @@ private void encodeValue(UnsafeArrayTypeWriter buf, Object value, Class type) default: throw GraalError.shouldNotReachHere(); } + } else if (type == Throwable.class) { + buf.putS4(encoders.objectConstants.getIndex((JavaConstant) value)); } else { throw GraalError.shouldNotReachHere(); } @@ -561,13 +1062,15 @@ private static byte tag(Class type) { return (byte) Wrapper.forPrimitiveType(type).basicTypeChar(); } else if (Wrapper.isWrapperType(type)) { return (byte) Wrapper.forWrapperType(type).basicTypeChar(); + } else if (type == Throwable.class) { + return 'E'; } else { - throw GraalError.shouldNotReachHere(); + throw GraalError.shouldNotReachHere(type.toString()); } } private byte[] encodeTypeAnnotations(TypeAnnotation[] annotations) { - UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); buf.putU2(annotations.length); for (TypeAnnotation typeAnnotation : annotations) { encodeTypeAnnotation(buf, typeAnnotation); @@ -656,7 +1159,38 @@ private static void encodeLocationInfo(UnsafeArrayTypeWriter buf, TypeAnnotation buf.putU1(location.index); } } catch (IllegalAccessException e) { - throw GraalError.shouldNotReachHere(); + throw GraalError.shouldNotReachHere(e); + } + } + + private byte[] encodeReflectParameters(ReflectParameterMetadata[] reflectParameters) { + if (reflectParameters == null) { + return new byte[0]; } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + encodeArray(buf, reflectParameters, reflectParameter -> encodeReflectParameter(buf, reflectParameter)); + return buf.toArray(); + } + + private void encodeReflectParameter(UnsafeArrayTypeWriter buf, ReflectParameterMetadata reflectParameter) { + encodeName(buf, reflectParameter.name); + buf.putUV(reflectParameter.modifiers); + } + + private void encodeRecordComponent(UnsafeArrayTypeWriter buf, RecordComponentMetadata recordComponent) { + encodeName(buf, recordComponent.name); + encodeType(buf, recordComponent.type); + encodeName(buf, recordComponent.signature); + encodeObject(buf, recordComponent.accessor); + encodeByteArray(buf, encodeAnnotations(recordComponent.annotations)); + encodeByteArray(buf, encodeTypeAnnotations(recordComponent.typeAnnotations)); + } + + private byte[] encodeEnclosingMethod(Object[] enclosingMethod) { + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + encodeType(buf, (HostedType) enclosingMethod[0]); + encodeName(buf, (String) enclosingMethod[1]); + encodeName(buf, (String) enclosingMethod[2]); + return buf.toArray(); } } 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 76a494f4596a..b8f414391958 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,12 +24,16 @@ */ package com.oracle.svm.reflect.hosted; +import static com.oracle.svm.reflect.hosted.MethodMetadataEncoderImpl.getTypeAnnotations; + import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InaccessibleObjectException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; @@ -46,104 +50,68 @@ import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; import java.util.stream.Collectors; -import org.graalvm.compiler.debug.GraalError; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; +import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.InjectAccessors; import com.oracle.svm.core.hub.AnnotationTypeSupport; import com.oracle.svm.core.hub.ClassForNameSupport; -import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.RecordSupport; -import com.oracle.svm.core.jdk.SealedClassSupport; import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ConditionalConfigurationRegistry; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; -import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; -import com.oracle.svm.hosted.FeatureImpl.FeatureAccessImpl; +import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.annotation.AnnotationSubstitutionType; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; -import com.oracle.svm.util.ReflectionUtil; +import com.oracle.svm.util.ModuleSupport; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import sun.reflect.annotation.AnnotationType; +import sun.reflect.annotation.EnumConstantNotPresentExceptionProxy; import sun.reflect.annotation.TypeAnnotation; -import sun.reflect.annotation.TypeAnnotationParser; +import sun.reflect.annotation.TypeNotPresentExceptionProxy; public class ReflectionDataBuilder extends ConditionalConfigurationRegistry implements RuntimeReflectionSupport { - public static final Field[] EMPTY_FIELDS = new Field[0]; - public static final Method[] EMPTY_METHODS = new Method[0]; - public static final Constructor[] EMPTY_CONSTRUCTORS = new Constructor[0]; - public static final Class[] EMPTY_CLASSES = new Class[0]; - private final Set> modifiedClasses = ConcurrentHashMap.newKeySet(); private boolean sealed; - private final DynamicHub.ReflectionData arrayReflectionData; private final Set> reflectionClasses = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private final Set reflectionMethods = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Map reflectionMethods = new ConcurrentHashMap<>(); + private final Map methodAccessors = new ConcurrentHashMap<>(); private final Set reflectionFields = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private Set queriedMethods; - private Set hidingMethods; + private final Set hidingMethods = ConcurrentHashMap.newKeySet(); + private final Set registeredMethods = ConcurrentHashMap.newKeySet(); + private final Set registeredFields = ConcurrentHashMap.newKeySet(); + private final Map, Object[]> registeredRecordComponents = new ConcurrentHashMap<>(); + private final Set heapReflectionObjects = ConcurrentHashMap.newKeySet(); + private final Map, Set>> innerClasses = new ConcurrentHashMap<>(); private final Set> processedClasses = new HashSet<>(); + private final Set processedTypes = new HashSet<>(); + private final Set processedAnalysisTypes = new HashSet<>(); + private final Map> processedHidingMethods = new HashMap<>(); + private final Set processedHeapReflectionObjects = new HashSet<>(); /* Keep track of annotation interface members to include in proxy classes */ private final Map, Set> annotationMembers = new HashMap<>(); - private final ReflectionDataAccessors accessors; - - private static Field dynamicHubReflectionDataField; - - public ReflectionDataBuilder(FeatureAccessImpl access) { - arrayReflectionData = getArrayReflectionData(); - accessors = new ReflectionDataAccessors(access); - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - queriedMethods = ConcurrentHashMap.newKeySet(); - hidingMethods = ConcurrentHashMap.newKeySet(); - } - } - - private static DynamicHub.ReflectionData getArrayReflectionData() { - Method[] publicArrayMethods; - try { - Method getPublicMethodsMethod = ReflectionUtil.lookupMethod(Class.class, "privateGetPublicMethods"); - publicArrayMethods = (Method[]) getPublicMethodsMethod.invoke(Object[].class); - } catch (ReflectiveOperationException e) { - throw VMError.shouldNotReachHere(e); - } - - // array classes only have methods inherited from Object - return DynamicHub.ReflectionData.get( - EMPTY_FIELDS, - EMPTY_FIELDS, - EMPTY_FIELDS, - EMPTY_METHODS, - publicArrayMethods, - EMPTY_CONSTRUCTORS, - EMPTY_CONSTRUCTORS, - null, - EMPTY_FIELDS, - EMPTY_METHODS, - EMPTY_CLASSES, - null, - EMPTY_CLASSES, - null, - null); + public ReflectionDataBuilder() { } @Override @@ -163,16 +131,18 @@ private void registerClasses(Class[] classes) { @Override public void register(ConfigurationCondition condition, boolean queriedOnly, Executable... methods) { checkNotSealed(); - if (queriedOnly && !SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - throw UserError.abort("Found manual reflection metadata configuration. Please use --configure-reflection-metadata to enable this behavior."); - } registerConditionalConfiguration(condition, () -> registerMethods(queriedOnly, methods)); } private void registerMethods(boolean queriedOnly, Executable[] methods) { for (Executable method : methods) { - boolean added = queriedOnly ? queriedMethods.add(method) : reflectionMethods.add(method); - if (added) { + if (reflectionMethods.containsKey(method) && reflectionMethods.get(method) == ExecutableAccessibility.Accessed) { + /* Do not downgrade a method already registered as accessed */ + continue; + } + ExecutableAccessibility newValue = queriedOnly ? ExecutableAccessibility.QueriedOnly : ExecutableAccessibility.Accessed; + ExecutableAccessibility oldValue = reflectionMethods.put(method, newValue); + if (oldValue != newValue) { modifiedClasses.add(method.getDeclaringClass()); } } @@ -199,10 +169,6 @@ private void checkNotSealed() { } } - protected void duringSetup(DuringSetupAccessImpl access) { - dynamicHubReflectionDataField = access.findField(DynamicHub.class, "rd"); - } - protected void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; processReachableTypes(access); @@ -238,7 +204,7 @@ private void processReachableTypes(DuringAnalysisAccessImpl access) { */ continue; } - if (type.isArray() || enclosingMethodOrConstructor(originalClass) != null) { + if (type.isArray() || enclosingMethodOrConstructor(originalClass, null) != null) { /* * This type is either an array or it has an enclosing method or constructor. In * either case we process the class, i.e., initialize its reflection data, mark @@ -283,25 +249,58 @@ private void processReachableTypes(DuringAnalysisAccessImpl access) { * See {@link MethodMetadataEncoderImpl} for details. */ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - Set newQueriedMethods = new HashSet<>(); - for (Executable reflectMethod : queriedMethods) { - if (!SubstitutionReflectivityFilter.shouldExclude(reflectMethod, access.getMetaAccess(), access.getUniverse())) { - AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(reflectMethod); - registerTypesForQueriedMethod(access, analysisMethod, reflectMethod); - registerHidingSubTypeMethods(access, analysisMethod, analysisMethod.getDeclaringClass()); - newQueriedMethods.add(reflectMethod); - } - } - queriedMethods = newQueriedMethods; - for (Executable method : reflectionMethods) { - if (!SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { - AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(method); + for (AnalysisType type : access.getUniverse().getTypes()) { + if (!processedAnalysisTypes.contains(type) && type.isReachable() && !SubstitutionReflectivityFilter.shouldExclude(type.getJavaClass(), access.getMetaAccess(), access.getUniverse())) { + registerTypesForClass(access, type, type.getJavaClass()); + processedAnalysisTypes.add(type); + } + } + for (Field reflectField : reflectionFields) { + if (!registeredFields.contains(reflectField) && !SubstitutionReflectivityFilter.shouldExclude(reflectField, access.getMetaAccess(), access.getUniverse())) { + AnalysisField analysisField = access.getMetaAccess().lookupJavaField(reflectField); + registerTypesForField(access, analysisField, reflectField); + registeredFields.add(reflectField); + } + } + for (Executable method : reflectionMethods.keySet()) { + if (SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { + continue; + } + if (!registeredMethods.contains(method)) { + AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(method); + registerTypesForMethod(access, analysisMethod, method); + registerHidingSubTypeMethods(access, analysisMethod, analysisMethod.getDeclaringClass()); + registeredMethods.add(method); + } + if (reflectionMethods.get(method) == ExecutableAccessibility.Accessed) { + /* + * We must also generate the accessor for a method that was registered as queried + * and then registered again as accessed + */ + methodAccessors.putIfAbsent(method, ImageSingletons.lookup(ReflectionFeature.class).getOrCreateAccessor(method)); + } + } + for (AccessibleObject object : heapReflectionObjects) { + if (!processedHeapReflectionObjects.contains(object)) { + if (object instanceof Field) { + Field field = (Field) object; + AnalysisField analysisField = access.getMetaAccess().lookupJavaField(field); + registerTypesForField(access, analysisField, field); + } else if (object instanceof Executable) { + Executable executable = (Executable) object; + AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(executable); + registerTypesForMethod(access, analysisMethod, executable); registerHidingSubTypeMethods(access, analysisMethod, analysisMethod.getDeclaringClass()); } + processedHeapReflectionObjects.add(object); } } if (SubstrateOptions.IncludeMethodData.getValue()) { + for (AnalysisField field : access.getUniverse().getFields()) { + if (field.isAccessed()) { + registerTypesForReachableField(access, field); + } + } for (AnalysisMethod method : access.getUniverse().getMethods()) { if (method.isReachable() && !method.isIntrinsicMethod()) { registerTypesForReachableMethod(access, method); @@ -310,12 +309,10 @@ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { } } - private final Map> seenHidingMethods = new HashMap<>(); - - private void registerHidingSubTypeMethods(DuringAnalysisAccessImpl access, AnalysisMethod method, AnalysisType type) { + private void registerHidingSubTypeMethods(DuringAnalysisAccess access, AnalysisMethod method, AnalysisType type) { if (!type.equals(method.getDeclaringClass()) && type.isReachable()) { - if (!seenHidingMethods.containsKey(method) || !seenHidingMethods.get(method).contains(type)) { - seenHidingMethods.computeIfAbsent(method, m -> new HashSet<>()).add(type); + if (!processedHidingMethods.containsKey(method) || !processedHidingMethods.get(method).contains(type)) { + processedHidingMethods.computeIfAbsent(method, m -> ConcurrentHashMap.newKeySet()).add(type); try { /* * Using findMethod here which uses getDeclaredMethods internally, instead of @@ -356,9 +353,74 @@ private void registerHidingSubTypeMethods(DuringAnalysisAccessImpl access, Analy } } - private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); + private void registerTypesForClass(DuringAnalysisAccessImpl access, AnalysisType analysisType, Class clazz) { + List errors = new ArrayList<>(); + makeTypeReachable(access, query(clazz::getGenericSuperclass, errors)); + Type[] genericInterfaces = query(clazz::getGenericInterfaces, errors); + if (genericInterfaces != null) { + for (Type genericInterface : genericInterfaces) { + try { + makeTypeReachable(access, genericInterface); + } catch (TypeNotPresentException | LinkageError e) { + errors.add(e); + } + } + } + Executable enclosingMethod = enclosingMethodOrConstructor(clazz, errors); + if (enclosingMethod != null) { + makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(enclosingMethod.getDeclaringClass())); + RuntimeReflection.register(enclosingMethod); + } + reportLinkingErrors(clazz, errors); + + Object[] recordComponents = buildRecordComponents(clazz, access); + if (recordComponents != null) { + for (Object recordComponent : recordComponents) { + registerTypesForRecordComponent(access, recordComponent); + } + registeredRecordComponents.put(clazz, recordComponents); + } + for (Annotation annotation : GuardedAnnotationAccess.getDeclaredAnnotations(analysisType)) { + registerTypesForAnnotation(access, annotation); + } + for (TypeAnnotation typeAnnotation : getTypeAnnotations(clazz)) { + // Checkstyle: allow direct annotation access + registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access + } + } + + private void registerTypesForField(DuringAnalysisAccessImpl access, AnalysisField analysisField, Field reflectField) { + /* + * Reflection accessors use Unsafe, so ensure that all reflectively accessible fields are + * registered as unsafe-accessible, whether they have been explicitly registered or their + * Field object is reachable in the image heap. + */ + ImageSingletons.lookup(ReflectionFeature.class).inspectAccessibleField(reflectField); - private static void registerTypesForQueriedMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod, Executable reflectMethod) { + if (!analysisField.isUnsafeAccessed() && !GuardedAnnotationAccess.isAnnotationPresent(analysisField, InjectAccessors.class)) { + analysisField.registerAsAccessed(); + analysisField.registerAsUnsafeAccessed(); + } + + makeAnalysisTypeReachable(access, analysisField.getDeclaringClass()); + makeAnalysisTypeReachable(access, analysisField.getType()); + makeTypeReachable(access, reflectField.getGenericType()); + + /* + * Enable runtime instantiation of annotations + */ + for (Annotation annotation : GuardedAnnotationAccess.getDeclaredAnnotations(analysisField)) { + registerTypesForAnnotation(access, annotation); + } + for (TypeAnnotation typeAnnotation : getTypeAnnotations(reflectField)) { + // Checkstyle: allow direct annotation access + registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access + } + } + + private void registerTypesForMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod, Executable reflectMethod) { makeAnalysisTypeReachable(access, analysisMethod.getDeclaringClass()); for (TypeVariable type : reflectMethod.getTypeParameters()) { @@ -378,24 +440,24 @@ private static void registerTypesForQueriedMethod(DuringAnalysisAccessImpl acces * Enable runtime instantiation of annotations */ for (Annotation annotation : GuardedAnnotationAccess.getDeclaredAnnotations(analysisMethod)) { - registerTypesForAnnotationValue(access, annotation.annotationType(), annotation); + registerTypesForAnnotation(access, annotation); } for (Annotation[] parameterAnnotations : reflectMethod.getParameterAnnotations()) { for (Annotation parameterAnnotation : parameterAnnotations) { - registerTypesForAnnotationValue(access, parameterAnnotation.annotationType(), parameterAnnotation); + registerTypesForAnnotation(access, parameterAnnotation); } } - try { - for (TypeAnnotation typeAnnotation : (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, reflectMethod)) { - // Checkstyle: allow direct annotation access - registerTypesForAnnotationValue(access, typeAnnotation.getAnnotation().annotationType(), typeAnnotation.getAnnotation()); - // Checkstyle: disallow direct annotation access - } - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); + for (TypeAnnotation typeAnnotation : getTypeAnnotations(reflectMethod)) { + // Checkstyle: allow direct annotation access + registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access } } + private static void registerTypesForReachableField(DuringAnalysisAccessImpl access, AnalysisField analysisField) { + makeAnalysisTypeReachable(access, analysisField.getDeclaringClass()); + } + private static void registerTypesForReachableMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod) { makeAnalysisTypeReachable(access, analysisMethod.getDeclaringClass()); for (JavaType paramType : analysisMethod.toParameterTypes()) { @@ -403,14 +465,19 @@ private static void registerTypesForReachableMethod(DuringAnalysisAccessImpl acc } } - private static final Set seenTypes = new HashSet<>(); - - @SuppressWarnings("unchecked") - private static void makeTypeReachable(DuringAnalysisAccessImpl access, Type type) { - if (type == null || seenTypes.contains(type)) { - return; + private void makeTypeReachable(DuringAnalysisAccessImpl access, Type type) { + try { + if (type == null || processedTypes.contains(type)) { + return; + } + } catch (TypeNotPresentException e) { + /* Hash code computation can trigger an exception if the type is missing */ + if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { + return; + } + throw e; } - seenTypes.add(type); + processedTypes.add(type); if (type instanceof Class && !SubstitutionReflectivityFilter.shouldExclude((Class) type, access.getMetaAccess(), access.getUniverse())) { Class clazz = (Class) type; makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(clazz)); @@ -446,15 +513,34 @@ private static void makeTypeReachable(DuringAnalysisAccessImpl access, Type type } } + private static void registerTypesForRecordComponent(DuringAnalysisAccessImpl access, Object recordComponent) { + for (Annotation annotation : GuardedAnnotationAccess.getAnnotations((AnnotatedElement) recordComponent)) { + registerTypesForAnnotation(access, annotation); + } + for (TypeAnnotation typeAnnotation : getTypeAnnotations((AnnotatedElement) recordComponent)) { + // Checkstyle: allow direct annotation access + registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access + } + } + + private static void registerTypesForAnnotation(DuringAnalysisAccessImpl accessImpl, Annotation annotation) { + /* + * Don't make annotation types reachable unless they have a chance of being queried. + */ + accessImpl.registerReachabilityHandler((access) -> registerTypesForAnnotationValue((DuringAnalysisAccessImpl) access, annotation.annotationType(), annotation), annotation.annotationType()); + } + @SuppressWarnings("unchecked") private static void registerTypesForAnnotationValue(DuringAnalysisAccessImpl access, Class type, Object value) { - if (type.isAnnotation()) { + if (type.isAnnotation() && !SubstitutionReflectivityFilter.shouldExclude(type, access.getMetaAccess(), access.getUniverse())) { makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(type)); /* * Parsing annotation data in reflection classes requires being able to instantiate all * annotation types at runtime. */ ImageSingletons.lookup(AnnotationTypeSupport.class).createInstance((Class) type); + ModuleSupport.openModuleByClass(type, ReflectionDataBuilder.class); ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(type); Annotation annotation = (Annotation) value; @@ -467,8 +553,21 @@ private static void registerTypesForAnnotationValue(DuringAnalysisAccessImpl acc getAnnotationValue.setAccessible(true); Object annotationValue = getAnnotationValue.invoke(annotation); registerTypesForAnnotationValue(access, valueType, annotationValue); - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); + } catch (IllegalAccessException | InvocationTargetException | InaccessibleObjectException e) { + // Ignore the value + Throwable exception = e instanceof InvocationTargetException ? ((InvocationTargetException) e).getTargetException() : e; + System.out.println("Warning: unable to register annotation value \"" + valueName + "\" for annotation type " + type + ". Reason: " + exception); + if (e instanceof InvocationTargetException) { + if (exception instanceof TypeNotPresentException) { + AnalysisType proxyType = access.getMetaAccess().lookupJavaType(TypeNotPresentExceptionProxy.class); + makeAnalysisTypeReachable(access, proxyType); + proxyType.registerAsInHeap(); + } else if (exception instanceof EnumConstantNotPresentException) { + AnalysisType proxyType = access.getMetaAccess().lookupJavaType(EnumConstantNotPresentExceptionProxy.class); + makeAnalysisTypeReachable(access, proxyType); + proxyType.registerAsInHeap(); + } + } } } } else if (type.isArray()) { @@ -514,64 +613,16 @@ private void processClass(DuringAnalysisAccessImpl access, Class clazz) { * build the reflection metadata. */ type.registerAsReachable(); - DynamicHub hub = access.getHostVM().dynamicHub(type); if (reflectionClasses.contains(clazz)) { ClassForNameSupport.registerClass(clazz); - } - - /* - * Trigger initialization of the fields of the original Class.ReflectionData object by - * calling the public methods. - * - * If one of the called methods throws a LinkageError because of a missing type or types - * that have incompatible changes, we skip registering that part of the reflection metadata - * for this class, but continue to try to register other parts of the reflection metadata. - * - * If the class fails verification then no reflection metadata can be registered. However, - * the class is still registered for run time loading with Class.forName() and its class - * initializer is replaced with a synthesized 'throw new VerifyError()' (see - * ClassInitializationFeature.buildRuntimeInitializationInfo()). - */ - List errors = new ArrayList<>(); - query(clazz::getDeclaredFields, errors); - query(clazz::getFields, errors); - query(clazz::getDeclaredMethods, errors); - query(clazz::getMethods, errors); - query(clazz::getDeclaredConstructors, errors); - query(clazz::getConstructors, errors); - Class[] declaredClasses = query(clazz::getDeclaredClasses, errors); - Class[] permittedClasses = SealedClassSupport.singleton().getPermittedSubclasses(clazz); - Class[] classes = query(clazz::getClasses, errors); - reportLinkingErrors(clazz, errors); - Object originalReflectionData = accessors.getReflectionData(clazz); - DynamicHub.ReflectionData reflectionData; - - if (type.isArray()) { - // Always register reflection data for array classes - reflectionData = arrayReflectionData; - } else { - reflectionData = DynamicHub.ReflectionData.get( - filterFields(accessors.getDeclaredFields(originalReflectionData), reflectionFields, access), - filterFields(accessors.getPublicFields(originalReflectionData), reflectionFields, access), - filterFields(accessors.getPublicFields(originalReflectionData), f -> reflectionFields.contains(f) && !isHiddenIn(f, clazz), access), - filterMethods(accessors.getDeclaredMethods(originalReflectionData), reflectionMethods, access), - filterMethods(accessors.getPublicMethods(originalReflectionData), reflectionMethods, access), - filterConstructors(accessors.getDeclaredConstructors(originalReflectionData), reflectionMethods, access), - filterConstructors(accessors.getPublicConstructors(originalReflectionData), reflectionMethods, access), - nullaryConstructor(accessors.getDeclaredConstructors(originalReflectionData), reflectionMethods, access), - filterFields(accessors.getDeclaredPublicFields(originalReflectionData), reflectionFields, access), - filterMethods(accessors.getDeclaredPublicMethods(originalReflectionData), reflectionMethods, access), - filterClasses(declaredClasses, reflectionClasses, access), - filterClasses(permittedClasses, reflectionClasses, access, true), - /* null is different from Class[0] here. */ - filterClasses(classes, reflectionClasses, access), - enclosingMethodOrConstructor(clazz), - buildRecordComponents(clazz, access)); - } - hub.setReflectionData(reflectionData); - access.rescanField(hub, dynamicHubReflectionDataField); + List errors = new ArrayList<>(); + if (query(clazz::getEnclosingClass, errors) != null) { + innerClasses.computeIfAbsent(access.getMetaAccess().lookupJavaType(clazz.getEnclosingClass()).getJavaClass(), (enclosingType) -> ConcurrentHashMap.newKeySet()).add(clazz); + } + reportLinkingErrors(clazz, errors); + } if (type.isAnnotation()) { /* @@ -579,8 +630,16 @@ private void processClass(DuringAnalysisAccessImpl access, Class clazz) { * their own reflection data */ Set members = new HashSet<>(); - Collections.addAll(members, filterFields(accessors.getDeclaredFields(originalReflectionData), reflectionFields, access)); - Collections.addAll(members, filterMethods(accessors.getDeclaredMethods(originalReflectionData), reflectionMethods, access)); + for (Field field : reflectionFields) { + if (field.getDeclaringClass().equals(clazz) && !SubstitutionReflectivityFilter.shouldExclude(field, access.getMetaAccess(), access.getUniverse())) { + members.add(field); + } + } + for (Executable executable : reflectionMethods.keySet()) { + if (executable.getDeclaringClass().equals(clazz) && !SubstitutionReflectivityFilter.shouldExclude(executable, access.getMetaAccess(), access.getUniverse())) { + members.add(executable); + } + } annotationMembers.put(clazz, members); access.requireAnalysisIteration(); /* Need the proxy class to see the added members */ } @@ -615,13 +674,12 @@ private Object[] buildRecordComponents(Class clazz, DuringAnalysisAccessImpl * DynamicHub.getRecordComponents0(). */ Method[] allMethods = support.getRecordComponentAccessorMethods(clazz); - Method[] filteredMethods = filterMethods(allMethods, reflectionMethods, access); - - if (allMethods.length == filteredMethods.length) { - return support.getRecordComponents(clazz); - } else { - return null; + for (Method method : allMethods) { + if (!reflectionMethods.containsKey(method) || SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { + return null; + } } + return support.getRecordComponents(clazz); } private static void reportLinkingErrors(Class clazz, List errors) { @@ -645,19 +703,7 @@ public boolean requiresProcessing() { return !modifiedClasses.isEmpty(); } - private static Constructor nullaryConstructor(Object constructors, Set reflectionMethods, DuringAnalysisAccessImpl access) { - if (constructors != null) { - for (Constructor constructor : (Constructor[]) constructors) { - if (constructor.getParameterCount() == 0 && reflectionMethods.contains(constructor) && - !SubstitutionReflectivityFilter.shouldExclude(constructor, access.getMetaAccess(), access.getUniverse())) { - return constructor; - } - } - } - return null; - } - - private Executable enclosingMethodOrConstructor(Class clazz) { + private Executable enclosingMethodOrConstructor(Class clazz, List errors) { Method enclosingMethod; Constructor enclosingConstructor; try { @@ -669,6 +715,9 @@ private Executable enclosingMethodOrConstructor(Class clazz) { * missing types or types that have incompatible changes a LinkageError is thrown. Skip * the class. */ + if (errors != null) { + errors.add(e); + } return null; } catch (InternalError ex) { /* @@ -677,6 +726,9 @@ private Executable enclosingMethodOrConstructor(Class clazz) { * synthetic method for a anonymous class declared inside a lambda. We skip registering * the enclosing method for such classes. */ + if (errors != null) { + errors.add(ex); + } return null; } @@ -689,89 +741,59 @@ private Executable enclosingMethodOrConstructor(Class clazz) { Executable enclosingMethodOrConstructor = enclosingMethod != null ? enclosingMethod : enclosingConstructor; - if (reflectionMethods.contains(enclosingMethodOrConstructor)) { + if (reflectionMethods.containsKey(enclosingMethodOrConstructor)) { return enclosingMethodOrConstructor; } else { return null; } } - private static Field[] filterFields(Object fields, Set filterSet, DuringAnalysisAccessImpl access) { - return filterFields(fields, filterSet::contains, access); - } - - private static boolean isHiddenIn(Field field, Class clazz) { - try { - return !clazz.getField(field.getName()).equals(field); - } catch (NoSuchFieldException e) { - throw VMError.shouldNotReachHere(e); - } - } - - private static Field[] filterFields(Object fields, Predicate filter, DuringAnalysisAccessImpl access) { - if (fields == null) { - return EMPTY_FIELDS; - } - List result = new ArrayList<>(); - for (Field field : (Field[]) fields) { - if (filter.test(field) && !SubstitutionReflectivityFilter.shouldExclude(field, access.getMetaAccess(), access.getUniverse())) { - result.add(field); - } - } - return result.toArray(EMPTY_FIELDS); + @Override + public Map, Set>> getReflectionInnerClasses() { + assert sealed; + return Collections.unmodifiableMap(innerClasses); } - private static Constructor[] filterConstructors(Object methods, Set filter, DuringAnalysisAccessImpl access) { - return filterMethods(methods, filter, access, EMPTY_CONSTRUCTORS); + @Override + public Set getReflectionFields() { + assert sealed; + return Collections.unmodifiableSet(registeredFields); } - private static Method[] filterMethods(Object methods, Set filter, DuringAnalysisAccessImpl access) { - return filterMethods(methods, filter, access, EMPTY_METHODS); + @Override + public Set getReflectionExecutables() { + assert sealed; + return Collections.unmodifiableSet(registeredMethods); } - @SuppressWarnings("unchecked") - private static T[] filterMethods(Object methods, Set filter, DuringAnalysisAccessImpl access, T[] emptyArray) { - if (methods == null) { - return emptyArray; - } - List result = new ArrayList<>(); - for (T method : (T[]) methods) { - if (filter.contains(method) && !SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { - result.add(method); - } - } - return result.toArray(emptyArray); + @Override + public Object getAccessor(Executable method) { + assert sealed; + return methodAccessors.get(method); } - private static Class[] filterClasses(Object classes, Set> filter, DuringAnalysisAccessImpl access) { - return filterClasses(classes, filter, access, false); + @Override + public Set getHidingReflectionMethods() { + assert sealed; + return Collections.unmodifiableSet(hidingMethods); } - private static Class[] filterClasses(Object classes, Set> filter, DuringAnalysisAccessImpl access, boolean keepNull) { - if (classes == null) { - if (keepNull) { - return null; - } else { - return EMPTY_CLASSES; - } - } - List> result = new ArrayList<>(); - for (Class clazz : (Class[]) classes) { - if (filter.contains(clazz) && !SubstitutionReflectivityFilter.shouldExclude(clazz, access.getMetaAccess(), access.getUniverse())) { - result.add(clazz); - } - } - return result.toArray(EMPTY_CLASSES); + @Override + public Object[] getRecordComponents(Class type) { + assert sealed; + return registeredRecordComponents.get(type); } @Override - public Set getQueriedOnlyMethods() { - return queriedMethods != null ? Collections.unmodifiableSet(queriedMethods) : Collections.emptySet(); + public void registerHeapReflectionObject(AccessibleObject object) { + assert !sealed; + heapReflectionObjects.add(object); } @Override - public Set getHidingMethods() { - return hidingMethods != null ? Collections.unmodifiableSet(hidingMethods) : Collections.emptySet(); + public Set getHeapReflectionObjects() { + assert sealed; + return Collections.unmodifiableSet(heapReflectionObjects); } @Override @@ -781,108 +803,16 @@ public int getReflectionClassesCount() { @Override public int getReflectionMethodsCount() { - return reflectionMethods.size(); + return registeredMethods.size(); } @Override public int getReflectionFieldsCount() { - return reflectionFields.size(); + return registeredFields.size(); } - static final class ReflectionDataAccessors { - private final Method reflectionDataMethod; - private final Field declaredFieldsField; - private final Field publicFieldsField; - private final Field declaredMethodsField; - private final Field publicMethodsField; - private final Field declaredConstructorsField; - private final Field publicConstructorsField; - private final Field declaredPublicFieldsField; - private final Field declaredPublicMethodsField; - - ReflectionDataAccessors(FeatureAccessImpl access) { - reflectionDataMethod = ReflectionUtil.lookupMethod(Class.class, "reflectionData"); - Class originalReflectionDataClass = access.getImageClassLoader().findClassOrFail("java.lang.Class$ReflectionData"); - declaredFieldsField = ReflectionUtil.lookupField(originalReflectionDataClass, "declaredFields"); - publicFieldsField = ReflectionUtil.lookupField(originalReflectionDataClass, "publicFields"); - declaredMethodsField = ReflectionUtil.lookupField(originalReflectionDataClass, "declaredMethods"); - publicMethodsField = ReflectionUtil.lookupField(originalReflectionDataClass, "publicMethods"); - declaredConstructorsField = ReflectionUtil.lookupField(originalReflectionDataClass, "declaredConstructors"); - publicConstructorsField = ReflectionUtil.lookupField(originalReflectionDataClass, "publicConstructors"); - declaredPublicFieldsField = ReflectionUtil.lookupField(originalReflectionDataClass, "declaredPublicFields"); - declaredPublicMethodsField = ReflectionUtil.lookupField(originalReflectionDataClass, "declaredPublicMethods"); - } - - public Object getReflectionData(Class clazz) { - try { - return reflectionDataMethod.invoke(clazz); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - throw VMError.shouldNotReachHere(e); - } - } - - public Object getDeclaredFields(Object obj) { - try { - return declaredFieldsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } - - public Object getPublicFields(Object obj) { - try { - return publicFieldsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } - - public Object getDeclaredMethods(Object obj) { - try { - return declaredMethodsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } - - public Object getPublicMethods(Object obj) { - try { - return publicMethodsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } - - public Object getDeclaredConstructors(Object obj) { - try { - return declaredConstructorsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } - - public Object getPublicConstructors(Object obj) { - try { - return publicConstructorsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } - - public Object getDeclaredPublicFields(Object obj) { - try { - return declaredPublicFieldsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } - - public Object getDeclaredPublicMethods(Object obj) { - try { - return declaredPublicMethodsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } + private enum ExecutableAccessibility { + QueriedOnly, + Accessed } } 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 fb5f0469ba4d..c71f6c373be4 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 @@ -57,7 +57,6 @@ import com.oracle.svm.hosted.FallbackFeature; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; -import com.oracle.svm.hosted.FeatureImpl.FeatureAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.analysis.Inflation; import com.oracle.svm.hosted.config.ConfigurationParserUtils; @@ -156,7 +155,7 @@ protected void inspectAccessibleField(@SuppressWarnings("unused") Field field) { public void afterRegistration(AfterRegistrationAccess access) { ModuleSupport.exportAndOpenPackageToUnnamed("java.base", "jdk.internal.reflect", false); - reflectionData = new ReflectionDataBuilder((FeatureAccessImpl) access); + reflectionData = new ReflectionDataBuilder(); ImageSingletons.add(RuntimeReflectionSupport.class, reflectionData); } @@ -165,7 +164,7 @@ public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; aUniverse = access.getUniverse(); - access.registerObjectReplacer(new ReflectionObjectReplacer(access.getMetaAccess())); + access.registerObjectReplacer(new ReflectionObjectReplacer()); ReflectionConfigurationParser>> parser = ConfigurationParserUtils.create(reflectionData, access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, access.getImageClassLoader(), "reflection", @@ -174,7 +173,6 @@ public void duringSetup(DuringSetupAccess a) { loader = access.getImageClassLoader(); annotationSubstitutions = ((Inflation) access.getBigBang()).getAnnotationSubstitutionProcessor(); - reflectionData.duringSetup(access); } @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 new file mode 100644 index 000000000000..7ddc2e33fd25 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2021, 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.reflect.hosted; + +import static com.oracle.svm.core.meta.SharedField.LOC_UNINITIALIZED; + +import java.lang.annotation.Annotation; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; +import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl; + +import jdk.vm.ci.meta.JavaConstant; +import sun.reflect.annotation.TypeAnnotation; + +@AutomaticFeature +class MethodMetadataFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(MethodMetadataEncoderFactory.class, new MethodMetadataEncoderImpl.Factory()); + ImageSingletons.add(MethodMetadataDecoder.class, new MethodMetadataDecoderImpl()); + } +} + +public class ReflectionMetadata { + + static class AnnotatedElementMetadata { + final Annotation[] annotations; + final TypeAnnotation[] typeAnnotations; + + AnnotatedElementMetadata(Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + this.annotations = annotations; + this.typeAnnotations = typeAnnotations; + } + } + + static class ClassMetadata extends AnnotatedElementMetadata { + final HostedType[] classes; + final HostedType enclosingMethodDeclaringClass; + final String enclosingMethodName; + final String enclosingMethodDescriptor; + final RecordComponentMetadata[] recordComponents; + final HostedType[] permittedSubclasses; + + ClassMetadata(HostedType[] classes, HostedType enclosingMethodDeclaringClass, String enclosingMethodName, String enclosingMethodDescriptor, RecordComponentMetadata[] recordComponents, + HostedType[] permittedSubclasses, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + super(annotations, typeAnnotations); + this.classes = classes; + this.enclosingMethodDeclaringClass = enclosingMethodDeclaringClass; + this.enclosingMethodName = enclosingMethodName; + this.enclosingMethodDescriptor = enclosingMethodDescriptor; + this.recordComponents = recordComponents; + this.permittedSubclasses = permittedSubclasses; + } + } + + static class AccessibleObjectMetadata extends AnnotatedElementMetadata { + final boolean complete; + final JavaConstant heapObject; + final HostedType declaringType; + final int modifiers; + final String signature; + + AccessibleObjectMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, int modifiers, String signature, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + super(annotations, typeAnnotations); + this.complete = complete; + this.heapObject = heapObject; + this.declaringType = declaringType; + this.modifiers = modifiers; + this.signature = signature; + } + } + + static class FieldMetadata extends AccessibleObjectMetadata { + 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, + Annotation[] annotations, TypeAnnotation[] typeAnnotations, int offset, String deletedReason) { + super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations); + this.name = name; + this.type = type; + this.trustedFinal = trustedFinal; + this.offset = offset; + this.deletedReason = deletedReason; + } + + 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); + } + + FieldMetadata(JavaConstant heapObject, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + this(true, heapObject, null, null, null, 0, false, null, annotations, typeAnnotations, LOC_UNINITIALIZED, null); + } + + FieldMetadata(HostedType declaringType, String name) { + this(false, null, declaringType, name, null, 0, false, null, null, null, LOC_UNINITIALIZED, null); + } + } + + static class ExecutableMetadata extends AccessibleObjectMetadata { + final HostedType[] parameterTypes; + final HostedType[] exceptionTypes; + final Annotation[][] parameterAnnotations; + final ReflectParameterMetadata[] reflectParameters; + final JavaConstant accessor; + + ExecutableMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, + Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { + super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations); + this.parameterTypes = parameterTypes; + this.exceptionTypes = exceptionTypes; + this.parameterAnnotations = parameterAnnotations; + this.reflectParameters = reflectParameters; + this.accessor = accessor; + } + } + + static class MethodMetadata extends ExecutableMetadata { + final boolean hiding; + final String name; + final HostedType returnType; + final Object annotationDefault; + + 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); + this.hiding = hiding; + this.name = name; + this.returnType = returnType; + this.annotationDefault = annotationDefault; + } + + 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) { + this(true, false, null, declaringClass, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, parameterAnnotations, annotationDefault, typeAnnotations, + reflectParameters, + accessor); + } + + MethodMetadata(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); + } + + 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); + } + } + + static class ConstructorMetadata extends ExecutableMetadata { + + 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); + } + + 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); + } + + ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes, int modifiers) { + this(false, null, declaringClass, parameterTypes, modifiers, null, null, null, null, null, null, null); + } + } + + static class RecordComponentMetadata extends AnnotatedElementMetadata { + final HostedType declaringType; + final String name; + final HostedType type; + final String signature; + final JavaConstant accessor; + + RecordComponentMetadata(HostedType declaringType, String name, HostedType type, String signature, JavaConstant accessor, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + super(annotations, typeAnnotations); + this.declaringType = declaringType; + this.name = name; + this.type = type; + this.signature = signature; + this.accessor = accessor; + } + } + + static class ReflectParameterMetadata { + final String name; + final int modifiers; + + ReflectParameterMetadata(String name, int modifiers) { + this.name = name; + this.modifiers = modifiers; + } + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java index 7102fdad5c20..42a75360b392 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java @@ -25,36 +25,15 @@ package com.oracle.svm.reflect.hosted; import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.util.GuardedAnnotationAccess; - -import com.oracle.graal.pointsto.meta.AnalysisField; -import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; -import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.svm.core.annotate.Delete; - import sun.reflect.generics.repository.AbstractRepository; -import sun.reflect.generics.repository.ClassRepository; -import sun.reflect.generics.repository.ConstructorRepository; -import sun.reflect.generics.repository.FieldRepository; -import sun.reflect.generics.repository.GenericDeclRepository; -import sun.reflect.generics.repository.MethodRepository; import sun.reflect.generics.scope.AbstractScope; public class ReflectionObjectReplacer implements Function { - private final AnalysisMetaAccess metaAccess; - - public ReflectionObjectReplacer(AnalysisMetaAccess metaAccess) { - this.metaAccess = metaAccess; - } static class Identity { private final Object wrapped; @@ -87,100 +66,7 @@ public Object apply(Object original) { return original; } - private void scan(Object original) { - /* - * Reflection accessors use Unsafe, so ensure that all reflectively accessible fields are - * registered as unsafe-accessible, whether they have been explicitly registered or their - * Field object is reachable in the image heap. - */ - - if (original instanceof Field) { - AnalysisType declaring = metaAccess.lookupJavaType(((Field) original).getDeclaringClass()); - if (!GuardedAnnotationAccess.isAnnotationPresent(declaring, Delete.class)) { - // The declaring class must be reachable for the field lookup - declaring.registerAsReachable(); - AnalysisField analysisField = metaAccess.lookupJavaField((Field) original); - if (analysisField == null) { - /* - * We are after static analysis, and the field has not been seen during the - * static analysis. This is a corner case that happens when all static fields of - * a type are iterated and read after static analysis. We can just ignore such - * cases and not process the field. - */ - return; - } - if (!GuardedAnnotationAccess.isAnnotationPresent(analysisField, Delete.class)) { - ImageSingletons.lookup(ReflectionFeature.class).inspectAccessibleField((Field) original); - - if (!analysisField.isUnsafeAccessed()) { - analysisField.registerAsAccessed(); - analysisField.registerAsUnsafeAccessed(); - } - } - } - } - - /* - * Ensure that the generic info and annotations data structures are initialized. By calling - * the methods that access the metadata we ensure that the corresponding data structures are - * pre-loaded and cached in the native image heap, therefore no field value recomputation is - * required. - */ - - if (original instanceof Method) { - Method method = (Method) original; - method.getGenericReturnType(); - } - - if (original instanceof Executable) { - Executable executable = (Executable) original; - executable.getGenericParameterTypes(); - executable.getGenericExceptionTypes(); - executable.getParameters(); - executable.getTypeParameters(); - } - - if (original instanceof Field) { - Field field = (Field) original; - field.getGenericType(); - } - - if (original instanceof AccessibleObject) { - AccessibleObject accessibleObject = (AccessibleObject) original; - GuardedAnnotationAccess.getDeclaredAnnotations(accessibleObject); - } - - if (original instanceof Parameter) { - Parameter parameter = (Parameter) original; - parameter.getType(); - } - - if (original instanceof FieldRepository) { - FieldRepository fieldRepository = (FieldRepository) original; - fieldRepository.getGenericType(); - } - - if (original instanceof MethodRepository) { - MethodRepository methodRepository = (MethodRepository) original; - methodRepository.getReturnType(); - } - - if (original instanceof ConstructorRepository) { - ConstructorRepository constructorRepository = (ConstructorRepository) original; - constructorRepository.getExceptionTypes(); - constructorRepository.getParameterTypes(); - } - - if (original instanceof GenericDeclRepository) { - GenericDeclRepository methodRepository = (GenericDeclRepository) original; - methodRepository.getTypeParameters(); - } - - if (original instanceof ClassRepository) { - ClassRepository classRepository = (ClassRepository) original; - classRepository.getSuperclass(); - classRepository.getSuperInterfaces(); - } + private static void scan(Object original) { if (original instanceof AbstractScope) { AbstractScope abstractScope = (AbstractScope) original; /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java index 488832341a76..7e16b70d7a33 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java @@ -77,11 +77,9 @@ public String toString() { } } - private final ClassLoader classLoader; private final Map proxyCache; - public DynamicProxySupport(ClassLoader classLoader) { - this.classLoader = classLoader; + public DynamicProxySupport() { this.proxyCache = new ConcurrentHashMap<>(); } @@ -97,7 +95,7 @@ public void addProxyClass(Class... interfaces) { proxyCache.computeIfAbsent(key, k -> { Class clazz; try { - clazz = getJdkProxyClass(classLoader, intfs); + clazz = getJdkProxyClass(getCommonClassLoader(intfs), intfs); } catch (Throwable e) { return e; } @@ -131,6 +129,21 @@ public void addProxyClass(Class... interfaces) { }); } + private static ClassLoader getCommonClassLoader(Class... intfs) { + ClassLoader classLoader = null; + for (Class intf : intfs) { + ClassLoader intfLoader = intf.getClassLoader(); + if (classLoader == null) { + classLoader = intfLoader; + } else { + if (intfLoader != classLoader) { + return null; + } + } + } + return classLoader; + } + @Override public Class getProxyClass(ClassLoader loader, Class... interfaces) { ProxyCacheKey key = new ProxyCacheKey(interfaces); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java index c265f3d901c4..4596dad14460 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java @@ -60,7 +60,7 @@ public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; ImageClassLoader imageClassLoader = access.getImageClassLoader(); - DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(imageClassLoader.getClassLoader()); + DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(); ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport); ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("resource configuration", imageClassLoader); ProxyRegistry proxyRegistry = new ProxyRegistry(typeResolver, dynamicProxySupport, imageClassLoader); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java index d140c440f1e2..b2f796530fb3 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java @@ -25,19 +25,18 @@ package com.oracle.svm.reflect.target; import java.lang.reflect.Array; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; -import java.util.ArrayList; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; import java.util.Arrays; -import java.util.List; -import java.util.function.Supplier; +import java.util.function.Function; -import org.graalvm.collections.Pair; -import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; -import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.code.CodeInfo; @@ -45,6 +44,7 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent; import com.oracle.svm.core.util.ByteArrayReader; /** @@ -108,120 +108,221 @@ */ public class MethodMetadataDecoderImpl implements MethodMetadataDecoder { public static final int NO_METHOD_METADATA = -1; + public static final int NULL_OBJECT = -1; + public static final int COMPLETE_FLAG_INDEX = 31; + public static final int COMPLETE_FLAG_MASK = 1 << COMPLETE_FLAG_INDEX; + public static final int IN_HEAP_FLAG_INDEX = 30; + 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; - @Fold - static boolean hasQueriedMethods() { - return !ImageSingletons.lookup(RuntimeReflectionSupport.class).getQueriedOnlyMethods().isEmpty(); + @Override + public Field[] parseFields(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Field.class, (i) -> decodeField(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); } - /** - * This method returns two arrays. The first one contains the desired method data, the second - * one contains the names and parameter types of methods hiding methods declared in superclasses - * which therefore should not be returned by a call to getMethods(). - */ @Override - public Pair getQueriedAndHidingMethods(DynamicHub declaringType) { - int dataOffset = getOffset(declaringType.getTypeID()); - if (SubstrateOptions.ConfigureReflectionMetadata.getValue() && getOffset(declaringType.getTypeID()) != NO_METHOD_METADATA) { - CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); - byte[] data = ImageSingletons.lookup(MethodMetadataEncoding.class).getMethodsEncoding(); - UnsafeArrayTypeReader dataReader = UnsafeArrayTypeReader.create(data, dataOffset, ByteArrayReader.supportsUnalignedMemoryAccess()); + public Method[] parseMethods(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Method.class, (i) -> decodeMethod(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); + } - Executable[] queriedMethods = decodeArray(dataReader, Executable.class, () -> decodeReflectionMethod(dataReader, codeInfo, DynamicHub.toClass(declaringType))); - MethodDescriptor[] hiddenMethods = decodeArray(dataReader, MethodDescriptor.class, () -> decodeSimpleMethod(dataReader, codeInfo, DynamicHub.toClass(declaringType))); - return Pair.create(queriedMethods, hiddenMethods); - } else { - return Pair.create(new Executable[0], new MethodDescriptor[0]); - } + @Override + public Constructor[] parseConstructors(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Constructor.class, (i) -> decodeConstructor(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); } @Override - public MethodDescriptor[] getAllReachableMethods() { - if (!SubstrateOptions.IncludeMethodData.getValue()) { - return new MethodDescriptor[0]; - } + public Class[] parseClasses(byte[] encoding) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Class.class, (i) -> decodeType(reader, codeInfo)); + } + @Override + public Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, byte[] encoding) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); - byte[] data = ImageSingletons.lookup(MethodMetadataEncoding.class).getMethodsEncoding(); - UnsafeArrayTypeReader dataReader = UnsafeArrayTypeReader.create(data, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); - List allMethods = new ArrayList<>(); - for (int i = 0; i < ImageSingletons.lookup(MethodMetadataEncoding.class).getIndexEncoding().length / Integer.BYTES; ++i) { - int dataOffset = getOffset(i); - if (dataOffset != NO_METHOD_METADATA) { - dataReader.setByteIndex(dataOffset); - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - /* Skip the queried methods data */ - decodeArray(dataReader, Executable.class, () -> decodeReflectionMethod(dataReader, codeInfo, null)); - decodeArray(dataReader, MethodDescriptor.class, () -> decodeSimpleMethod(dataReader, codeInfo, null)); - } - Class declaringClass = decodeType(dataReader, codeInfo); - if (declaringClass != null) { - allMethods.addAll(Arrays.asList(decodeArray(dataReader, MethodDescriptor.class, () -> decodeSimpleMethod(dataReader, codeInfo, declaringClass)))); - } - } - } - return allMethods.toArray(new MethodDescriptor[0]); + return decodeArray(reader, Target_java_lang_reflect_RecordComponent.class, (i) -> decodeRecordComponent(reader, codeInfo, DynamicHub.toClass(declaringType))); + } + + @Override + public Parameter[] parseReflectParameters(Executable executable, byte[] encoding) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Parameter.class, (i) -> decodeReflectParameter(reader, codeInfo, executable, i)); } @Override - public long getMetadataByteLength() { - MethodMetadataEncoding encoding = ImageSingletons.lookup(MethodMetadataEncoding.class); - return encoding.getMethodsEncoding().length + encoding.getIndexEncoding().length; + public Object[] parseEnclosingMethod(byte[] encoding) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + Class declaringClass = decodeType(reader, codeInfo); + String name = decodeName(reader, codeInfo); + String descriptor = decodeName(reader, codeInfo); + return new Object[]{declaringClass, name, descriptor}; } - private static int getOffset(int typeID) { - MethodMetadataEncoding encoding = ImageSingletons.lookup(MethodMetadataEncoding.class); - byte[] index = encoding.getIndexEncoding(); - UnsafeArrayTypeReader indexReader = UnsafeArrayTypeReader.create(index, Integer.BYTES * typeID, ByteArrayReader.supportsUnalignedMemoryAccess()); - return indexReader.getS4(); + @Override + public boolean isHidingMethod(int modifiers) { + return (modifiers & HIDING_FLAG_MASK) != 0; } - private static Executable decodeReflectionMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { + 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; + if (inHeap) { + Field field = (Field) decodeObject(buf, info); + if (publicOnly && !Modifier.isPublic(field.getModifiers())) { + return null; + } + return field; + } + boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; + modifiers &= ~COMPLETE_FLAG_MASK; + String name = decodeName(buf, info); - Class[] parameterTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); + if (!complete) { + if (reflectOnly) { + 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); + } else { + field.constructorJDK11OrEarlier(declaringClass, name, null, 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); + byte[] typeAnnotations = decodeByteArray(buf); + int offset = buf.getSVInt(); + String deletedReason = decodeName(buf, info); + if (publicOnly && !Modifier.isPublic(modifiers)) { + return null; + } + + Target_java_lang_reflect_Field field = new Target_java_lang_reflect_Field(); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + field.constructorJDK17OrLater(declaringClass, name, type, modifiers, trustedFinal, -1, signature, annotations); + } else { + field.constructorJDK11OrEarlier(declaringClass, name, type, modifiers, -1, signature, annotations); + } + field.offset = offset; + field.deletedReason = deletedReason; + SubstrateUtil.cast(field, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations = typeAnnotations; + return SubstrateUtil.cast(field, Field.class); + } + + private static Method decodeMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { + return (Method) decodeExecutable(buf, info, declaringClass, publicOnly, reflectOnly, true); + } + + private static Constructor decodeConstructor(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { + return (Constructor) decodeExecutable(buf, info, declaringClass, publicOnly, reflectOnly, false); + } + + private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly, boolean isMethod) { int modifiers = buf.getUVInt(); - Class returnType = decodeType(buf, info); - Class[] exceptionTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); + boolean inHeap = (modifiers & IN_HEAP_FLAG_MASK) != 0; + if (inHeap) { + Executable executable = (Executable) decodeObject(buf, info); + if (publicOnly && !Modifier.isPublic(executable.getModifiers())) { + return null; + } + return executable; + } + boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; + boolean hiding = (modifiers & HIDING_FLAG_MASK) != 0; + modifiers &= ~COMPLETE_FLAG_MASK; + + String name = isMethod ? decodeName(buf, info) : null; + Class[] parameterTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf, info)); + Class returnType = isMethod && hiding ? decodeType(buf, info) : null; + if (!complete) { + if (reflectOnly != hiding) { + /* + * When querying for reflection methods, we want the hiding methods but not the + * reachable methods. When querying for reachable methods, we want the reachable + * methods but not the hiding methods. + */ + return null; + } + if (isMethod) { + Target_java_lang_reflect_Method method = new Target_java_lang_reflect_Method(); + method.constructor(declaringClass, name, parameterTypes, returnType, null, modifiers, -1, null, null, null, null); + return SubstrateUtil.cast(method, Executable.class); + } else { + Target_java_lang_reflect_Constructor constructor = new Target_java_lang_reflect_Constructor(); + constructor.constructor(declaringClass, parameterTypes, null, modifiers, -1, null, null, null); + return SubstrateUtil.cast(constructor, Executable.class); + } + } + assert !hiding; + returnType = isMethod ? decodeType(buf, info) : null; + Class[] exceptionTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf, info)); String signature = decodeName(buf, info); byte[] annotations = decodeByteArray(buf); byte[] parameterAnnotations = decodeByteArray(buf); + byte[] annotationDefault = isMethod && declaringClass.isAnnotation() ? decodeByteArray(buf) : null; byte[] typeAnnotations = decodeByteArray(buf); - boolean hasRealParameterData = buf.getU1() == 1; - ReflectParameterDescriptor[] reflectParameters = hasRealParameterData ? decodeArray(buf, ReflectParameterDescriptor.class, () -> decodeReflectParameter(buf, info)) : null; + byte[] reflectParameters = decodeByteArray(buf); + Object accessor = decodeObject(buf, info); + if (publicOnly && !Modifier.isPublic(modifiers)) { + return null; + } Target_java_lang_reflect_Executable executable; - if (name.equals("")) { - assert returnType == void.class; + if (isMethod) { + Target_java_lang_reflect_Method method = new Target_java_lang_reflect_Method(); + method.constructor(declaringClass, name, parameterTypes, returnType, exceptionTypes, modifiers, -1, signature, annotations, parameterAnnotations, annotationDefault); + method.methodAccessor = (Target_jdk_internal_reflect_MethodAccessor) accessor; + executable = SubstrateUtil.cast(method, Target_java_lang_reflect_Executable.class); + } else { Target_java_lang_reflect_Constructor constructor = new Target_java_lang_reflect_Constructor(); constructor.constructor(declaringClass, parameterTypes, exceptionTypes, modifiers, -1, signature, annotations, parameterAnnotations); + constructor.constructorAccessor = (Target_jdk_internal_reflect_ConstructorAccessor) accessor; executable = SubstrateUtil.cast(constructor, Target_java_lang_reflect_Executable.class); - } else { - Target_java_lang_reflect_Method method = new Target_java_lang_reflect_Method(); - method.constructor(declaringClass, name, parameterTypes, returnType, exceptionTypes, modifiers, -1, signature, annotations, parameterAnnotations, null); - executable = SubstrateUtil.cast(method, Target_java_lang_reflect_Executable.class); - } - if (hasQueriedMethods()) { - executable.hasRealParameterData = hasRealParameterData; - if (hasRealParameterData) { - fillReflectParameters(executable, reflectParameters); - } - executable.typeAnnotations = typeAnnotations; } + executable.rawParameters = reflectParameters; + SubstrateUtil.cast(executable, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations = typeAnnotations; return SubstrateUtil.cast(executable, Executable.class); } - private static void fillReflectParameters(Target_java_lang_reflect_Executable executable, ReflectParameterDescriptor[] reflectParameters) { - executable.parameters = new Target_java_lang_reflect_Parameter[reflectParameters.length]; - for (int i = 0; i < reflectParameters.length; ++i) { - executable.parameters[i] = new Target_java_lang_reflect_Parameter(); - executable.parameters[i].constructor(reflectParameters[i].getName(), reflectParameters[i].getModifiers(), executable, i); - } + private static Target_java_lang_reflect_RecordComponent decodeRecordComponent(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { + String name = decodeName(buf, info); + Class type = decodeType(buf, info); + String signature = decodeName(buf, info); + Method accessor = (Method) decodeObject(buf, info); + byte[] annotations = decodeByteArray(buf); + byte[] typeAnnotations = decodeByteArray(buf); + + Target_java_lang_reflect_RecordComponent recordComponent = new Target_java_lang_reflect_RecordComponent(); + recordComponent.clazz = declaringClass; + recordComponent.name = name; + recordComponent.type = type; + recordComponent.signature = signature; + recordComponent.accessor = accessor; + recordComponent.annotations = annotations; + recordComponent.typeAnnotations = typeAnnotations; + return recordComponent; } - private static MethodDescriptor decodeSimpleMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { + private static Parameter decodeReflectParameter(UnsafeArrayTypeReader buf, CodeInfo info, Executable executable, int i) { String name = decodeName(buf, info); - Class[] paramTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); - return new MethodDescriptor(declaringClass, name, paramTypes); + int modifiers = buf.getUVInt(); + + Target_java_lang_reflect_Parameter parameter = new Target_java_lang_reflect_Parameter(); + parameter.constructor(name, modifiers, executable, i); + return SubstrateUtil.cast(parameter, Parameter.class); } private static Class decodeType(UnsafeArrayTypeReader buf, CodeInfo info) { @@ -239,46 +340,37 @@ private static String decodeName(UnsafeArrayTypeReader buf, CodeInfo info) { return name == null ? null : name.intern(); } + private static Object decodeObject(UnsafeArrayTypeReader buf, CodeInfo info) { + int objectIndex = buf.getSVInt(); + if (objectIndex == NULL_OBJECT) { + return null; + } + return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoObjectConstants(info), objectIndex); + } + @SuppressWarnings("unchecked") - private static T[] decodeArray(UnsafeArrayTypeReader buf, Class elementType, Supplier elementDecoder) { + private static T[] decodeArray(UnsafeArrayTypeReader buf, Class elementType, Function elementDecoder) { int length = buf.getUVInt(); T[] result = (T[]) Array.newInstance(elementType, length); + int valueCount = 0; for (int i = 0; i < length; ++i) { - result[i] = elementDecoder.get(); + T element = elementDecoder.apply(i); + if (element != null) { + result[valueCount++] = element; + } } - return result; + return Arrays.copyOf(result, valueCount); } private static byte[] decodeByteArray(UnsafeArrayTypeReader buf) { int length = buf.getUVInt(); + if (length == 0) { + return null; + } byte[] result = new byte[length]; for (int i = 0; i < length; ++i) { result[i] = (byte) buf.getS1(); } return result; } - - private static ReflectParameterDescriptor decodeReflectParameter(UnsafeArrayTypeReader buf, CodeInfo info) { - String name = decodeName(buf, info); - int modifiers = buf.getS4(); - return new ReflectParameterDescriptor(name, modifiers); - } - - public static class ReflectParameterDescriptor { - private final String name; - private final int modifiers; - - public ReflectParameterDescriptor(String name, int modifiers) { - this.name = name; - this.modifiers = modifiers; - } - - public String getName() { - return name; - } - - public int getModifiers() { - return modifiers; - } - } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java index e988e4f00540..a9da42b99f9b 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java @@ -26,12 +26,20 @@ import java.lang.reflect.AccessibleObject; +import org.graalvm.nativeimage.ImageSingletons; + import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.annotate.UnknownObjectField; import com.oracle.svm.core.jdk.JDK11OrEarlier; import com.oracle.svm.core.jdk.JDK17OrLater; +import com.oracle.svm.hosted.image.NativeImageCodeCache; + +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaField; @TargetClass(value = AccessibleObject.class) public final class Target_java_lang_reflect_AccessibleObject { @@ -46,6 +54,22 @@ public final class Target_java_lang_reflect_AccessibleObject { @TargetElement(onlyWith = JDK17OrLater.class) // volatile Object accessCheckCache; - @Alias // - native Target_java_lang_reflect_AccessibleObject getRoot(); + @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = TypeAnnotationsComputer.class) // + @UnknownObjectField(types = {byte[].class}) byte[] typeAnnotations; + + @Alias + native AccessibleObject getRoot(); + + static class TypeAnnotationsComputer implements RecomputeFieldValue.CustomFieldValueComputer { + + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterCompilation; + } + + @Override + public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getTypeAnnotationsEncoding((AccessibleObject) receiver); + } + } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java index 4527c1b7ac15..f1778a3b3937 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java @@ -26,59 +26,43 @@ import static com.oracle.svm.core.annotate.TargetElement.CONSTRUCTOR_NAME; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.AnnotatedType; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; +import java.lang.reflect.Executable; + +import org.graalvm.nativeimage.ImageSingletons; -import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; 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.annotate.UnknownObjectField; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.image.NativeImageCodeCache; import com.oracle.svm.reflect.hosted.ExecutableAccessorComputer; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; -import sun.reflect.annotation.TypeAnnotation; -import sun.reflect.generics.repository.ConstructorRepository; @TargetClass(value = Constructor.class) public final class Target_java_lang_reflect_Constructor { - @Alias ConstructorRepository genericInfo; - - @Alias private Class[] parameterTypes; - - @Alias @RecomputeFieldValue(kind = Kind.Reset)// - private byte[] annotations; + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotationsComputer.class)// + @UnknownObjectField(types = {byte[].class}) byte[] annotations; - @Alias @RecomputeFieldValue(kind = Kind.Reset)// - private byte[] parameterAnnotations; + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = ParameterAnnotationsComputer.class)// + @UnknownObjectField(types = {byte[].class}) byte[] parameterAnnotations; @Alias // @RecomputeFieldValue(kind = Kind.Custom, declClass = ExecutableAccessorComputer.class) // Target_jdk_internal_reflect_ConstructorAccessor constructorAccessor; - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = ConstructorAnnotatedReceiverTypeComputer.class) // - AnnotatedType annotatedReceiverType; - @Alias @TargetElement(name = CONSTRUCTOR_NAME) @SuppressWarnings("hiding") - public native void constructor(Class declaringClass, - Class[] parameterTypes, - Class[] checkedExceptions, - int modifiers, - int slot, - String signature, - byte[] annotations, + public native void constructor(Class declaringClass, Class[] parameterTypes, Class[] checkedExceptions, int modifiers, int slot, String signature, byte[] annotations, byte[] parameterAnnotations); @Alias @@ -92,73 +76,29 @@ Target_jdk_internal_reflect_ConstructorAccessor acquireConstructorAccessor() { return constructorAccessor; } - @Alias // - public native Class getDeclaringClass(); - - @Substitute - public Annotation[][] getParameterAnnotations() { - Target_java_lang_reflect_Executable self = SubstrateUtil.cast(this, Target_java_lang_reflect_Executable.class); - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(self); - if (holder.parameterAnnotations != null) { - return holder.parameterAnnotations; - } - return self.sharedGetParameterAnnotations(parameterTypes, parameterAnnotations); - } - - @Substitute - public AnnotatedType getAnnotatedReceiverType() { - Target_java_lang_reflect_Constructor holder = ReflectionHelper.getHolder(this); - if (holder.annotatedReceiverType != null) { - return holder.annotatedReceiverType; - } - Class thisDeclClass = getDeclaringClass(); - Class enclosingClass = thisDeclClass.getEnclosingClass(); - - if (enclosingClass == null) { - // A Constructor for a top-level class - return null; - } - - Class outerDeclaringClass = thisDeclClass.getDeclaringClass(); - if (outerDeclaringClass == null) { - // A constructor for a local or anonymous class - return null; - } + static class AnnotationsComputer implements RecomputeFieldValue.CustomFieldValueComputer { - // Either static nested or inner class - if (Modifier.isStatic(thisDeclClass.getModifiers())) { - // static nested - return null; + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterCompilation; } - if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - // A Constructor for an inner class - return Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedType(SubstrateUtil.cast(holder, Target_java_lang_reflect_Executable.class).typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - thisDeclClass, - enclosingClass, - TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER); + @Override + public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); } - throw VMError.shouldNotReachHere(); } - /** - * The Constructor.annotatedReceiverType computation is needed, even though there is a similar - * computation for Executable.annotatedReceiverType, because the Constructor class overrides - * Executable.getAnnotatedReceiverType(). - */ - public static final class ConstructorAnnotatedReceiverTypeComputer implements CustomFieldValueComputer { + static class ParameterAnnotationsComputer implements RecomputeFieldValue.CustomFieldValueComputer { + @Override public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; + return RecomputeFieldValue.ValueAvailability.AfterCompilation; } @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Constructor constructor = (Constructor) receiver; - return constructor.getAnnotatedReceiverType(); + return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getParameterAnnotationsEncoding((Executable) receiver); } } - } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java index f56e3a90520f..0bdfee943d97 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java @@ -25,304 +25,61 @@ package com.oracle.svm.reflect.target; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.AnnotatedType; import java.lang.reflect.Executable; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; +import java.lang.reflect.Parameter; import java.util.Map; -import java.util.function.Supplier; + +import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; 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.util.VMError; -import com.oracle.svm.hosted.LinkAtBuildTimeSupport; -import com.oracle.svm.reflect.hosted.ReflectionObjectReplacer; +import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; -import sun.reflect.annotation.TypeAnnotation; @TargetClass(value = Executable.class) public final class Target_java_lang_reflect_Executable { - /** - * The parameters field doesn't need a value recomputation. Its value is pre-loaded in the - * {@link ReflectionObjectReplacer}. - */ - @Alias // - Target_java_lang_reflect_Parameter[] parameters; - - @Alias // - boolean hasRealParameterData; + @Alias @RecomputeFieldValue(kind = Kind.Reset)// + Parameter[] parameters; - /** - * The declaredAnnotations field doesn't need a value recomputation. Its value is pre-loaded in - * the {@link ReflectionObjectReplacer}. - */ - @Alias // + @Alias @RecomputeFieldValue(kind = Kind.Reset)// Map, Annotation> declaredAnnotations; - @Inject @RecomputeFieldValue(kind = Kind.Reset) // - byte[] typeAnnotations; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = ParameterAnnotationsComputer.class) // - Annotation[][] parameterAnnotations; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedReceiverTypeComputer.class) // - Object annotatedReceiverType; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedParameterTypesComputer.class) // - Object[] annotatedParameterTypes; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedReturnTypeComputer.class) // - Object annotatedReturnType; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedExceptionTypesComputer.class) // - Object[] annotatedExceptionTypes; - - @Alias // - public native int getModifiers(); - - @Alias // - native byte[] getAnnotationBytes(); - - @Alias // - public native Class getDeclaringClass(); - - @Alias // - native Type[] getAllGenericParameterTypes(); - - @Alias // - public native Type[] getGenericExceptionTypes(); - - @Alias // - private native Target_java_lang_reflect_Parameter[] synthesizeAllParams(); - - @Substitute - private Target_java_lang_reflect_Parameter[] privateGetParameters() { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.parameters != null) { - return holder.parameters; - } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - assert !hasRealParameterData; - holder.parameters = synthesizeAllParams(); - return holder.parameters; - } - throw VMError.shouldNotReachHere(); - } + @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = RawParametersComputer.class)// + @UnknownObjectField(types = {byte[].class}) byte[] rawParameters; @Substitute - Map, Annotation> declaredAnnotations() { - Map, Annotation> declAnnos; - if ((declAnnos = declaredAnnotations) == null) { - if (!MethodMetadataDecoderImpl.hasQueriedMethods()) { - throw VMError.shouldNotReachHere(); - } - synchronized (this) { - if ((declAnnos = declaredAnnotations) == null) { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - declAnnos = Target_sun_reflect_annotation_AnnotationParser.parseAnnotations( - holder.getAnnotationBytes(), - new Target_jdk_internal_reflect_ConstantPool(), - holder.getDeclaringClass()); - declaredAnnotations = declAnnos; - } - } - } - return declAnnos; - } - - @Alias - native Annotation[][] sharedGetParameterAnnotations(Class[] parameterTypes, byte[] annotations); - - @Substitute - @SuppressWarnings({"unused", "hiding", "static-method"}) - Annotation[][] parseParameterAnnotations(byte[] parameterAnnotations) { - if (!MethodMetadataDecoderImpl.hasQueriedMethods()) { - throw VMError.shouldNotReachHere(); - } - return Target_sun_reflect_annotation_AnnotationParser.parseParameterAnnotations( - parameterAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - getDeclaringClass()); - } - - @Substitute - public AnnotatedType getAnnotatedReceiverType() { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.annotatedReceiverType != null) { - return (AnnotatedType) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedReceiverType); - } - if (Modifier.isStatic(this.getModifiers())) { + private Parameter[] getParameters0() { + if (rawParameters == null) { return null; } - if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - AnnotatedType annotatedRecvType = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedType(typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - getDeclaringClass(), - getDeclaringClass(), - TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER); - holder.annotatedReceiverType = annotatedRecvType; - return annotatedRecvType; - } - throw VMError.shouldNotReachHere(); + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseReflectParameters(SubstrateUtil.cast(this, Executable.class), rawParameters); } @Substitute - public AnnotatedType[] getAnnotatedParameterTypes() { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.annotatedParameterTypes != null) { - return (AnnotatedType[]) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedParameterTypes); - } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - AnnotatedType[] annotatedParamTypes = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedTypes(typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - getDeclaringClass(), - getAllGenericParameterTypes(), - TypeAnnotation.TypeAnnotationTarget.METHOD_FORMAL_PARAMETER); - holder.annotatedParameterTypes = annotatedParamTypes; - return annotatedParamTypes; - } - throw VMError.shouldNotReachHere(); + byte[] getTypeAnnotationBytes0() { + return SubstrateUtil.cast(this, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations; } - @Substitute - public AnnotatedType getAnnotatedReturnType0(@SuppressWarnings("unused") Type returnType) { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.annotatedReturnType != null) { - return (AnnotatedType) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedReturnType); - } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - AnnotatedType annotatedRetType = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedType(typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - getDeclaringClass(), - returnType, - TypeAnnotation.TypeAnnotationTarget.METHOD_RETURN); - holder.annotatedReturnType = annotatedRetType; - return annotatedRetType; - } - throw VMError.shouldNotReachHere(); - } + static class RawParametersComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Substitute - public AnnotatedType[] getAnnotatedExceptionTypes() { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.annotatedExceptionTypes != null) { - return (AnnotatedType[]) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedExceptionTypes); - } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - AnnotatedType[] annotatedExcTypes = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedTypes(typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - getDeclaringClass(), - getGenericExceptionTypes(), - TypeAnnotation.TypeAnnotationTarget.THROWS); - holder.annotatedExceptionTypes = annotatedExcTypes; - return annotatedExcTypes; - } - throw VMError.shouldNotReachHere(); - } - - public static final class ParameterAnnotationsComputer implements CustomFieldValueComputer { @Override public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; + return RecomputeFieldValue.ValueAvailability.AfterCompilation; } @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return executable.getParameterAnnotations(); - } - } - - public static final class AnnotatedReceiverTypeComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedReceiverType, executable); - } - } - - public static final class AnnotatedParameterTypesComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedParameterTypes, executable); - } - } - - public static final class AnnotatedReturnTypeComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedReturnType, executable); - } - } - - public static final class AnnotatedExceptionTypesComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedExceptionTypes, executable); - } - } - - private static final class AnnotatedTypeEncoder { - static Object encodeAnnotationTypes(Supplier supplier, Executable executable) { - try { - return supplier.get(); - } catch (InternalError e) { - return e; - } catch (LinkageError e) { - if (!LinkAtBuildTimeSupport.singleton().linkAtBuildTime(executable.getDeclaringClass())) { - return e; - } - String message = "Encountered an error while processing annotated types for type: " + executable.getDeclaringClass().getName() + ", executable: " + executable.getName() + ". " + - LinkAtBuildTimeSupport.singleton().errorMessageFor(executable.getDeclaringClass()); - throw new UnsupportedOperationException(message, e); - } - } - - static Object decodeAnnotationTypes(Object value) { - if (value == null) { - return null; - } else if (value instanceof AnnotatedType || value instanceof AnnotatedType[]) { - return value; - } else if (value instanceof LinkageError) { - throw (LinkageError) value; - } else if (value instanceof InternalError) { - throw (InternalError) value; - } else { - throw VMError.shouldNotReachHere("Unexpected value while decoding annotation types: " + value.toString()); - } + return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getReflectParametersEncoding((Executable) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java index 7df97126af7e..045580fa6599 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java @@ -24,11 +24,14 @@ */ package com.oracle.svm.reflect.target; +import static com.oracle.svm.core.annotate.TargetElement.CONSTRUCTOR_NAME; + import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedType; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.util.Map; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.svm.core.SubstrateUtil; @@ -40,55 +43,62 @@ 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.annotate.UnknownObjectField; +import com.oracle.svm.core.jdk.JDK11OrEarlier; +import com.oracle.svm.core.jdk.JDK17OrLater; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.image.NativeImageCodeCache; import com.oracle.svm.reflect.hosted.FieldOffsetComputer; -import com.oracle.svm.reflect.hosted.ReflectionObjectReplacer; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; -import sun.reflect.generics.repository.FieldRepository; @TargetClass(value = Field.class) public final class Target_java_lang_reflect_Field { - @Alias FieldRepository genericInfo; - - /** Field accessor objects are created on demand at image runtime. */ + /** Field accessor and annotation objects are created on demand at image runtime. */ @Alias @RecomputeFieldValue(kind = Kind.Reset) // Target_jdk_internal_reflect_FieldAccessor fieldAccessor; @Alias @RecomputeFieldValue(kind = Kind.Reset) // Target_jdk_internal_reflect_FieldAccessor overrideFieldAccessor; - @Alias // - boolean override; - - /** - * The declaredAnnotations field doesn't need a value recomputation. Its value is pre-loaded in - * the {@link ReflectionObjectReplacer}. - */ - @Alias // + @Alias @RecomputeFieldValue(kind = Kind.Reset)// Map, Annotation> declaredAnnotations; - @Alias // - Target_java_lang_reflect_Field root; + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = AnnotationsComputer.class) // + @UnknownObjectField(types = {byte[].class}) byte[] annotations; @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = FieldOffsetComputer.class) // public int offset; - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedTypeComputer.class) // - AnnotatedType annotatedType; - /** If non-null, the field was deleted via substitution and this string provides the reason. */ @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = FieldDeletionReasonComputer.class) // String deletedReason; + @Alias // + boolean override; + + @Alias // + Target_java_lang_reflect_Field root; + @Alias native Target_java_lang_reflect_Field copy(); @Alias native Target_jdk_internal_reflect_FieldAccessor acquireFieldAccessor(boolean overrideFinalCheck); + @Alias + @TargetElement(name = CONSTRUCTOR_NAME, onlyWith = JDK17OrLater.class) + @SuppressWarnings("hiding") + native void constructorJDK17OrLater(Class declaringClass, String name, Class type, int modifiers, boolean trustedFinal, int slot, String signature, byte[] annotations); + + @Alias + @TargetElement(name = CONSTRUCTOR_NAME, onlyWith = JDK11OrEarlier.class) + @SuppressWarnings("hiding") + native void constructorJDK11OrEarlier(Class declaringClass, String name, Class type, int modifiers, int slot, String signature, byte[] annotations); + /** @see com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor#deleteErrorMessage */ @Substitute Target_jdk_internal_reflect_FieldAccessor getFieldAccessor(@SuppressWarnings("unused") Object obj) { @@ -106,18 +116,11 @@ Target_jdk_internal_reflect_FieldAccessor getFieldAccessor(@SuppressWarnings("un } @Substitute - Map, Annotation> declaredAnnotations() { - Target_java_lang_reflect_Field holder = ReflectionHelper.getHolder(this); - return ReflectionHelper.requireNonNull(holder.declaredAnnotations, "Declared annotations must be computed during native image generation."); - } - - @Substitute - public AnnotatedType getAnnotatedType() { - Target_java_lang_reflect_Field holder = ReflectionHelper.getHolder(this); - return ReflectionHelper.requireNonNull(holder.annotatedType, "Annotated type must be computed during native image generation."); + private byte[] getTypeAnnotationBytes0() { + return SubstrateUtil.cast(this, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations; } - public static final class AnnotatedTypeComputer implements CustomFieldValueComputer { + public static final class FieldDeletionReasonComputer implements CustomFieldValueComputer { @Override public RecomputeFieldValue.ValueAvailability valueAvailability() { return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; @@ -125,22 +128,22 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Field field = (Field) receiver; - return field.getAnnotatedType(); + ResolvedJavaField field = metaAccess.lookupJavaField((Field) receiver); + Delete annotation = GuardedAnnotationAccess.getAnnotation(field, Delete.class); + return (annotation != null) ? annotation.value() : null; } } - public static final class FieldDeletionReasonComputer implements CustomFieldValueComputer { + static class AnnotationsComputer implements RecomputeFieldValue.CustomFieldValueComputer { + @Override public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; + return RecomputeFieldValue.ValueAvailability.AfterCompilation; } @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - ResolvedJavaField field = metaAccess.lookupJavaField((Field) receiver); - Delete annotation = GuardedAnnotationAccess.getAnnotation(field, Delete.class); - return (annotation != null) ? annotation.value() : null; + return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java index 6163dfe0e6e9..7707edfcbe19 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java @@ -26,59 +26,47 @@ import static com.oracle.svm.core.annotate.TargetElement.CONSTRUCTOR_NAME; -import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Executable; import java.lang.reflect.Method; -import com.oracle.svm.core.SubstrateUtil; +import org.graalvm.nativeimage.ImageSingletons; + import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; 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.annotate.UnknownObjectField; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.image.NativeImageCodeCache; import com.oracle.svm.reflect.hosted.ExecutableAccessorComputer; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; -import sun.reflect.generics.repository.MethodRepository; @TargetClass(value = Method.class) public final class Target_java_lang_reflect_Method { - @Alias MethodRepository genericInfo; - - @Alias private Class[] parameterTypes; + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotationsComputer.class)// + @UnknownObjectField(types = {byte[].class}) byte[] annotations; - @Alias @RecomputeFieldValue(kind = Kind.Reset)// - private byte[] annotations; + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = ParameterAnnotationsComputer.class)// + @UnknownObjectField(types = {byte[].class}) byte[] parameterAnnotations; - @Alias @RecomputeFieldValue(kind = Kind.Reset)// - private byte[] parameterAnnotations; + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotationDefaultComputer.class)// + @UnknownObjectField(types = {byte[].class}) byte[] annotationDefault; @Alias // @RecomputeFieldValue(kind = Kind.Custom, declClass = ExecutableAccessorComputer.class) // Target_jdk_internal_reflect_MethodAccessor methodAccessor; - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = DefaultValueComputer.class) // - Object defaultValue; - @Alias @TargetElement(name = CONSTRUCTOR_NAME) @SuppressWarnings("hiding") - public native void constructor(Class declaringClass, - String name, - Class[] parameterTypes, - Class returnType, - Class[] checkedExceptions, - int modifiers, - int slot, - String signature, - byte[] annotations, - byte[] parameterAnnotations, - byte[] annotationDefault); + public native void constructor(Class declaringClass, String name, Class[] parameterTypes, Class returnType, Class[] checkedExceptions, int modifiers, int slot, String signature, + byte[] annotations, byte[] parameterAnnotations, byte[] annotationDefault); @Alias native Target_java_lang_reflect_Method copy(); @@ -91,33 +79,42 @@ public Target_jdk_internal_reflect_MethodAccessor acquireMethodAccessor() { return methodAccessor; } - @Substitute - public Annotation[][] getParameterAnnotations() { - Target_java_lang_reflect_Executable self = SubstrateUtil.cast(this, Target_java_lang_reflect_Executable.class); - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(self); - if (holder.parameterAnnotations != null) { - return holder.parameterAnnotations; + static class AnnotationsComputer implements RecomputeFieldValue.CustomFieldValueComputer { + + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterCompilation; } - return self.sharedGetParameterAnnotations(parameterTypes, parameterAnnotations); - } - @Substitute - public Object getDefaultValue() { - Target_java_lang_reflect_Method holder = ReflectionHelper.getHolder(this); - return holder.defaultValue; + @Override + public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); + } } - public static final class DefaultValueComputer implements CustomFieldValueComputer { + static class ParameterAnnotationsComputer implements RecomputeFieldValue.CustomFieldValueComputer { + @Override public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; + return RecomputeFieldValue.ValueAvailability.AfterCompilation; } @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Method method = (Method) receiver; - return method.getDefaultValue(); + return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getParameterAnnotationsEncoding((Executable) receiver); } } + static class AnnotationDefaultComputer implements RecomputeFieldValue.CustomFieldValueComputer { + + @Override + public RecomputeFieldValue.ValueAvailability valueAvailability() { + return RecomputeFieldValue.ValueAvailability.AfterCompilation; + } + + @Override + public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getAnnotationDefaultEncoding((Method) receiver); + } + } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Parameter.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Parameter.java index d4483747a870..9b9d03d68b31 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Parameter.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Parameter.java @@ -24,18 +24,22 @@ */ package com.oracle.svm.reflect.target; +import java.lang.reflect.Executable; import java.lang.reflect.Parameter; +import java.lang.reflect.Type; import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; @TargetClass(Parameter.class) public final class Target_java_lang_reflect_Parameter { + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// + private Type parameterTypeCache; + @Alias // @TargetElement(name = TargetElement.CONSTRUCTOR_NAME) - public native void constructor(String name, - int modifiers, - Target_java_lang_reflect_Executable executable, - int index); + public native void constructor(String name, int modifiers, Executable executable, int index); } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java index a240a6c7d8cb..f46bed4838f1 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java @@ -24,15 +24,22 @@ */ package com.oracle.svm.reflect.target; +import static com.oracle.svm.reflect.target.Util_java_lang_reflect_ReflectAccess.copyAccessibleObject; +import static com.oracle.svm.reflect.target.Util_java_lang_reflect_ReflectAccess.copyExecutable; + import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; /** - * These substitutions are needed to set the genericInfo field on Method, Field, Constructor. The - * genericInfo is eagerly loaded at image build time. The Method, Field, Constructor elements, when - * accessed via reflection a copy is returned. The original implementation of copy() doesn't - * propagate the genericInfo. + * These substitutions are needed to set the injected fields on Method, Field, Constructor. The + * metadata for those fields is generated at image build time. The Method, Field, Constructor + * elements, when accessed via reflection a copy is returned. The original implementation of copy() + * doesn't propagate the injected fields. + * + * We substitute these methods instead of the copy constructors on the reflection objects to avoid + * having to copy the contents of those methods. This is ok since the ReflectAccess methods are the + * only users of those copy constructors. */ @TargetClass(className = "java.lang.reflect.ReflectAccess") @@ -42,8 +49,7 @@ public final class Target_java_lang_reflect_ReflectAccess { @SuppressWarnings("static-method") public Target_java_lang_reflect_Method copyMethod(Target_java_lang_reflect_Method method) { Target_java_lang_reflect_Method copy = method.copy(); - copy.genericInfo = method.genericInfo; - Util_java_lang_reflect_ReflectAccess.copyExecutable(SubstrateUtil.cast(copy, Target_java_lang_reflect_Executable.class), + copyExecutable(SubstrateUtil.cast(copy, Target_java_lang_reflect_Executable.class), SubstrateUtil.cast(method, Target_java_lang_reflect_Executable.class)); return copy; } @@ -52,7 +58,10 @@ public Target_java_lang_reflect_Method copyMethod(Target_java_lang_reflect_Metho @SuppressWarnings("static-method") public Target_java_lang_reflect_Field copyField(Target_java_lang_reflect_Field field) { Target_java_lang_reflect_Field copy = field.copy(); - copy.genericInfo = field.genericInfo; + copy.offset = field.offset; + copy.deletedReason = field.deletedReason; + copyAccessibleObject(SubstrateUtil.cast(copy, Target_java_lang_reflect_AccessibleObject.class), + SubstrateUtil.cast(field, Target_java_lang_reflect_AccessibleObject.class)); return copy; } @@ -60,8 +69,7 @@ public Target_java_lang_reflect_Field copyField(Target_java_lang_reflect_Field f @SuppressWarnings("static-method") public Target_java_lang_reflect_Constructor copyConstructor(Target_java_lang_reflect_Constructor constructor) { Target_java_lang_reflect_Constructor copy = constructor.copy(); - copy.genericInfo = constructor.genericInfo; - Util_java_lang_reflect_ReflectAccess.copyExecutable(SubstrateUtil.cast(copy, Target_java_lang_reflect_Executable.class), + copyExecutable(SubstrateUtil.cast(copy, Target_java_lang_reflect_Executable.class), SubstrateUtil.cast(constructor, Target_java_lang_reflect_Executable.class)); return copy; } @@ -69,16 +77,12 @@ public Target_java_lang_reflect_Constructor copyConstructor(Target_java_lang_ref class Util_java_lang_reflect_ReflectAccess { static void copyExecutable(Target_java_lang_reflect_Executable copy, Target_java_lang_reflect_Executable executable) { - if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - /* Isolated to avoid pulling the full signature parsing capabilities from the JDK. */ - copy.parameters = executable.parameters; - } - copy.declaredAnnotations = executable.declaredAnnotations; - copy.parameterAnnotations = executable.parameterAnnotations; - copy.typeAnnotations = executable.typeAnnotations; - copy.annotatedReceiverType = executable.annotatedReceiverType; - copy.annotatedReturnType = executable.annotatedReturnType; - copy.annotatedParameterTypes = executable.annotatedParameterTypes; - copy.annotatedExceptionTypes = executable.annotatedExceptionTypes; + copy.rawParameters = executable.rawParameters; + copyAccessibleObject(SubstrateUtil.cast(copy, Target_java_lang_reflect_AccessibleObject.class), + SubstrateUtil.cast(executable, Target_java_lang_reflect_AccessibleObject.class)); + } + + static void copyAccessibleObject(Target_java_lang_reflect_AccessibleObject copy, Target_java_lang_reflect_AccessibleObject accessibleObject) { + copy.typeAnnotations = accessibleObject.typeAnnotations; } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java index 9032338213bd..e900235c1769 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java @@ -26,19 +26,19 @@ import java.lang.annotation.Annotation; import java.lang.annotation.AnnotationFormatError; -import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import com.oracle.svm.core.annotate.Alias; 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.config.ConfigurationValues; +import com.oracle.svm.core.c.NonmovableArrays; +import com.oracle.svm.core.code.CodeInfoAccess; +import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.VMError; import com.oracle.svm.reflect.hosted.MethodMetadataEncoderImpl; @@ -53,82 +53,7 @@ * a description of the changes and the rationale behind them. */ @TargetClass(AnnotationParser.class) -@SuppressWarnings("unused") public final class Target_sun_reflect_annotation_AnnotationParser { - @Alias - public static native Map, Annotation> parseAnnotations( - byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container); - - @Substitute - private static Map, Annotation> parseAnnotations2( - byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container, - Class[] selectAnnotationClasses) { - Map, Annotation> result = new LinkedHashMap, Annotation>(); - ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); - buf.order(ConfigurationValues.getTarget().arch.getByteOrder()); - int numAnnotations = buf.getShort() & 0xFFFF; - for (int i = 0; i < numAnnotations; i++) { - Annotation a = parseAnnotation2(buf, constPool, container, false, selectAnnotationClasses); - if (a != null) { - Class klass = a.annotationType(); - if (AnnotationType.getInstance(klass).retention() == RetentionPolicy.RUNTIME && - result.put(klass, a) != null) { - throw new AnnotationFormatError( - "Duplicate annotation for class: " + klass + ": " + a); - } - } - } - return result; - } - - @Alias - public static native Annotation[][] parseParameterAnnotations( - byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container); - - @Substitute - private static Annotation[][] parseParameterAnnotations2( - byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container) { - ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); - buf.order(ConfigurationValues.getTarget().arch.getByteOrder()); - int numParameters = buf.get() & 0xFF; - Annotation[][] result = new Annotation[numParameters][]; - - for (int i = 0; i < numParameters; i++) { - int numAnnotations = buf.getShort() & 0xFFFF; - List annotations = new ArrayList(numAnnotations); - for (int j = 0; j < numAnnotations; j++) { - Annotation a = parseAnnotation(buf, constPool, container, false); - if (a != null) { - AnnotationType type = AnnotationType.getInstance( - a.annotationType()); - if (type.retention() == RetentionPolicy.RUNTIME) { - annotations.add(a); - } - } - } - result[i] = annotations.toArray(EMPTY_ANNOTATIONS_ARRAY); - } - return result; - } - - // Checkstyle: stop - @Alias// - private static Annotation[] EMPTY_ANNOTATIONS_ARRAY; - // Checkstyle: resume - - @Alias - static native Annotation parseAnnotation(ByteBuffer buf, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container, - boolean exceptionOnMissingAnnotationClass); @Substitute @SuppressWarnings("unchecked") @@ -193,7 +118,7 @@ public static native Object parseMemberValue(Class memberType, @Substitute private static Object parseClassValue(ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool, - Class container) { + @SuppressWarnings("unused") Class container) { int classIndex = buf.getInt(); try { return constPool.getClassAt(classIndex); @@ -206,12 +131,12 @@ private static Object parseClassValue(ByteBuffer buf, @SuppressWarnings({"unchecked", "rawtypes"}) private static Object parseEnumValue(Class enumType, ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool, - Class container) { + @SuppressWarnings("unused") Class container) { int typeIndex = buf.getInt(); int constNameIndex = buf.getInt(); String constName = constPool.getUTF8At(constNameIndex); - if (enumType != constPool.getClassAt(typeIndex)) { + if (!enumType.isEnum() || enumType != constPool.getClassAt(typeIndex)) { Target_sun_reflect_annotation_AnnotationTypeMismatchExceptionProxy e = new Target_sun_reflect_annotation_AnnotationTypeMismatchExceptionProxy(); e.constructor(enumType.getTypeName() + "." + constName); return e; @@ -248,6 +173,8 @@ private static Object parseConst(int tag, return value == 1; case 's': return constPool.getUTF8At(buf.getInt()); + case 'E': + return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoObjectConstants(CodeInfoTable.getImageCodeInfo()), buf.getInt()); default: throw new AnnotationFormatError( "Invalid member-value tag in annotation: " + tag); @@ -256,7 +183,7 @@ private static Object parseConst(int tag, @Substitute private static Object parseByteArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { byte[] result = new byte[length]; boolean typeMismatch = false; int tag = 0; @@ -275,7 +202,7 @@ private static Object parseByteArray(int length, @Substitute private static Object parseCharArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { char[] result = new char[length]; boolean typeMismatch = false; byte tag = 0; @@ -294,7 +221,7 @@ private static Object parseCharArray(int length, @Substitute private static Object parseDoubleArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { double[] result = new double[length]; boolean typeMismatch = false; int tag = 0; @@ -313,7 +240,7 @@ private static Object parseDoubleArray(int length, @Substitute private static Object parseFloatArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { float[] result = new float[length]; boolean typeMismatch = false; int tag = 0; @@ -332,7 +259,7 @@ private static Object parseFloatArray(int length, @Substitute private static Object parseIntArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { int[] result = new int[length]; boolean typeMismatch = false; int tag = 0; @@ -351,7 +278,7 @@ private static Object parseIntArray(int length, @Substitute private static Object parseLongArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { long[] result = new long[length]; boolean typeMismatch = false; int tag = 0; @@ -370,7 +297,7 @@ private static Object parseLongArray(int length, @Substitute private static Object parseShortArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { short[] result = new short[length]; boolean typeMismatch = false; int tag = 0; @@ -389,7 +316,7 @@ private static Object parseShortArray(int length, @Substitute private static Object parseBooleanArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { boolean[] result = new boolean[length]; boolean typeMismatch = false; int tag = 0; @@ -413,7 +340,7 @@ private static Object parseBooleanArray(int length, @Substitute private static Object parseStringArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { String[] result = new String[length]; boolean typeMismatch = false; int tag = 0; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java index 432d09b858e5..7ac4bc2274b3 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java @@ -35,6 +35,7 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 22af88086e4b..f749eb14ebcc 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -784,6 +784,12 @@ public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField origina final class Target_java_lang_ProcessBuilder { } +@Delete +@TargetClass(className = "java.lang.ProcessBuilder", innerClass = "Redirect", onlyWith = {TruffleBaseFeature.IsEnabled.class, + TruffleBaseFeature.IsCreateProcessDisabled.class}) +final class Target_java_lang_ProcessBuilder_Redirect { +} + /* * If allowProcess() is disabled at build time, then we ensure ObjdumpDisassemblerProvider does not * try to invoke the nonexistent ProcessBuilder. From 1872df9f837b90779bd69e9ea13259b1ca8da9d4 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Wed, 2 Mar 2022 14:32:30 +0100 Subject: [PATCH 2/3] Encode all metadata in a single array --- .../com/oracle/svm/core/hub/DynamicHub.java | 91 ++++++------- ...er.java => ReflectionMetadataDecoder.java} | 20 ++- .../oracle/svm/hosted/ProgressReporter.java | 11 +- .../hosted/image/NativeImageCodeCache.java | 24 ++-- .../reflect/hosted/ReflectionDataBuilder.java | 4 +- .../reflect/hosted/ReflectionMetadata.java | 14 +- ...ava => ReflectionMetadataEncoderImpl.java} | 126 ++++++++++-------- ...ava => ReflectionMetadataDecoderImpl.java} | 44 ++++-- .../target/ReflectionMetadataEncoding.java | 43 ++++++ ...et_java_lang_reflect_AccessibleObject.java | 2 +- .../Target_java_lang_reflect_Constructor.java | 4 +- .../Target_java_lang_reflect_Executable.java | 6 +- .../Target_java_lang_reflect_Field.java | 2 +- .../Target_java_lang_reflect_Method.java | 6 +- ...n_reflect_annotation_AnnotationParser.java | 6 +- 15 files changed, 242 insertions(+), 161 deletions(-) rename substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/{MethodMetadataDecoder.java => ReflectionMetadataDecoder.java} (76%) rename substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/{MethodMetadataEncoderImpl.java => ReflectionMetadataEncoderImpl.java} (90%) rename substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/{MethodMetadataDecoderImpl.java => ReflectionMetadataDecoderImpl.java} (91%) create mode 100644 substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataEncoding.java 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 c2e8a6ce01f2..566880010c5b 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 @@ -71,13 +71,14 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.annotate.UnknownPrimitiveField; import com.oracle.svm.core.classinitialization.ClassInitializationInfo; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; import com.oracle.svm.core.jdk.JDK11OrEarlier; import com.oracle.svm.core.jdk.JDK17OrLater; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.meta.SharedType; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent; import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.LazyFinalReference; @@ -323,24 +324,24 @@ public void setModule(Module module) { @TargetElement(onlyWith = JDK11OrEarlier.class) // private Class newInstanceCallerCache; - @UnknownObjectField(types = {byte[].class}) private byte[] enclosingMethodInfo; + @UnknownPrimitiveField private int enclosingMethodInfoIndex; - @UnknownObjectField(types = {byte[].class}) private byte[] annotations; + @UnknownPrimitiveField private int annotationsIndex; - @UnknownObjectField(types = {byte[].class}) private byte[] typeAnnotations; + @UnknownPrimitiveField private int typeAnnotationsIndex; - @UnknownObjectField(types = {byte[].class}) byte[] fieldsEncoding; + @UnknownPrimitiveField int fieldsEncodingIndex; - @UnknownObjectField(types = {byte[].class}) byte[] methodsEncoding; + @UnknownPrimitiveField int methodsEncodingIndex; - @UnknownObjectField(types = {byte[].class}) byte[] constructorsEncoding; + @UnknownPrimitiveField int constructorsEncodingIndex; - @UnknownObjectField(types = {byte[].class}) byte[] classesEncoding; + @UnknownPrimitiveField int classesEncodingIndex; @TargetElement(onlyWith = JDK17OrLater.class)// - @UnknownObjectField(types = {byte[].class}) byte[] recordComponentsEncoding; + @UnknownPrimitiveField int recordComponentsEncodingIndex; - @UnknownObjectField(types = {byte[].class}) byte[] permittedSubclassesEncoding; + @UnknownPrimitiveField int permittedSubclassesEncodingIndex; @Platforms(Platform.HOSTED_ONLY.class) public DynamicHub(Class hostedJavaClass, String name, HubType hubType, ReferenceType referenceType, Object isLocalClass, Object isAnonymousClass, DynamicHub superType, DynamicHub componentHub, @@ -475,48 +476,48 @@ public String getSignature() { } @Platforms(Platform.HOSTED_ONLY.class) - public void setFieldsEncoding(byte[] encoding) { - this.fieldsEncoding = encoding; + public void setFieldsEncodingIndex(int encodingIndex) { + this.fieldsEncodingIndex = encodingIndex; } @Platforms(Platform.HOSTED_ONLY.class) - public void setMethodsEncoding(byte[] encoding) { - this.methodsEncoding = encoding; + public void setMethodsEncodingIndex(int encodingIndex) { + this.methodsEncodingIndex = encodingIndex; } @Platforms(Platform.HOSTED_ONLY.class) - public void setConstructorsEncoding(byte[] encoding) { - this.constructorsEncoding = encoding; + public void setConstructorsEncodingIndex(int encodingIndex) { + this.constructorsEncodingIndex = encodingIndex; } @Platforms(Platform.HOSTED_ONLY.class) - public void setClassesEncoding(byte[] encoding) { - this.classesEncoding = encoding; + public void setClassesEncodingIndex(int encodingIndex) { + this.classesEncodingIndex = encodingIndex; } @Platforms(Platform.HOSTED_ONLY.class) - public void setRecordComponentsEncoding(byte[] encoding) { - this.recordComponentsEncoding = encoding; + public void setRecordComponentsEncodingIndex(int encodingIndex) { + this.recordComponentsEncodingIndex = encodingIndex; } @Platforms(Platform.HOSTED_ONLY.class) - public void setPermittedSubclassesEncoding(byte[] encoding) { - this.permittedSubclassesEncoding = encoding; + public void setPermittedSubclassesEncodingIndex(int encodingIndex) { + this.permittedSubclassesEncodingIndex = encodingIndex; } @Platforms(Platform.HOSTED_ONLY.class) - public void setAnnotationsEncoding(byte[] encoding) { - this.annotations = encoding; + public void setAnnotationsEncodingIndex(int encodingIndex) { + this.annotationsIndex = encodingIndex; } @Platforms(Platform.HOSTED_ONLY.class) - public void setTypeAnnotationsEncoding(byte[] encoding) { - this.typeAnnotations = encoding; + public void setTypeAnnotationsEncodingIndex(int encodingIndex) { + this.typeAnnotationsIndex = encodingIndex; } @Platforms(Platform.HOSTED_ONLY.class) - public void setEnclosingMethodInfo(byte[] encoding) { - this.enclosingMethodInfo = encoding; + public void setEnclosingMethodInfoIndex(int encodingIndex) { + this.enclosingMethodInfoIndex = encodingIndex; } /** Executed at runtime. */ @@ -1001,12 +1002,12 @@ public T[] getAnnotationsByType(Class annotationClass) @Substitute @TargetElement(onlyWith = JDK17OrLater.class) private Target_java_lang_reflect_RecordComponent[] getRecordComponents0() { - if (recordComponentsEncoding == null) { + if (recordComponentsEncodingIndex == ReflectionMetadataDecoder.NULL_ARRAY) { /* See ReflectionDataBuilder.buildRecordComponents() for details. */ throw VMError.unsupportedFeature("Record components not available for record class " + getTypeName() + ". " + "All record component accessor methods of this record class must be included in the reflection configuration at image build time, then this method can be called."); } - return ImageSingletons.lookup(MethodMetadataDecoder.class).parseRecordComponents(this, recordComponentsEncoding); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseRecordComponents(this, recordComponentsEncodingIndex); } @KeepOriginal @@ -1329,10 +1330,10 @@ public Optional describeConstable() { @Substitute private Object[] getEnclosingMethod0() { - if (enclosingMethodInfo == null) { + if (enclosingMethodInfoIndex == ReflectionMetadataDecoder.NULL_ARRAY) { return null; } - Object[] enclosingMethod = ImageSingletons.lookup(MethodMetadataDecoder.class).parseEnclosingMethod(enclosingMethodInfo); + Object[] enclosingMethod = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseEnclosingMethod(enclosingMethodInfoIndex); if (enclosingMethod != null) { PredefinedClassesSupport.throwIfUnresolvable((Class) enclosingMethod[0], getClassLoader0()); } @@ -1365,12 +1366,12 @@ private String getGenericSignature0() { @Substitute byte[] getRawAnnotations() { - return annotations; + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseByteArray(annotationsIndex); } @Substitute byte[] getRawTypeAnnotations() { - return typeAnnotations; + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseByteArray(typeAnnotationsIndex); } @Substitute @@ -1380,22 +1381,22 @@ Target_jdk_internal_reflect_ConstantPool getConstantPool() { @Substitute private Field[] getDeclaredFields0(boolean publicOnly) { - return ImageSingletons.lookup(MethodMetadataDecoder.class).parseFields(this, fieldsEncoding, publicOnly, true); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, fieldsEncodingIndex, publicOnly, true); } @Substitute private Method[] getDeclaredMethods0(boolean publicOnly) { - return ImageSingletons.lookup(MethodMetadataDecoder.class).parseMethods(this, methodsEncoding, publicOnly, true); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, methodsEncodingIndex, publicOnly, true); } @Substitute private Constructor[] getDeclaredConstructors0(boolean publicOnly) { - return ImageSingletons.lookup(MethodMetadataDecoder.class).parseConstructors(this, constructorsEncoding, publicOnly, true); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, constructorsEncodingIndex, publicOnly, true); } @Substitute private Class[] getDeclaredClasses0() { - Class[] declaredClasses = ImageSingletons.lookup(MethodMetadataDecoder.class).parseClasses(classesEncoding); + Class[] declaredClasses = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseClasses(classesEncodingIndex); for (Class clazz : declaredClasses) { PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); } @@ -1447,10 +1448,10 @@ private native Target_java_lang_Class_ReflectionData newReflectionData(So @Substitute @TargetElement(onlyWith = JDK17OrLater.class) private Class[] getPermittedSubclasses0() { - if (permittedSubclassesEncoding.length == 0) { + if (permittedSubclassesEncodingIndex == ReflectionMetadataDecoder.NULL_ARRAY) { return null; } - Class[] permittedSubclasses = ImageSingletons.lookup(MethodMetadataDecoder.class).parseClasses(permittedSubclassesEncoding); + Class[] permittedSubclasses = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseClasses(permittedSubclassesEncodingIndex); for (Class clazz : permittedSubclasses) { PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); } @@ -1478,7 +1479,7 @@ private Class[] getPermittedSubclasses0() { private static Method[] filterHidingMethods(Method... methods) { List filtered = new ArrayList<>(); for (Method method : methods) { - if (!ImageSingletons.lookup(MethodMetadataDecoder.class).isHidingMethod(method.getModifiers())) { + if (!ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHidingMethod(method.getModifiers())) { filtered.add(method); } } @@ -1531,15 +1532,15 @@ private static void setNewInstanceCallerCache(DynamicHub that, Class value) { } public Field[] getReachableFields() { - return ImageSingletons.lookup(MethodMetadataDecoder.class).parseFields(this, fieldsEncoding, false, false); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, fieldsEncodingIndex, false, false); } public Method[] getReachableMethods() { - return ImageSingletons.lookup(MethodMetadataDecoder.class).parseMethods(this, methodsEncoding, false, false); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, methodsEncodingIndex, false, false); } public Constructor[] getReachableConstructors() { - return ImageSingletons.lookup(MethodMetadataDecoder.class).parseConstructors(this, constructorsEncoding, false, false); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, constructorsEncodingIndex, false, false); } } @@ -1640,7 +1641,7 @@ Method getMostSpecific() { } } /* Filter out hiding methods after the retursive lookup is done */ - return ImageSingletons.lookup(MethodMetadataDecoder.class).isHidingMethod(m.getModifiers()) ? null : m; + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHidingMethod(m.getModifiers()) ? null : m; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java similarity index 76% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java index 0d7442fe974a..a566335a87a8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java @@ -32,20 +32,26 @@ import com.oracle.svm.core.hub.DynamicHub; -public interface MethodMetadataDecoder { - Field[] parseFields(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly); +public interface ReflectionMetadataDecoder { + int NULL_ARRAY = -1; - Method[] parseMethods(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly); + Field[] parseFields(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly); - Constructor[] parseConstructors(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly); + Method[] parseMethods(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly); - Class[] parseClasses(byte[] encoding); + Constructor[] parseConstructors(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly); - Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, byte[] encoding); + Class[] parseClasses(int index); + + Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, int index); Parameter[] parseReflectParameters(Executable executable, byte[] encoding); - Object[] parseEnclosingMethod(byte[] encoding); + Object[] parseEnclosingMethod(int index); + + byte[] parseByteArray(int index); boolean isHidingMethod(int modifiers); + + long getMetadataByteLength(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index f4ec9fdf0ec3..2bc642aca595 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -45,7 +45,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import com.oracle.graal.pointsto.util.TimerCollection; import org.graalvm.compiler.debug.DebugOptions; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.serviceprovider.GraalServices; @@ -60,6 +59,7 @@ import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.reports.ReportUtils; import com.oracle.graal.pointsto.util.Timer; +import com.oracle.graal.pointsto.util.TimerCollection; import com.oracle.svm.core.BuildArtifacts; import com.oracle.svm.core.BuildArtifacts.ArtifactType; import com.oracle.svm.core.OS; @@ -69,6 +69,7 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.option.HostedOptionValues; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.c.codegen.CCompilerInvoker; import com.oracle.svm.hosted.code.CompileQueue.CompileTask; @@ -105,7 +106,6 @@ public class ProgressReporter { private GCStats lastGCStats = GCStats.getCurrent(); private long numRuntimeCompiledMethods = -1; private long graphEncodingByteLength = 0; - private long metadataByteLength = 0; private int numJNIClasses = -1; private int numJNIFields = -1; private int numJNIMethods = -1; @@ -207,10 +207,6 @@ public void setGraphEncodingByteLength(int value) { graphEncodingByteLength = value; } - public void setMetadataByteLength(int value) { - metadataByteLength = value; - } - public void setJNIInfo(int numClasses, int numFields, int numMethods) { numJNIClasses = numClasses; numJNIFields = numFields; @@ -507,8 +503,9 @@ private Map calculateHeapBreakdown(Collection heapObje classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("code metadata", "#glossary-code-metadata"), codeInfoSize); remainingBytes -= codeInfoSize; } + long metadataByteLength = ImageSingletons.lookup(ReflectionMetadataDecoder.class).getMetadataByteLength(); if (metadataByteLength > 0) { - classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("method metadata", "#glossary-method-metadata"), metadataByteLength); + classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("reflection metadata", "#glossary-method-metadata"), metadataByteLength); remainingBytes -= metadataByteLength; } if (graphEncodingByteLength > 0) { 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 626b8e6841e5..17bdf1ee7211 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 @@ -237,11 +237,11 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code codeInfoEncoder.addMethod(method, compilation, method.getCodeAddressOffset()); } - MethodMetadataEncoder methodMetadataEncoder = ImageSingletons.lookup(MethodMetadataEncoderFactory.class).create(encoders); + ReflectionMetadataEncoder reflectionMetadataEncoder = ImageSingletons.lookup(ReflectionMetadataEncoderFactory.class).create(encoders); for (HostedType type : imageHeap.getUniverse().getTypes()) { Map, Set>> innerClasses = ImageSingletons.lookup(RuntimeReflectionSupport.class).getReflectionInnerClasses(); if (type.getWrapped().isReachable()) { - methodMetadataEncoder.addClassMetadata(imageHeap.getMetaAccess(), type, innerClasses.getOrDefault(type.getJavaClass(), Collections.emptySet()).toArray(new Class[0])); + reflectionMetadataEncoder.addClassMetadata(imageHeap.getMetaAccess(), type, innerClasses.getOrDefault(type.getJavaClass(), Collections.emptySet()).toArray(new Class[0])); } } Set includedFields = new HashSet<>(); @@ -253,12 +253,12 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code if (object instanceof Method || object instanceof Constructor) { includedMethods.add(imageHeap.getMetaAccess().lookupJavaMethod((Executable) object)); } - methodMetadataEncoder.addHeapObjectMetadata(imageHeap.getMetaAccess(), object); + reflectionMetadataEncoder.addHeapObjectMetadata(imageHeap.getMetaAccess(), object); } for (Field reflectField : ImageSingletons.lookup(RuntimeReflectionSupport.class).getReflectionFields()) { HostedField field = imageHeap.getMetaAccess().lookupJavaField(reflectField); if (!includedFields.contains(field)) { - methodMetadataEncoder.addReflectionFieldMetadata(imageHeap.getMetaAccess(), field, reflectField); + reflectionMetadataEncoder.addReflectionFieldMetadata(imageHeap.getMetaAccess(), field, reflectField); includedFields.add(field); } } @@ -266,7 +266,7 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code HostedMethod method = imageHeap.getMetaAccess().lookupJavaMethod(reflectMethod); if (!includedMethods.contains(method)) { Object accessor = ImageSingletons.lookup(RuntimeReflectionSupport.class).getAccessor(reflectMethod); - methodMetadataEncoder.addReflectionExecutableMetadata(imageHeap.getMetaAccess(), method, reflectMethod, accessor); + reflectionMetadataEncoder.addReflectionExecutableMetadata(imageHeap.getMetaAccess(), method, reflectMethod, accessor); includedMethods.add(method); } } @@ -283,7 +283,7 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code } int modifiers = hidingMethod.getModifiers(); HostedType returnType = imageHeap.getUniverse().lookup(hidingMethod.getSignature().getReturnType(null)); - methodMetadataEncoder.addHidingMethodMetadata(declaringType, name, parameterTypes, modifiers, returnType); + reflectionMetadataEncoder.addHidingMethodMetadata(declaringType, name, parameterTypes, modifiers, returnType); if (hostedMethod != null) { includedMethods.add(hostedMethod); } @@ -292,12 +292,12 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code if (SubstrateOptions.IncludeMethodData.getValue()) { for (HostedField field : imageHeap.getUniverse().getFields()) { if (field.isAccessed() && !includedFields.contains(field)) { - methodMetadataEncoder.addReachableFieldMetadata(field); + reflectionMetadataEncoder.addReachableFieldMetadata(field); } } for (HostedMethod method : imageHeap.getUniverse().getMethods()) { if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod() && !includedMethods.contains(method)) { - methodMetadataEncoder.addReachableMethodMetadata(method); + reflectionMetadataEncoder.addReachableMethodMetadata(method); } } } @@ -309,7 +309,7 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code HostedImageCodeInfo imageCodeInfo = CodeInfoTable.getImageCodeCache().getHostedImageCodeInfo(); codeInfoEncoder.encodeAllAndInstall(imageCodeInfo, new InstantReferenceAdjuster()); - methodMetadataEncoder.encodeAllAndInstall(); + reflectionMetadataEncoder.encodeAllAndInstall(); imageCodeInfo.setCodeStart(firstMethod); imageCodeInfo.setCodeSize(codeSize); imageCodeInfo.setDataOffset(codeSize); @@ -601,7 +601,7 @@ protected boolean isDeoptEntry(ResolvedJavaMethod method, Infopoint infopoint) { } } - public interface MethodMetadataEncoder { + public interface ReflectionMetadataEncoder { void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Class[] reflectionClasses); void addReflectionFieldMetadata(MetaAccessProvider metaAccess, HostedField sharedField, Field reflectField); @@ -629,7 +629,7 @@ public interface MethodMetadataEncoder { byte[] getReflectParametersEncoding(Executable object); } - public interface MethodMetadataEncoderFactory { - MethodMetadataEncoder create(CodeInfoEncoder.Encoders encoders); + public interface ReflectionMetadataEncoderFactory { + ReflectionMetadataEncoder create(CodeInfoEncoder.Encoders encoders); } } 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 b8f414391958..3339ea1ef660 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,7 +24,7 @@ */ package com.oracle.svm.reflect.hosted; -import static com.oracle.svm.reflect.hosted.MethodMetadataEncoderImpl.getTypeAnnotations; +import static com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl.getTypeAnnotations; import java.lang.annotation.Annotation; import java.lang.reflect.AccessibleObject; @@ -246,7 +246,7 @@ private void processReachableTypes(DuringAnalysisAccessImpl access) { } /** - * See {@link MethodMetadataEncoderImpl} for details. + * See {@link ReflectionMetadataEncoderImpl} for details. */ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { for (AnalysisType type : access.getUniverse().getTypes()) { 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 7ddc2e33fd25..225e7d1cbd79 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 @@ -32,20 +32,22 @@ import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; -import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache.ReflectionMetadataEncoderFactory; import com.oracle.svm.hosted.meta.HostedType; -import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl; +import com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl; +import com.oracle.svm.reflect.target.ReflectionMetadataEncoding; import jdk.vm.ci.meta.JavaConstant; import sun.reflect.annotation.TypeAnnotation; @AutomaticFeature -class MethodMetadataFeature implements Feature { +class ReflectionMetadataFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(MethodMetadataEncoderFactory.class, new MethodMetadataEncoderImpl.Factory()); - ImageSingletons.add(MethodMetadataDecoder.class, new MethodMetadataDecoderImpl()); + ImageSingletons.add(ReflectionMetadataEncoderFactory.class, new ReflectionMetadataEncoderImpl.Factory()); + ImageSingletons.add(ReflectionMetadataDecoder.class, new ReflectionMetadataDecoderImpl()); + ImageSingletons.add(ReflectionMetadataEncoding.class, new ReflectionMetadataEncoding()); } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java similarity index 90% rename from substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java rename to substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java index 7fafb0e45ed3..a3066bf623f8 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java @@ -24,10 +24,10 @@ */ package com.oracle.svm.reflect.hosted; -import static com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.COMPLETE_FLAG_MASK; -import static com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.HIDING_FLAG_MASK; -import static com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.IN_HEAP_FLAG_MASK; -import static com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.NULL_OBJECT; +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; +import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.NULL_OBJECT; import java.lang.annotation.Annotation; import java.lang.reflect.AccessibleObject; @@ -64,12 +64,12 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.ByteArrayReader; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.ProgressReporter; -import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoder; -import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; +import com.oracle.svm.hosted.image.NativeImageCodeCache.ReflectionMetadataEncoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache.ReflectionMetadataEncoderFactory; import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; @@ -83,7 +83,8 @@ import com.oracle.svm.reflect.hosted.ReflectionMetadata.MethodMetadata; import com.oracle.svm.reflect.hosted.ReflectionMetadata.RecordComponentMetadata; import com.oracle.svm.reflect.hosted.ReflectionMetadata.ReflectParameterMetadata; -import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl; +import com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl; +import com.oracle.svm.reflect.target.ReflectionMetadataEncoding; import com.oracle.svm.reflect.target.Target_sun_reflect_annotation_AnnotationParser; import com.oracle.svm.util.ReflectionUtil; @@ -115,12 +116,12 @@ * phase, the values are encoded as byte arrays and stored in {@link DynamicHub} arrays (see * {@link #encodeAllAndInstall()}). */ -public class MethodMetadataEncoderImpl implements MethodMetadataEncoder { +public class ReflectionMetadataEncoderImpl implements ReflectionMetadataEncoder { - static class Factory implements MethodMetadataEncoderFactory { + static class Factory implements ReflectionMetadataEncoderFactory { @Override - public MethodMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { - return new MethodMetadataEncoderImpl(encoders); + public ReflectionMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { + return new ReflectionMetadataEncoderImpl(encoders); } } @@ -140,7 +141,7 @@ public MethodMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { private final Map typeAnnotationsEncodings = new HashMap<>(); private final Map reflectParametersEncodings = new HashMap<>(); - public MethodMetadataEncoderImpl(CodeInfoEncoder.Encoders encoders) { + public ReflectionMetadataEncoderImpl(CodeInfoEncoder.Encoders encoders) { this.encoders = encoders; } @@ -202,6 +203,7 @@ public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Cla TypeAnnotation[] typeAnnotations = getTypeAnnotations(javaClass); /* Register string and class values in annotations */ + encoders.sourceClasses.addObject(javaClass); if (enclosingMethodInfo != null) { encoders.sourceClasses.addObject(enclosingMethodDeclaringClass.getJavaClass()); encoders.sourceMethodNames.addObject(enclosingMethodName); @@ -672,73 +674,90 @@ private static Method getRecordComponentAccessor(Object recordComponent) { } /** - * See {@link MethodMetadataDecoderImpl} for the encoding format description. + * See {@link ReflectionMetadataDecoderImpl} for the encoding format description. */ @Override public void encodeAllAndInstall() { - int metadataByteLength = 0; + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + encodeAndAddCollection(buf, sortedTypes.toArray(new HostedType[0]), this::encodeType, (ignored) -> { + }, false); for (HostedType declaringType : sortedTypes) { DynamicHub hub = declaringType.getHub(); ClassMetadata classMetadata = classData.get(declaringType); - metadataByteLength += encodeAndInstallCollection(classMetadata.classes, this::encodeType, hub::setClassesEncoding, false); - if (classMetadata.enclosingMethodDeclaringClass != null || classMetadata.enclosingMethodName != null || classMetadata.enclosingMethodDescriptor != null) { - metadataByteLength += encodeAndInstall(new Object[]{classMetadata.enclosingMethodDeclaringClass, classMetadata.enclosingMethodName, classMetadata.enclosingMethodDescriptor}, - this::encodeEnclosingMethod, hub::setEnclosingMethodInfo); - } + encodeAndAddCollection(buf, classMetadata.classes, this::encodeType, hub::setClassesEncodingIndex, false); + encodeAndAddElement(buf, new Object[]{classMetadata.enclosingMethodDeclaringClass, classMetadata.enclosingMethodName, classMetadata.enclosingMethodDescriptor}, + this::encodeEnclosingMethod, hub::setEnclosingMethodInfoIndex); if (JavaVersionUtil.JAVA_SPEC >= 17) { - metadataByteLength += encodeAndInstallCollection(classMetadata.recordComponents, this::encodeRecordComponent, hub::setRecordComponentsEncoding, true); - metadataByteLength += encodeAndInstallCollection(classMetadata.permittedSubclasses, this::encodeType, hub::setPermittedSubclassesEncoding, true); + encodeAndAddCollection(buf, classMetadata.recordComponents, this::encodeRecordComponent, hub::setRecordComponentsEncodingIndex, true); + encodeAndAddCollection(buf, classMetadata.permittedSubclasses, this::encodeType, hub::setPermittedSubclassesEncodingIndex, true); } - metadataByteLength += encodeAndInstall(classMetadata.annotations, this::encodeAnnotations, hub::setAnnotationsEncoding); - metadataByteLength += encodeAndInstall(classMetadata.typeAnnotations, this::encodeTypeAnnotations, - hub::setTypeAnnotationsEncoding); - metadataByteLength += encodeAndInstallCollection(fieldData.getOrDefault(declaringType, Collections.emptySet()).toArray(new FieldMetadata[0]), this::encodeField, hub::setFieldsEncoding, + encodeAndAddEncodedElement(buf, classMetadata.annotations, this::encodeAnnotations, hub::setAnnotationsEncodingIndex); + encodeAndAddEncodedElement(buf, classMetadata.typeAnnotations, this::encodeTypeAnnotations, + hub::setTypeAnnotationsEncodingIndex); + encodeAndAddCollection(buf, fieldData.getOrDefault(declaringType, Collections.emptySet()).toArray(new FieldMetadata[0]), this::encodeField, hub::setFieldsEncodingIndex, false); - metadataByteLength += encodeAndInstallCollection(methodData.getOrDefault(declaringType, Collections.emptySet()).toArray(new MethodMetadata[0]), this::encodeExecutable, - hub::setMethodsEncoding, false); - metadataByteLength += encodeAndInstallCollection(constructorData.getOrDefault(declaringType, Collections.emptySet()).toArray(new ConstructorMetadata[0]), this::encodeExecutable, - hub::setConstructorsEncoding, false); + encodeAndAddCollection(buf, methodData.getOrDefault(declaringType, Collections.emptySet()).toArray(new MethodMetadata[0]), this::encodeExecutable, + hub::setMethodsEncodingIndex, false); + encodeAndAddCollection(buf, constructorData.getOrDefault(declaringType, Collections.emptySet()).toArray(new ConstructorMetadata[0]), this::encodeExecutable, + hub::setConstructorsEncodingIndex, false); } for (AccessibleObjectMetadata metadata : heapData) { AccessibleObject heapObject = (AccessibleObject) SubstrateObjectConstant.asObject(metadata.heapObject); - metadataByteLength += encodeAndInstall(metadata.annotations, this::encodeAnnotations, (array) -> annotationsEncodings.put(heapObject, array)); - metadataByteLength += encodeAndInstall(metadata.typeAnnotations, this::encodeTypeAnnotations, (array) -> typeAnnotationsEncodings.put(heapObject, array)); + encodeAndAddHeapElement(metadata.annotations, this::encodeAnnotations, (array) -> annotationsEncodings.put(heapObject, array)); + encodeAndAddHeapElement(metadata.typeAnnotations, this::encodeTypeAnnotations, (array) -> typeAnnotationsEncodings.put(heapObject, array)); if (metadata instanceof ExecutableMetadata) { - metadataByteLength += encodeAndInstall(((ExecutableMetadata) metadata).parameterAnnotations, this::encodeParameterAnnotations, + encodeAndAddHeapElement(((ExecutableMetadata) metadata).parameterAnnotations, this::encodeParameterAnnotations, (array) -> parameterAnnotationsEncodings.put((Executable) heapObject, array)); if (((ExecutableMetadata) metadata).reflectParameters != null) { - metadataByteLength += encodeAndInstall(((ExecutableMetadata) metadata).reflectParameters, this::encodeReflectParameters, + encodeAndAddHeapElement(((ExecutableMetadata) metadata).reflectParameters, this::encodeReflectParameters, (array) -> reflectParametersEncodings.put((Executable) heapObject, array)); } if (metadata instanceof MethodMetadata && ((Method) SubstrateObjectConstant.asObject(metadata.heapObject)).getDeclaringClass().isAnnotation() && ((MethodMetadata) metadata).annotationDefault != null) { - metadataByteLength += encodeAndInstall(((MethodMetadata) metadata).annotationDefault, this::encodeMemberValue, + encodeAndAddHeapElement(((MethodMetadata) metadata).annotationDefault, this::encodeMemberValue, (array) -> annotationDefaultEncodings.put((Method) heapObject, array)); } } } + install(buf); /* Enable field recomputers in reflection objects to see the computed values */ - ImageSingletons.add(MethodMetadataEncoder.class, this); - ProgressReporter.singleton().setMetadataByteLength(metadataByteLength); + ImageSingletons.add(ReflectionMetadataEncoder.class, this); } - private static int encodeAndInstallCollection(T[] data, BiConsumer encodeCallback, Consumer saveCallback, boolean canBeNull) { - UnsafeArrayTypeWriter encodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - encodeArray(encodingBuffer, data, element -> encodeCallback.accept(encodingBuffer, element), canBeNull); - return install(encodingBuffer, saveCallback); + private static void encodeAndAddCollection(UnsafeArrayTypeWriter buf, T[] data, BiConsumer encodeCallback, Consumer saveCallback, boolean canBeNull) { + int offset = ReflectionMetadataDecoder.NULL_ARRAY; + if (!canBeNull || data != null) { + offset = TypeConversion.asS4(buf.getBytesWritten()); + encodeArray(buf, data, element -> encodeCallback.accept(buf, element)); + } + saveCallback.accept(offset); } - private static int encodeAndInstall(T data, Function encodeCallback, Consumer saveCallback) { - UnsafeArrayTypeWriter encodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - encodeBytes(encodingBuffer, encodeCallback.apply(data)); - return install(encodingBuffer, saveCallback); + private static void encodeAndAddElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback, Consumer saveCallback) { + byte[] encoding = encodeCallback.apply(data); + int offset = ReflectionMetadataDecoder.NULL_ARRAY; + if (encoding != null) { + offset = TypeConversion.asS4(buf.getBytesWritten()); + encodeBytes(buf, encoding); + } + saveCallback.accept(offset); } - private static int install(UnsafeArrayTypeWriter encodingBuffer, Consumer saveCallback) { + private static void encodeAndAddEncodedElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback, Consumer saveCallback) { + int offset = TypeConversion.asS4(buf.getBytesWritten()); + encodeByteArray(buf, encodeCallback.apply(data)); + saveCallback.accept(offset); + } + + private static void encodeAndAddHeapElement(T data, Function encodeCallback, Consumer saveCallback) { + byte[] encoding = encodeCallback.apply(data); + saveCallback.accept(encoding); + } + + private static void install(UnsafeArrayTypeWriter encodingBuffer) { int encodingSize = TypeConversion.asS4(encodingBuffer.getBytesWritten()); byte[] dataEncoding = new byte[encodingSize]; - saveCallback.accept(encodingBuffer.toArray(dataEncoding)); - return encodingSize; + ImageSingletons.lookup(ReflectionMetadataEncoding.class).setEncoding(encodingBuffer.toArray(dataEncoding)); } private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) { @@ -817,13 +836,6 @@ private void encodeObject(UnsafeArrayTypeWriter buf, JavaConstant object) { } private static void encodeArray(UnsafeArrayTypeWriter buf, T[] array, Consumer elementEncoder) { - encodeArray(buf, array, elementEncoder, false); - } - - private static void encodeArray(UnsafeArrayTypeWriter buf, T[] array, Consumer elementEncoder, boolean canBeNull) { - if (canBeNull && array == null) { - return; - } buf.putUV(array.length); for (T elem : array) { elementEncoder.accept(elem); @@ -1187,6 +1199,10 @@ private void encodeRecordComponent(UnsafeArrayTypeWriter buf, RecordComponentMet } private byte[] encodeEnclosingMethod(Object[] enclosingMethod) { + assert enclosingMethod.length == 3; + if (enclosingMethod[0] == null && enclosingMethod[1] == null && enclosingMethod[2] == null) { + return null; + } UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); encodeType(buf, (HostedType) enclosingMethod[0]); encodeName(buf, (String) enclosingMethod[1]); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java similarity index 91% rename from substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java rename to substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java index b2f796530fb3..d3613b092898 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.c.NonmovableArrays; @@ -43,7 +44,7 @@ import com.oracle.svm.core.code.CodeInfoAccess; import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent; import com.oracle.svm.core.util.ByteArrayReader; @@ -106,7 +107,7 @@ * } SimpleMethodEncoding; * */ -public class MethodMetadataDecoderImpl implements MethodMetadataDecoder { +public class ReflectionMetadataDecoderImpl implements ReflectionMetadataDecoder { public static final int NO_METHOD_METADATA = -1; public static final int NULL_OBJECT = -1; public static final int COMPLETE_FLAG_INDEX = 31; @@ -116,37 +117,41 @@ public class MethodMetadataDecoderImpl implements MethodMetadataDecoder { public static final int HIDING_FLAG_INDEX = 29; public static final int HIDING_FLAG_MASK = 1 << HIDING_FLAG_INDEX; + static byte[] getEncoding() { + return ImageSingletons.lookup(ReflectionMetadataEncoding.class).getEncoding(); + } + @Override - public Field[] parseFields(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly) { - UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + public Field[] parseFields(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); return decodeArray(reader, Field.class, (i) -> decodeField(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); } @Override - public Method[] parseMethods(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly) { - UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + public Method[] parseMethods(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); return decodeArray(reader, Method.class, (i) -> decodeMethod(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); } @Override - public Constructor[] parseConstructors(DynamicHub declaringType, byte[] encoding, boolean publicOnly, boolean reflectOnly) { - UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + public Constructor[] parseConstructors(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); return decodeArray(reader, Constructor.class, (i) -> decodeConstructor(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); } @Override - public Class[] parseClasses(byte[] encoding) { - UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + public Class[] parseClasses(int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); return decodeArray(reader, Class.class, (i) -> decodeType(reader, codeInfo)); } @Override - public Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, byte[] encoding) { - UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + public Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); return decodeArray(reader, Target_java_lang_reflect_RecordComponent.class, (i) -> decodeRecordComponent(reader, codeInfo, DynamicHub.toClass(declaringType))); } @@ -159,8 +164,8 @@ public Parameter[] parseReflectParameters(Executable executable, byte[] encoding } @Override - public Object[] parseEnclosingMethod(byte[] encoding) { - UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + public Object[] parseEnclosingMethod(int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); Class declaringClass = decodeType(reader, codeInfo); String name = decodeName(reader, codeInfo); @@ -168,11 +173,22 @@ public Object[] parseEnclosingMethod(byte[] encoding) { return new Object[]{declaringClass, name, descriptor}; } + @Override + public byte[] parseByteArray(int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + return decodeByteArray(reader); + } + @Override public boolean isHidingMethod(int modifiers) { return (modifiers & HIDING_FLAG_MASK) != 0; } + @Override + public long getMetadataByteLength() { + return ImageSingletons.lookup(ReflectionMetadataEncoding.class).getEncoding().length; + } + 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; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataEncoding.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataEncoding.java new file mode 100644 index 000000000000..fc33ab2c2b53 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataEncoding.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, 2022, 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.reflect.target; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.annotate.UnknownObjectField; + +public class ReflectionMetadataEncoding { + @UnknownObjectField(types = {byte[].class}) private byte[] encoding; + + public byte[] getEncoding() { + return encoding; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setEncoding(byte[] encoding) { + this.encoding = encoding; + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java index a9da42b99f9b..df50222f2c57 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java @@ -69,7 +69,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getTypeAnnotationsEncoding((AccessibleObject) receiver); + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getTypeAnnotationsEncoding((AccessibleObject) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java index f1778a3b3937..2fa6e30e8bc9 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java @@ -85,7 +85,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); } } @@ -98,7 +98,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getParameterAnnotationsEncoding((Executable) receiver); + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getParameterAnnotationsEncoding((Executable) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java index 0bdfee943d97..72813c6aef51 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java @@ -39,7 +39,7 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.UnknownObjectField; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.hosted.image.NativeImageCodeCache; import jdk.vm.ci.meta.MetaAccessProvider; @@ -62,7 +62,7 @@ private Parameter[] getParameters0() { if (rawParameters == null) { return null; } - return ImageSingletons.lookup(MethodMetadataDecoder.class).parseReflectParameters(SubstrateUtil.cast(this, Executable.class), rawParameters); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseReflectParameters(SubstrateUtil.cast(this, Executable.class), rawParameters); } @Substitute @@ -79,7 +79,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getReflectParametersEncoding((Executable) receiver); + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getReflectParametersEncoding((Executable) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java index 045580fa6599..e1b3e7dbe8d6 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java @@ -143,7 +143,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java index 7707edfcbe19..08f89e20e68b 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java @@ -88,7 +88,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); } } @@ -101,7 +101,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getParameterAnnotationsEncoding((Executable) receiver); + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getParameterAnnotationsEncoding((Executable) receiver); } } @@ -114,7 +114,7 @@ public RecomputeFieldValue.ValueAvailability valueAvailability() { @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return ImageSingletons.lookup(NativeImageCodeCache.MethodMetadataEncoder.class).getAnnotationDefaultEncoding((Method) receiver); + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getAnnotationDefaultEncoding((Method) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java index e900235c1769..b7253aef0c8c 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java @@ -40,7 +40,7 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.reflect.hosted.MethodMetadataEncoderImpl; +import com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl; import sun.reflect.annotation.AnnotationParser; import sun.reflect.annotation.AnnotationType; @@ -49,8 +49,8 @@ /** * Substitutions in this class are required to adapt the JDK encoding for annotations to our - * modified version of it. See {@link MethodMetadataEncoderImpl#encodeAnnotations(Annotation[])} for - * a description of the changes and the rationale behind them. + * modified version of it. See {@link ReflectionMetadataEncoderImpl#encodeAnnotations(Annotation[])} + * for a description of the changes and the rationale behind them. */ @TargetClass(AnnotationParser.class) public final class Target_sun_reflect_annotation_AnnotationParser { From 830e38c47da61ea7576b0a2c0254154cc22ff6d4 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Fri, 4 Mar 2022 19:18:14 +0100 Subject: [PATCH 3/3] Bugfixes --- .../native-image/BuildOutput.md | 10 +- .../org/graalvm/nativeimage/c/CContext.java | 5 +- .../impl/RuntimeReflectionSupport.java | 4 + .../oracle/graal/pointsto/ObjectScanner.java | 3 + .../com/oracle/svm/core/SubstrateOptions.java | 5 + .../com/oracle/svm/core/hub/DynamicHub.java | 186 ++++++---- .../core/hub/SunReflectTypeSubstitutions.java | 255 ------------- ...get_java_lang_module_ModuleDescriptor.java | 47 +++ .../svm/core/meta/ReadableJavaField.java | 7 +- .../reflect/ReflectionMetadataDecoder.java | 2 +- .../oracle/svm/hosted/ProgressReporter.java | 2 +- .../svm/hosted/heap/SVMImageHeapScanner.java | 10 +- .../hosted/image/NativeImageCodeCache.java | 72 ++-- .../hosted/substitute/ComputedValueField.java | 7 + .../reflect/hosted/ReflectionDataBuilder.java | 62 ++-- .../svm/reflect/hosted/ReflectionFeature.java | 2 - .../reflect/hosted/ReflectionMetadata.java | 12 +- .../hosted/ReflectionMetadataEncoderImpl.java | 338 ++++++++++++------ .../hosted/ReflectionObjectReplacer.java | 88 ----- .../reflect/proxy/DynamicProxySupport.java | 12 +- .../proxy/hosted/DynamicProxyFeature.java | 2 +- .../target/ReflectionMetadataDecoderImpl.java | 267 ++++++++++---- ...nnotation_AnnotationInvocationHandler.java | 223 ++++++++++++ ...flect_annotation_TypeAnnotationParser.java | 96 ----- .../svm/truffle/TruffleBaseFeature.java | 3 + 25 files changed, 967 insertions(+), 753 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_module_ModuleDescriptor.java delete mode 100644 substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java create mode 100644 substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationInvocationHandler.java delete mode 100644 substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java diff --git a/docs/reference-manual/native-image/BuildOutput.md b/docs/reference-manual/native-image/BuildOutput.md index 5b70d24b2c0a..f920f787ac74 100644 --- a/docs/reference-manual/native-image/BuildOutput.md +++ b/docs/reference-manual/native-image/BuildOutput.md @@ -99,7 +99,7 @@ To reduce overhead, please ensure that the classpath only contains entries that #### Reflection Registrations The number of classes, fields, and methods that are registered for reflection. -Large numbers can cause significant reflection overheads, slow down the build process, and increase the size of the native image (see [method metadata](#glossary-method-metadata)). +Large numbers can cause significant reflection overheads, slow down the build process, and increase the size of the native image (see [reflection metadata](#glossary-reflection-metadata)). #### JNI Access Registrations The number of classes, fields, and methods that are registered for [JNI][doc_jni] access. @@ -136,16 +136,16 @@ Therefore, reducing the number of [reachable methods](#glossary-reachability) al The image heap contains reachable objects such as static application data, metadata, and `byte[]` for different purposes. ##### General Heap Data Stored in `byte[]` -The total size of all `byte[]` objects that are neither used for `java.lang.String`, nor [code metadata](#glossary-code-metadata), nor [method metadata](#glossary-method-metadata), nor [graph encodings](#glossary-graph-encodings). +The total size of all `byte[]` objects that are neither used for `java.lang.String`, nor [code metadata](#glossary-code-metadata), nor [reflection metadata](#glossary-reflection-metadata), nor [graph encodings](#glossary-graph-encodings). Therefore, this can also include `byte[]` objects from application code. ##### Code Metadata Stored in `byte[]` The total size of all `byte[]` objects used for metadata for the [code area](#glossary-code-area). Therefore, reducing the number of [reachable methods](#glossary-reachability) also reduces the size of this metadata. -##### Method Metadata Stored in `byte[]` -The total size of all `byte[]` objects used for method metadata, a type of reflection metadata. -To reduce the amount of method metadata, reduce the number of [classes registered for reflection](#glossary-reflection-classes). +##### Reflection Metadata Stored in `byte[]` +The total size of all `byte[]` objects used for reflection metadata, including class, field, method and constructor data. +To reduce the amount of reflection metadata, reduce the number of [elements registered for reflection](#glossary-reflection-registrations). ##### Graph Encodings Stored in `byte[]` The total size of all `byte[]` objects used for graph encodings. diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/CContext.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/CContext.java index 7c9ae99abcf6..8b4e5690e15c 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/CContext.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/CContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, 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 @@ -47,6 +47,8 @@ import java.util.Collections; import java.util.List; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CLibrary; /** @@ -58,6 +60,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) +@Platforms(Platform.HOSTED_ONLY.class) public @interface CContext { /** 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 8d8bd0481988..c4d0f95765ea 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 @@ -63,6 +63,10 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry { Object[] getRecordComponents(Class type); + void registerHeapDynamicHub(Object hub); + + Set getHeapDynamicHubs(); + void registerHeapReflectionObject(AccessibleObject object); Set getHeapReflectionObjects(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index d3de39c1e64a..486a09e05fa6 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -159,6 +159,9 @@ protected final void scanField(AnalysisField field, JavaConstant receiver, ScanR throw AnalysisError.shouldNotReachHere("Could not find field " + field.format("%H.%n") + (receiver == null ? "" : " on " + constantType(bb, receiver).toJavaName()) + System.lineSeparator() + backtrace); + } else if (fieldValue.getJavaKind() == JavaKind.Illegal) { + /* The value is not available yet */ + return; } if (fieldValue.getJavaKind() == JavaKind.Object && bb.getHostVM().isRelocatedPointer(constantAsObject(bb, fieldValue))) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 061f85611a99..73f32838ba0b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -720,6 +720,11 @@ protected void onValueUpdate(EconomicMap, Object> values, String ol } }; + @SuppressWarnings("unused")// + @APIOption(name = "configure-reflection-metadata")// + @Option(help = "Enable runtime instantiation of reflection objects for non-invoked methods.", type = OptionType.Expert, deprecated = true)// + public static final HostedOptionKey ConfigureReflectionMetadata = new HostedOptionKey<>(true); + @Option(help = "Include a list of methods included in the image for runtime inspection.", type = OptionType.Expert)// public static final HostedOptionKey IncludeMethodData = new HostedOptionKey<>(true); 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 566880010c5b..cfd051289163 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 @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.hub; +import static com.oracle.svm.core.reflect.ReflectionMetadataDecoder.NO_DATA; + import java.io.InputStream; import java.io.Serializable; import java.lang.annotation.Annotation; @@ -33,6 +35,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.InvocationTargetException; @@ -71,7 +74,6 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.annotate.UnknownObjectField; -import com.oracle.svm.core.annotate.UnknownPrimitiveField; import com.oracle.svm.core.classinitialization.ClassInitializationInfo; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; import com.oracle.svm.core.jdk.JDK11OrEarlier; @@ -324,24 +326,9 @@ public void setModule(Module module) { @TargetElement(onlyWith = JDK11OrEarlier.class) // private Class newInstanceCallerCache; - @UnknownPrimitiveField private int enclosingMethodInfoIndex; - - @UnknownPrimitiveField private int annotationsIndex; - - @UnknownPrimitiveField private int typeAnnotationsIndex; - - @UnknownPrimitiveField int fieldsEncodingIndex; - - @UnknownPrimitiveField int methodsEncodingIndex; + @UnknownObjectField(types = DynamicHubMetadata.class, canBeNull = true) private DynamicHubMetadata hubMetadata; - @UnknownPrimitiveField int constructorsEncodingIndex; - - @UnknownPrimitiveField int classesEncodingIndex; - - @TargetElement(onlyWith = JDK17OrLater.class)// - @UnknownPrimitiveField int recordComponentsEncodingIndex; - - @UnknownPrimitiveField int permittedSubclassesEncodingIndex; + @UnknownObjectField(types = ReflectionMetadata.class, canBeNull = true) private ReflectionMetadata reflectionMetadata; @Platforms(Platform.HOSTED_ONLY.class) public DynamicHub(Class hostedJavaClass, String name, HubType hubType, ReferenceType referenceType, Object isLocalClass, Object isAnonymousClass, DynamicHub superType, DynamicHub componentHub, @@ -476,48 +463,13 @@ public String getSignature() { } @Platforms(Platform.HOSTED_ONLY.class) - public void setFieldsEncodingIndex(int encodingIndex) { - this.fieldsEncodingIndex = encodingIndex; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setMethodsEncodingIndex(int encodingIndex) { - this.methodsEncodingIndex = encodingIndex; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setConstructorsEncodingIndex(int encodingIndex) { - this.constructorsEncodingIndex = encodingIndex; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setClassesEncodingIndex(int encodingIndex) { - this.classesEncodingIndex = encodingIndex; + public void setHubMetadata(int enclosingMethodInfoIndex, int annotationsIndex, int typeAnnotationsIndex, int classesEncodingIndex, int permittedSubclassesEncodingIndex) { + this.hubMetadata = new DynamicHubMetadata(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesEncodingIndex); } @Platforms(Platform.HOSTED_ONLY.class) - public void setRecordComponentsEncodingIndex(int encodingIndex) { - this.recordComponentsEncodingIndex = encodingIndex; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setPermittedSubclassesEncodingIndex(int encodingIndex) { - this.permittedSubclassesEncodingIndex = encodingIndex; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setAnnotationsEncodingIndex(int encodingIndex) { - this.annotationsIndex = encodingIndex; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setTypeAnnotationsEncodingIndex(int encodingIndex) { - this.typeAnnotationsIndex = encodingIndex; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setEnclosingMethodInfoIndex(int encodingIndex) { - this.enclosingMethodInfoIndex = encodingIndex; + public void setReflectionMetadata(int fieldsEncodingIndex, int methodsEncodingIndex, int constructorsEncodingIndex, int recordComponentsEncodingIndex) { + this.reflectionMetadata = new ReflectionMetadata(fieldsEncodingIndex, methodsEncodingIndex, constructorsEncodingIndex, recordComponentsEncodingIndex); } /** Executed at runtime. */ @@ -1002,12 +954,12 @@ public T[] getAnnotationsByType(Class annotationClass) @Substitute @TargetElement(onlyWith = JDK17OrLater.class) private Target_java_lang_reflect_RecordComponent[] getRecordComponents0() { - if (recordComponentsEncodingIndex == ReflectionMetadataDecoder.NULL_ARRAY) { + if (reflectionMetadata == null || reflectionMetadata.recordComponentsEncodingIndex == NO_DATA) { /* See ReflectionDataBuilder.buildRecordComponents() for details. */ throw VMError.unsupportedFeature("Record components not available for record class " + getTypeName() + ". " + "All record component accessor methods of this record class must be included in the reflection configuration at image build time, then this method can be called."); } - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseRecordComponents(this, recordComponentsEncodingIndex); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseRecordComponents(this, reflectionMetadata.recordComponentsEncodingIndex); } @KeepOriginal @@ -1034,6 +986,9 @@ private static ReflectionFactory getReflectionFactory() { @KeepOriginal private static native Field searchFields(Field[] fields, String name); + /** + * @see #filterHidingMethods(Method...) + */ @Substitute private static Method searchMethods(Method[] allMethods, String name, Class[] parameterTypes) { Method[] methods = filterHidingMethods(allMethods); @@ -1057,6 +1012,9 @@ private static Method searchMethods(Method[] allMethods, String name, Class[] @KeepOriginal private static native Field[] copyFields(Field[] arg); + /** + * @see #filterHidingMethods(Method...) + */ @Substitute private static Method[] copyMethods(Method[] original) { Method[] arg = filterHidingMethods(original); @@ -1235,6 +1193,9 @@ private String getSimpleBinaryName0() { /* See open/src/hotspot/share/prims/jvm.cpp#1522. */ } + /** + * @see #filterHidingMethods(Method...) + */ @Substitute // @SuppressWarnings({"unused"}) List getDeclaredPublicMethods(String methodName, Class... parameterTypes) { @@ -1330,10 +1291,10 @@ public Optional describeConstable() { @Substitute private Object[] getEnclosingMethod0() { - if (enclosingMethodInfoIndex == ReflectionMetadataDecoder.NULL_ARRAY) { + if (hubMetadata == null || hubMetadata.enclosingMethodInfoIndex == NO_DATA) { return null; } - Object[] enclosingMethod = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseEnclosingMethod(enclosingMethodInfoIndex); + Object[] enclosingMethod = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseEnclosingMethod(hubMetadata.enclosingMethodInfoIndex); if (enclosingMethod != null) { PredefinedClassesSupport.throwIfUnresolvable((Class) enclosingMethod[0], getClassLoader0()); } @@ -1366,12 +1327,18 @@ private String getGenericSignature0() { @Substitute byte[] getRawAnnotations() { - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseByteArray(annotationsIndex); + if (hubMetadata == null || hubMetadata.annotationsIndex == NO_DATA) { + return null; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseByteArray(hubMetadata.annotationsIndex); } @Substitute byte[] getRawTypeAnnotations() { - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseByteArray(typeAnnotationsIndex); + if (hubMetadata == null || hubMetadata.typeAnnotationsIndex == NO_DATA) { + return null; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseByteArray(hubMetadata.typeAnnotationsIndex); } @Substitute @@ -1381,22 +1348,34 @@ Target_jdk_internal_reflect_ConstantPool getConstantPool() { @Substitute private Field[] getDeclaredFields0(boolean publicOnly) { - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, fieldsEncodingIndex, publicOnly, true); + if (reflectionMetadata == null || reflectionMetadata.fieldsEncodingIndex == NO_DATA) { + return new Field[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, reflectionMetadata.fieldsEncodingIndex, publicOnly, true); } @Substitute private Method[] getDeclaredMethods0(boolean publicOnly) { - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, methodsEncodingIndex, publicOnly, true); + if (reflectionMetadata == null || reflectionMetadata.methodsEncodingIndex == NO_DATA) { + return new Method[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, reflectionMetadata.methodsEncodingIndex, publicOnly, true); } @Substitute private Constructor[] getDeclaredConstructors0(boolean publicOnly) { - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, constructorsEncodingIndex, publicOnly, true); + if (reflectionMetadata == null || reflectionMetadata.constructorsEncodingIndex == NO_DATA) { + return new Constructor[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, reflectionMetadata.constructorsEncodingIndex, publicOnly, true); } @Substitute private Class[] getDeclaredClasses0() { - Class[] declaredClasses = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseClasses(classesEncodingIndex); + if (hubMetadata == null || hubMetadata.classesEncodingIndex == NO_DATA) { + return new Class[0]; + } + Class[] declaredClasses = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseClasses(hubMetadata.classesEncodingIndex); for (Class clazz : declaredClasses) { PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); } @@ -1448,10 +1427,10 @@ private native Target_java_lang_Class_ReflectionData newReflectionData(So @Substitute @TargetElement(onlyWith = JDK17OrLater.class) private Class[] getPermittedSubclasses0() { - if (permittedSubclassesEncodingIndex == ReflectionMetadataDecoder.NULL_ARRAY) { + if (hubMetadata == null || hubMetadata.permittedSubclassesEncodingIndex == NO_DATA) { return null; } - Class[] permittedSubclasses = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseClasses(permittedSubclassesEncodingIndex); + Class[] permittedSubclasses = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseClasses(hubMetadata.permittedSubclassesEncodingIndex); for (Class clazz : permittedSubclasses) { PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); } @@ -1476,6 +1455,14 @@ private Class[] getPermittedSubclasses0() { @KeepOriginal native AnnotationType getAnnotationType(); + @KeepOriginal + static native byte[] getExecutableTypeAnnotationBytes(Executable ex); + + /* + * 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. + */ private static Method[] filterHidingMethods(Method... methods) { List filtered = new ArrayList<>(); for (Method method : methods) { @@ -1510,6 +1497,13 @@ private static AnnotationType getAnnotationType(DynamicHub that) { private static class CachedConstructorAccessors { @SuppressWarnings("unused") private static Constructor getCachedConstructor(DynamicHub that) { + /* + * The JavaDoc for the Class.newInstance method states that "The class is initialized if + * it has not already been initialized". However, it doesn't specify if the absence of a + * nullary constructor will result in an InstantiationException before the class is + * initialized. We eagerly initialize the class to conform with JCK tests. + */ + that.ensureInitialized(); return that.companion.getCachedConstructor(); } @@ -1531,16 +1525,64 @@ private static void setNewInstanceCallerCache(DynamicHub that, Class value) { } } + private static final class DynamicHubMetadata { + final int enclosingMethodInfoIndex; + + final int annotationsIndex; + + final int typeAnnotationsIndex; + + final int classesEncodingIndex; + + @TargetElement(onlyWith = JDK17OrLater.class)// + final int permittedSubclassesEncodingIndex; + + private DynamicHubMetadata(int enclosingMethodInfoIndex, int annotationsIndex, int typeAnnotationsIndex, int classesEncodingIndex, int permittedSubclassesEncodingIndex) { + this.enclosingMethodInfoIndex = enclosingMethodInfoIndex; + this.annotationsIndex = annotationsIndex; + this.typeAnnotationsIndex = typeAnnotationsIndex; + this.classesEncodingIndex = classesEncodingIndex; + this.permittedSubclassesEncodingIndex = permittedSubclassesEncodingIndex; + } + } + + private static final class ReflectionMetadata { + final int fieldsEncodingIndex; + + final int methodsEncodingIndex; + + final int constructorsEncodingIndex; + + @TargetElement(onlyWith = JDK17OrLater.class)// + final int recordComponentsEncodingIndex; + + private ReflectionMetadata(int fieldsEncodingIndex, int methodsEncodingIndex, int constructorsEncodingIndex, int recordComponentsEncodingIndex) { + this.fieldsEncodingIndex = fieldsEncodingIndex; + this.methodsEncodingIndex = methodsEncodingIndex; + this.constructorsEncodingIndex = constructorsEncodingIndex; + this.recordComponentsEncodingIndex = recordComponentsEncodingIndex; + } + } + public Field[] getReachableFields() { - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, fieldsEncodingIndex, false, false); + if (reflectionMetadata == null || reflectionMetadata.fieldsEncodingIndex == NO_DATA) { + return new Field[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, reflectionMetadata.fieldsEncodingIndex, false, false); } public Method[] getReachableMethods() { - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, methodsEncodingIndex, false, false); + if (reflectionMetadata == null || reflectionMetadata.methodsEncodingIndex == NO_DATA) { + return new Method[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, reflectionMetadata.methodsEncodingIndex, false, false); } public Constructor[] getReachableConstructors() { - return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, constructorsEncodingIndex, false, false); + if (reflectionMetadata == null || reflectionMetadata.constructorsEncodingIndex == NO_DATA) { + return new Constructor[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, reflectionMetadata.constructorsEncodingIndex, false, false); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java index b2718e845248..de5b1007f859 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java @@ -24,277 +24,22 @@ */ package com.oracle.svm.core.hub; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedType; import java.lang.reflect.GenericDeclaration; -import java.lang.reflect.MalformedParameterizedTypeException; -import java.lang.reflect.Type; -import org.graalvm.compiler.core.common.SuppressFBWarnings; -import org.graalvm.util.GuardedAnnotationAccess; - -import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.Inject; -import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; -import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; -import sun.reflect.generics.reflectiveObjects.TypeVariableImpl; -import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl; -import sun.reflect.generics.tree.FieldTypeSignature; - -/** - * The bounds in TypeVariableImpl and WildcardTypeImpl are lazily computed. We capture the value and - * cache it in the native image heap. - */ - @TargetClass(sun.reflect.generics.reflectiveObjects.TypeVariableImpl.class) final class Target_sun_reflect_generics_reflectiveObjects_TypeVariableImpl { - @Alias private String name; - - /* Cache the bounds value. */ - - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = TypeVariableBoundsComputer.class) // - private Object[] bounds; - @Alias GenericDeclaration genericDeclaration; - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = TypeVariableAnnotationsComputer.class) // - Annotation[] annotations; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = TypeVariableAnnotatedBoundsComputer.class) // - AnnotatedType[] annotatedBounds; - - @Substitute - public AnnotatedType[] getAnnotatedBounds() { - return annotatedBounds; - } - /** Reason for substitutions: disable access checks in original method. */ @Substitute public GenericDeclaration getGenericDeclaration() { return genericDeclaration; } - - @Substitute - public Annotation[] getAnnotations() { - return annotations; - } -} - -final class Util_sun_reflect_generics_reflectiveObjects_TypeVariableImpl { - - /** Emulate the Java class hierarchy. */ - static Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator asLazyReflectiveObjectGenerator( - Target_sun_reflect_generics_reflectiveObjects_TypeVariableImpl typeVariableImpl) { - return SubstrateUtil.cast(typeVariableImpl, Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator.class); - } - - /** Emulate virtual dispatch. */ - static Type[] reifyBounds(Target_sun_reflect_generics_reflectiveObjects_TypeVariableImpl typeVariableImpl, FieldTypeSignature[] boundASTs) { - return asLazyReflectiveObjectGenerator(typeVariableImpl).reifyBounds(boundASTs); - } -} - -@TargetClass(className = "sun.reflect.generics.reflectiveObjects.LazyReflectiveObjectGenerator") -final class Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator { - - @Alias - native Type[] reifyBounds(FieldTypeSignature[] boundASTs); -} - -class TypeVariableBoundsComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedBoundsAccess.getBounds((TypeVariableImpl) receiver); - } -} - -class TypeVariableAnnotatedBoundsComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedBoundsAccess.getAnnotatedBounds((TypeVariableImpl) receiver); - } -} - -class TypeVariableAnnotationsComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedAnnotationAccess.getAnnotations((TypeVariableImpl) receiver); - } -} - -@TargetClass(sun.reflect.generics.reflectiveObjects.WildcardTypeImpl.class) -final class Target_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl { - - /* Cache the upperBounds value. */ - @Alias // - @RecomputeFieldValue(kind = Kind.Custom, declClass = WildcardTypeImplUpperBoundsComputer.class) // - private Object[] upperBounds; - - /* Cache the lowerBounds value. */ - @Alias // - @RecomputeFieldValue(kind = Kind.Custom, declClass = WildcardTypeImplLowerBoundsComputer.class) // - private Object[] lowerBounds; - - @Substitute - public Type[] getUpperBounds() { - Object[] value = upperBounds; - if (value instanceof FieldTypeSignature[]) { - value = Util_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl.reifyBounds(this, (FieldTypeSignature[]) value); - upperBounds = value; - } - return (Type[]) value.clone(); - } - - @Substitute - public Type[] getLowerBounds() { - Object[] value = lowerBounds; - if (value instanceof FieldTypeSignature[]) { - value = Util_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl.reifyBounds(this, (FieldTypeSignature[]) value); - lowerBounds = value; - } - return (Type[]) value.clone(); - } -} - -class WildcardTypeImplUpperBoundsComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedBoundsAccess.getUpperBounds((WildcardTypeImpl) receiver); - } -} - -class WildcardTypeImplLowerBoundsComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedBoundsAccess.getLowerBounds((WildcardTypeImpl) receiver); - } -} - -final class Util_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl { - - /** Emulate the Java class hierarchy. */ - @SuppressFBWarnings(value = "BC", justification = "Widening cast between @TargetClasses") - static Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator asLazyReflectiveObjectGenerator( - Target_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl wildCardTypeImpl) { - return Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator.class.cast(wildCardTypeImpl); - } - - /** Emulate virtual dispatch. */ - static Type[] reifyBounds(Target_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl wildCardTypeImpl, FieldTypeSignature[] boundASTs) { - return asLazyReflectiveObjectGenerator(wildCardTypeImpl).reifyBounds(boundASTs); - } -} - -class GuardedBoundsAccess { - - static Type[] getLowerBounds(WildcardTypeImpl receiver) { - try { - return receiver.getLowerBounds(); - } catch (TypeNotPresentException | MalformedParameterizedTypeException e) { - /* - * This computer is used to compute the value of the WildcardTypeImpl.lowerBounds field. - * As per WildcardTypeImpl.getLowerBounds() javadoc: - * - * "Returns an array of Type objects representing the lower bound(s) of this - * type variable. Note that if no lower bound is explicitly declared, the lower bound is - * the type of null. In this case, a zero length array is returned." - * - * Thus, if getLowerBounds() throws a TypeNotPresentException, i.e., any of the bounds - * refers to a non-existent type declaration, or a MalformedParameterizedTypeException, - * i.e., any of the bounds refer to a parameterized type that cannot be instantiated, we - * conservatively return a zero length array. - */ - return new Type[0]; - } - } - - static Type[] getUpperBounds(WildcardTypeImpl receiver) { - try { - return receiver.getUpperBounds(); - } catch (TypeNotPresentException | MalformedParameterizedTypeException e) { - /* - * This computer is used to compute the value of the WildcardTypeImpl.upperBounds field. - * As per WildcardTypeImpl.getUpperBounds() javadoc: - * - * "Returns an array of Type objects representing the upper bound(s) of this - * type variable. Note that if no upper bound is explicitly declared, the upper bound is - * Object." - * - * Thus, if getUpperBounds() throws a TypeNotPresentException, i.e., any of the bounds - * refers to a non-existent type declaration, or a MalformedParameterizedTypeException, - * i.e., any of the bounds refer to a parameterized type that cannot be instantiated, we - * conservatively return the upper bound. - */ - return new Type[0]; - } - } - - static Type[] getBounds(TypeVariableImpl receiver) { - try { - return receiver.getBounds(); - } catch (TypeNotPresentException | MalformedParameterizedTypeException e) { - /* - * This computer is used to compute the value of the TypeVariableImpl.bounds field. As - * per TypeVariableImpl.getBounds() javadoc: - * - * "Returns an array of Type objects representing the upper bound(s) of this - * type variable. Note that if no upper bound is explicitly declared, the upper bound is - * Object." - * - * Thus, if getBounds() throws a TypeNotPresentException, i.e., any of the bounds refers - * to a non-existent type declaration, or a MalformedParameterizedTypeException, i.e., - * any of the bounds refer to a parameterized type that cannot be instantiated, we - * conservatively return the upper bound. - */ - return new Type[]{Object.class}; - } - } - - static AnnotatedType[] getAnnotatedBounds(TypeVariableImpl receiver) { - try { - return receiver.getAnnotatedBounds(); - } catch (TypeNotPresentException | MalformedParameterizedTypeException e) { - /* - * This computer is used to compute the value of the TypeVariableImpl.annotatedBounds - * injected field. The original method calls TypeVariableImpl.getBounds() and if no - * bounds are present it returns an empty array. - */ - return new AnnotatedType[0]; - } - } } public class SunReflectTypeSubstitutions { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_module_ModuleDescriptor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_module_ModuleDescriptor.java new file mode 100644 index 000000000000..0f8d6b69fccc --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_module_ModuleDescriptor.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022, 2022, 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.core.jdk; + +import java.lang.module.ModuleDescriptor; +import java.util.Locale; +import java.util.Set; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; + +@TargetClass(ModuleDescriptor.class) +public final class Target_java_lang_module_ModuleDescriptor { + /* Substituted to prevent including stream classes in basic images */ + @Substitute + private static String toString(Set mods, String what) { + StringBuilder builder = new StringBuilder(); + for (M mod : mods) { + builder.append(mod.toString().toLowerCase(Locale.ROOT)); + builder.append(" "); + } + builder.append(what); + return builder.toString(); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java index b642e0319653..3fc182a66745 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java @@ -35,7 +35,8 @@ static JavaConstant readFieldValue(MetaAccessProvider metaAccess, ConstantReflec if (javaField instanceof ReadableJavaField) { ReadableJavaField readableField = (ReadableJavaField) javaField; if (!readableField.isValueAvailable()) { - return SubstrateObjectConstant.forObject(null); + assert !readableField.allowConstantFolding() && readableField.isUnknown(); + return JavaConstant.forIllegal(); } return readableField.readValue(metaAccess, javaConstant); } else { @@ -64,4 +65,8 @@ static boolean injectFinalForRuntimeCompilation(ResolvedJavaField original) { return false; } } + + default boolean isUnknown() { + return false; + } } 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 a566335a87a8..8a8b5c1ee75d 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 @@ -33,7 +33,7 @@ import com.oracle.svm.core.hub.DynamicHub; public interface ReflectionMetadataDecoder { - int NULL_ARRAY = -1; + int NO_DATA = -1; Field[] parseFields(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index 2bc642aca595..cabd098f06db 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -505,7 +505,7 @@ private Map calculateHeapBreakdown(Collection heapObje } long metadataByteLength = ImageSingletons.lookup(ReflectionMetadataDecoder.class).getMetadataByteLength(); if (metadataByteLength > 0) { - classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("reflection metadata", "#glossary-method-metadata"), metadataByteLength); + classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("reflection metadata", "#glossary-reflection-metadata"), metadataByteLength); remainingBytes -= metadataByteLength; } if (graphEncodingByteLength > 0) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java index 146a034e884c..2e5273c7502e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java @@ -25,6 +25,7 @@ package com.oracle.svm.hosted.heap; import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.util.function.Consumer; @@ -42,6 +43,7 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.svm.core.BuildPhaseProvider; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.ReadableJavaField; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.util.VMError; @@ -60,6 +62,7 @@ public class SVMImageHeapScanner extends ImageHeapScanner { private final Class economicMapImpl; private final Field economicMapImplEntriesField; private final Field economicMapImplHashArrayField; + private final RuntimeReflectionSupport reflectionSupport; public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { @@ -69,6 +72,7 @@ public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, Analysi economicMapImplEntriesField = ReflectionUtil.lookupField(economicMapImpl, "entries"); economicMapImplHashArrayField = ReflectionUtil.lookupField(economicMapImpl, "hashArray"); ImageSingletons.add(ImageHeapScanner.class, this); + reflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class); } public static ImageHeapScanner instance() { @@ -143,8 +147,10 @@ protected void onObjectReachable(ImageHeapObject imageHeapObject) { super.onObjectReachable(imageHeapObject); Object object = SubstrateObjectConstant.asObject(imageHeapObject.getObject()); - if (object instanceof AccessibleObject) { - ImageSingletons.lookup(RuntimeReflectionSupport.class).registerHeapReflectionObject((AccessibleObject) object); + if (object instanceof Field || object instanceof Executable) { + reflectionSupport.registerHeapReflectionObject((AccessibleObject) object); + } else if (object instanceof DynamicHub) { + reflectionSupport.registerHeapDynamicHub(object); } } } 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 17bdf1ee7211..9fcc81289c1a 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 @@ -89,8 +89,10 @@ import com.oracle.svm.hosted.code.HostedImageHeapConstantPatch; import com.oracle.svm.hosted.image.NativeImage.NativeTextSectionImpl; import com.oracle.svm.hosted.meta.HostedField; +import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.hosted.meta.HostedUniverse; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.site.Call; @@ -238,66 +240,78 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code } ReflectionMetadataEncoder reflectionMetadataEncoder = ImageSingletons.lookup(ReflectionMetadataEncoderFactory.class).create(encoders); - for (HostedType type : imageHeap.getUniverse().getTypes()) { - Map, Set>> innerClasses = ImageSingletons.lookup(RuntimeReflectionSupport.class).getReflectionInnerClasses(); - if (type.getWrapped().isReachable()) { - reflectionMetadataEncoder.addClassMetadata(imageHeap.getMetaAccess(), type, innerClasses.getOrDefault(type.getJavaClass(), Collections.emptySet()).toArray(new Class[0])); + RuntimeReflectionSupport reflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class); + HostedUniverse hUniverse = imageHeap.getUniverse(); + HostedMetaAccess hMetaAccess = imageHeap.getMetaAccess(); + + Map, Set>> innerClasses = reflectionSupport.getReflectionInnerClasses(); + Set heapDynamicHubs = reflectionSupport.getHeapDynamicHubs(); + for (HostedType type : hUniverse.getTypes()) { + if (type.getWrapped().isReachable() && heapDynamicHubs.contains(type.getHub())) { + Class[] typeInnerClasses = innerClasses.getOrDefault(type.getJavaClass(), Collections.emptySet()).toArray(new Class[0]); + reflectionMetadataEncoder.addClassMetadata(hMetaAccess, type, typeInnerClasses); } } + Set includedFields = new HashSet<>(); Set includedMethods = new HashSet<>(); - for (AccessibleObject object : ImageSingletons.lookup(RuntimeReflectionSupport.class).getHeapReflectionObjects()) { + + for (AccessibleObject object : reflectionSupport.getHeapReflectionObjects()) { if (object instanceof Field) { - includedFields.add(imageHeap.getMetaAccess().lookupJavaField((Field) object)); + includedFields.add(hMetaAccess.lookupJavaField((Field) object)); + } else if (object instanceof Method || object instanceof Constructor) { + includedMethods.add(hMetaAccess.lookupJavaMethod((Executable) object)); } - if (object instanceof Method || object instanceof Constructor) { - includedMethods.add(imageHeap.getMetaAccess().lookupJavaMethod((Executable) object)); - } - reflectionMetadataEncoder.addHeapObjectMetadata(imageHeap.getMetaAccess(), object); + reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, object); } - for (Field reflectField : ImageSingletons.lookup(RuntimeReflectionSupport.class).getReflectionFields()) { - HostedField field = imageHeap.getMetaAccess().lookupJavaField(reflectField); + + for (Field reflectField : reflectionSupport.getReflectionFields()) { + HostedField field = hMetaAccess.lookupJavaField(reflectField); if (!includedFields.contains(field)) { - reflectionMetadataEncoder.addReflectionFieldMetadata(imageHeap.getMetaAccess(), field, reflectField); + reflectionMetadataEncoder.addReflectionFieldMetadata(hMetaAccess, field, reflectField); includedFields.add(field); } } - for (Executable reflectMethod : ImageSingletons.lookup(RuntimeReflectionSupport.class).getReflectionExecutables()) { - HostedMethod method = imageHeap.getMetaAccess().lookupJavaMethod(reflectMethod); + + for (Executable reflectMethod : reflectionSupport.getReflectionExecutables()) { + HostedMethod method = hMetaAccess.lookupJavaMethod(reflectMethod); if (!includedMethods.contains(method)) { - Object accessor = ImageSingletons.lookup(RuntimeReflectionSupport.class).getAccessor(reflectMethod); - reflectionMetadataEncoder.addReflectionExecutableMetadata(imageHeap.getMetaAccess(), method, reflectMethod, accessor); + Object accessor = reflectionSupport.getAccessor(reflectMethod); + reflectionMetadataEncoder.addReflectionExecutableMetadata(hMetaAccess, method, reflectMethod, accessor); includedMethods.add(method); } } - for (Object method : ImageSingletons.lookup(RuntimeReflectionSupport.class).getHidingReflectionMethods()) { + + for (Object method : reflectionSupport.getHidingReflectionMethods()) { AnalysisMethod hidingMethod = (AnalysisMethod) method; - HostedMethod hostedMethod = imageHeap.getUniverse().optionalLookup(hidingMethod); + HostedMethod hostedMethod = hUniverse.optionalLookup(hidingMethod); if (hostedMethod == null || !includedMethods.contains(hostedMethod)) { - HostedType declaringType = imageHeap.getUniverse().lookup(hidingMethod.getDeclaringClass()); + HostedType declaringType = hUniverse.lookup(hidingMethod.getDeclaringClass()); String name = hidingMethod.getName(); JavaType[] analysisParameterTypes = hidingMethod.getSignature().toParameterTypes(null); HostedType[] parameterTypes = new HostedType[analysisParameterTypes.length]; for (int i = 0; i < analysisParameterTypes.length; ++i) { - parameterTypes[i] = imageHeap.getUniverse().lookup(analysisParameterTypes[i]); + parameterTypes[i] = hUniverse.lookup(analysisParameterTypes[i]); } int modifiers = hidingMethod.getModifiers(); - HostedType returnType = imageHeap.getUniverse().lookup(hidingMethod.getSignature().getReturnType(null)); - reflectionMetadataEncoder.addHidingMethodMetadata(declaringType, name, parameterTypes, modifiers, returnType); + HostedType returnType = hUniverse.lookup(hidingMethod.getSignature().getReturnType(null)); + reflectionMetadataEncoder.addHidingMethodMetadata(hidingMethod, declaringType, name, parameterTypes, modifiers, returnType); if (hostedMethod != null) { includedMethods.add(hostedMethod); } } } + if (SubstrateOptions.IncludeMethodData.getValue()) { - for (HostedField field : imageHeap.getUniverse().getFields()) { + for (HostedField field : hUniverse.getFields()) { if (field.isAccessed() && !includedFields.contains(field)) { reflectionMetadataEncoder.addReachableFieldMetadata(field); } } - for (HostedMethod method : imageHeap.getUniverse().getMethods()) { + + for (HostedMethod method : hUniverse.getMethods()) { if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod() && !includedMethods.contains(method)) { - reflectionMetadataEncoder.addReachableMethodMetadata(method); + reflectionMetadataEncoder.addReachableExecutableMetadata(method); } } } @@ -608,13 +622,13 @@ public interface ReflectionMetadataEncoder { void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, HostedMethod sharedMethod, Executable reflectMethod, Object accessor); - void addHeapObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object); + void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object); - void addHidingMethodMetadata(HostedType declType, String name, HostedType[] paramTypes, int modifiers, HostedType returnType); + void addHidingMethodMetadata(AnalysisMethod analysisMethod, HostedType declType, String name, HostedType[] paramTypes, int modifiers, HostedType returnType); void addReachableFieldMetadata(HostedField field); - void addReachableMethodMetadata(HostedMethod method); + void addReachableExecutableMetadata(HostedMethod method); void encodeAllAndInstall(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java index 2e1aed9d04ad..391c42457af9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java @@ -53,6 +53,8 @@ import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueProvider; import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueTransformer; +import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.annotate.UnknownPrimitiveField; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.meta.ReadableJavaField; import com.oracle.svm.core.util.UserError; @@ -499,4 +501,9 @@ public String toString() { public Field getJavaField() { return OriginalFieldProvider.getJavaField(GraalAccess.getOriginalSnippetReflection(), original); } + + @Override + public boolean isUnknown() { + return annotated != null && (annotated.isAnnotationPresent(UnknownObjectField.class) || annotated.isAnnotationPresent(UnknownPrimitiveField.class)); + } } 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 3339ea1ef660..29149fd945aa 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 @@ -67,13 +67,13 @@ import com.oracle.svm.core.annotate.InjectAccessors; import com.oracle.svm.core.hub.AnnotationTypeSupport; import com.oracle.svm.core.hub.ClassForNameSupport; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.RecordSupport; import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ConditionalConfigurationRegistry; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; -import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.annotation.AnnotationSubstitutionType; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; import com.oracle.svm.util.ModuleSupport; @@ -99,12 +99,13 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl private final Set registeredMethods = ConcurrentHashMap.newKeySet(); private final Set registeredFields = ConcurrentHashMap.newKeySet(); private final Map, Object[]> registeredRecordComponents = new ConcurrentHashMap<>(); + private final Set heapDynamicHubs = ConcurrentHashMap.newKeySet(); private final Set heapReflectionObjects = ConcurrentHashMap.newKeySet(); private final Map, Set>> innerClasses = new ConcurrentHashMap<>(); private final Set> processedClasses = new HashSet<>(); private final Set processedTypes = new HashSet<>(); - private final Set processedAnalysisTypes = new HashSet<>(); + private final Set processedDynamicHubs = new HashSet<>(); private final Map> processedHidingMethods = new HashMap<>(); private final Set processedHeapReflectionObjects = new HashSet<>(); @@ -136,12 +137,15 @@ public void register(ConfigurationCondition condition, boolean queriedOnly, Exec private void registerMethods(boolean queriedOnly, Executable[] methods) { for (Executable method : methods) { - if (reflectionMethods.containsKey(method) && reflectionMethods.get(method) == ExecutableAccessibility.Accessed) { - /* Do not downgrade a method already registered as accessed */ - continue; - } - ExecutableAccessibility newValue = queriedOnly ? ExecutableAccessibility.QueriedOnly : ExecutableAccessibility.Accessed; - ExecutableAccessibility oldValue = reflectionMethods.put(method, newValue); + ExecutableAccessibility oldValue; + ExecutableAccessibility newValue; + do { + newValue = queriedOnly ? ExecutableAccessibility.QueriedOnly : ExecutableAccessibility.Accessed; + oldValue = reflectionMethods.get(method); + if (oldValue != null) { + newValue = ExecutableAccessibility.max(oldValue, newValue); + } + } while (oldValue == null ? reflectionMethods.putIfAbsent(method, newValue) != null : !reflectionMethods.replace(method, oldValue, newValue)); if (oldValue != newValue) { modifiedClasses.add(method.getDeclaringClass()); } @@ -249,10 +253,13 @@ private void processReachableTypes(DuringAnalysisAccessImpl access) { * See {@link ReflectionMetadataEncoderImpl} for details. */ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { - for (AnalysisType type : access.getUniverse().getTypes()) { - if (!processedAnalysisTypes.contains(type) && type.isReachable() && !SubstitutionReflectivityFilter.shouldExclude(type.getJavaClass(), access.getMetaAccess(), access.getUniverse())) { - registerTypesForClass(access, type, type.getJavaClass()); - processedAnalysisTypes.add(type); + for (DynamicHub hub : heapDynamicHubs) { + if (!processedDynamicHubs.contains(hub)) { + AnalysisType type = access.getHostVM().lookupType(hub); + if (!SubstitutionReflectivityFilter.shouldExclude(type.getJavaClass(), access.getMetaAccess(), access.getUniverse())) { + registerTypesForClass(access, type, type.getJavaClass()); + processedDynamicHubs.add(hub); + } } } for (Field reflectField : reflectionFields) { @@ -463,6 +470,7 @@ 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) { @@ -472,10 +480,7 @@ private void makeTypeReachable(DuringAnalysisAccessImpl access, Type type) { } } catch (TypeNotPresentException e) { /* Hash code computation can trigger an exception if the type is missing */ - if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { - return; - } - throw e; + return; } processedTypes.add(type); if (type instanceof Class && !SubstitutionReflectivityFilter.shouldExclude((Class) type, access.getMetaAccess(), access.getUniverse())) { @@ -524,11 +529,8 @@ private static void registerTypesForRecordComponent(DuringAnalysisAccessImpl acc } } - private static void registerTypesForAnnotation(DuringAnalysisAccessImpl accessImpl, Annotation annotation) { - /* - * Don't make annotation types reachable unless they have a chance of being queried. - */ - accessImpl.registerReachabilityHandler((access) -> registerTypesForAnnotationValue((DuringAnalysisAccessImpl) access, annotation.annotationType(), annotation), annotation.annotationType()); + private static void registerTypesForAnnotation(DuringAnalysisAccessImpl access, Annotation annotation) { + registerTypesForAnnotationValue(access, annotation.annotationType(), annotation); } @SuppressWarnings("unchecked") @@ -784,6 +786,18 @@ public Object[] getRecordComponents(Class type) { return registeredRecordComponents.get(type); } + @Override + public void registerHeapDynamicHub(Object hub) { + assert !sealed; + heapDynamicHubs.add((DynamicHub) hub); + } + + @Override + public Set getHeapDynamicHubs() { + assert sealed; + return Collections.unmodifiableSet(heapDynamicHubs); + } + @Override public void registerHeapReflectionObject(AccessibleObject object) { assert !sealed; @@ -813,6 +827,10 @@ public int getReflectionFieldsCount() { private enum ExecutableAccessibility { QueriedOnly, - Accessed + Accessed; + + static ExecutableAccessibility max(ExecutableAccessibility a, ExecutableAccessibility b) { + return a == Accessed || b == Accessed ? Accessed : QueriedOnly; + } } } 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 c71f6c373be4..6f1d6303d536 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 @@ -164,8 +164,6 @@ public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; aUniverse = access.getUniverse(); - access.registerObjectReplacer(new ReflectionObjectReplacer()); - ReflectionConfigurationParser>> parser = ConfigurationParserUtils.create(reflectionData, access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, access.getImageClassLoader(), "reflection", ConfigurationFiles.Options.ReflectionConfigurationFiles, ConfigurationFiles.Options.ReflectionConfigurationResources, 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 225e7d1cbd79..5c72a4163b17 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 @@ -65,19 +65,15 @@ static class AnnotatedElementMetadata { static class ClassMetadata extends AnnotatedElementMetadata { final HostedType[] classes; - final HostedType enclosingMethodDeclaringClass; - final String enclosingMethodName; - final String enclosingMethodDescriptor; + final Object[] enclosingMethodInfo; final RecordComponentMetadata[] recordComponents; final HostedType[] permittedSubclasses; - ClassMetadata(HostedType[] classes, HostedType enclosingMethodDeclaringClass, String enclosingMethodName, String enclosingMethodDescriptor, RecordComponentMetadata[] recordComponents, - HostedType[] permittedSubclasses, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + ClassMetadata(HostedType[] classes, Object[] enclosingMethodInfo, RecordComponentMetadata[] recordComponents, HostedType[] permittedSubclasses, Annotation[] annotations, + TypeAnnotation[] typeAnnotations) { super(annotations, typeAnnotations); this.classes = classes; - this.enclosingMethodDeclaringClass = enclosingMethodDeclaringClass; - this.enclosingMethodName = enclosingMethodName; - this.enclosingMethodDescriptor = enclosingMethodDescriptor; + this.enclosingMethodInfo = enclosingMethodInfo; this.recordComponents = recordComponents; this.permittedSubclasses = permittedSubclasses; } 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 a3066bf623f8..1591d0bcf51c 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 @@ -24,6 +24,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.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; @@ -39,10 +40,12 @@ import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; @@ -59,12 +62,12 @@ import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.code.CodeInfoEncoder; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.meta.SubstrateObjectConstant; -import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.ByteArrayReader; import com.oracle.svm.core.util.VMError; @@ -99,22 +102,25 @@ import sun.reflect.annotation.TypeNotPresentExceptionProxy; /** - * The method metadata encoding puts data in the image for three distinct types of methods. - *
    - *
  1. Methods that are queried for reflection, but never accessed: in that case, the encoding - * includes everything required to recreate an {@link Executable} object at runtime.
  2. - *
  3. Methods that hide a method registered for reflection, but are not registered themselves: only - * basic method information is stored for those methods (declaring class, name and parameter types). - * They are used to ensure that the hidden superclass method is not incorrectly returned by a - * reflection query on the subclass where it is hidden.
  4. - *
  5. Methods that are included in the image: all reachable methods have their basic information - * included to enable introspecting the produced executable.
  6. - *
- * + * The reflection metadata encoder creates metadata for reflection objects (classes, fields, methods + * and constructors), as well as for annotations and other objects queried from the VM to enable + * their creation at runtime. The metadata related to classes is saved to a single byte array (see + * {@link ReflectionMetadataEncoding}), with the index into this array saved in the corresponding + * {@link DynamicHub}. The metadata for reflection objects already present in the image heap is + * encoded directly as byte arrays into the corresponding object through field recomputations. + * + * Method, field and constructor metadata can be complete, meaning it can be used to recreate a + * working object of the corresponding type, or not, meaning it only holds the element's signature. + * The incomplete form is used to implement hiding methods, which are methods that are not + * registered for reflection but override a registered method, and as such should be seen by the JDK + * code determining which superclass methods to include in a certain class (see + * {@link Class#getMethods()}). + * * Emitting the metadata happens in two phases. In the first phase, the string and class encoders - * are filled with the necessary values (in the {@code add*MethodMetadata} functions). In a second - * phase, the values are encoded as byte arrays and stored in {@link DynamicHub} arrays (see - * {@link #encodeAllAndInstall()}). + * are filled with the necessary values (in the {@code #add*Metadata} functions). In a second phase, + * the values are encoded into their intended byte arrays (see {@link #encodeAllAndInstall()}). + * + * The metadata encoding format is detailed in {@link ReflectionMetadataDecoderImpl}. */ public class ReflectionMetadataEncoderImpl implements ReflectionMetadataEncoder { @@ -126,12 +132,13 @@ public ReflectionMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { } private final CodeInfoEncoder.Encoders encoders; + private final ReflectionDataAccessors accessors; private final Map, JavaConstant> annotationExceptionProxies = new HashMap<>(); private final TreeSet sortedTypes = new TreeSet<>(Comparator.comparingLong(t -> t.getHub().getTypeID())); private final Map classData = new HashMap<>(); - private final Map> fieldData = new HashMap<>(); - private final Map> methodData = new HashMap<>(); - private final Map> constructorData = new HashMap<>(); + private final Map> fieldData = new HashMap<>(); + private final Map> methodData = new HashMap<>(); + private final Map> constructorData = new HashMap<>(); private final Set heapData = new HashSet<>(); @@ -143,6 +150,7 @@ public ReflectionMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { public ReflectionMetadataEncoderImpl(CodeInfoEncoder.Encoders encoders) { this.encoders = encoders; + this.accessors = new ReflectionDataAccessors(); } private void registerClass(HostedType type, ClassMetadata metadata) { @@ -150,19 +158,60 @@ private void registerClass(HostedType type, ClassMetadata metadata) { classData.put(type, metadata); } - private void registerField(HostedType declaringType, FieldMetadata metadata) { + private void registerField(HostedType declaringType, Object field, FieldMetadata metadata) { sortedTypes.add(declaringType); - fieldData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(metadata); + FieldMetadata oldData = fieldData.computeIfAbsent(declaringType, t -> new HashMap<>()).put(field, metadata); + assert oldData == null; + } + + private FieldMetadata[] getFields(HostedType declaringType) { + Field[] jdkFields = accessors.getDeclaredFields(declaringType.getJavaClass()); + Map fieldMetadata = fieldData.getOrDefault(declaringType, Collections.emptyMap()); + return sortElements(jdkFields, fieldMetadata).toArray(new FieldMetadata[0]); } - private void registerMethod(HostedType declaringType, MethodMetadata metadata) { + private void registerMethod(HostedType declaringType, Object method, MethodMetadata metadata) { sortedTypes.add(declaringType); - methodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(metadata); + MethodMetadata oldData = methodData.computeIfAbsent(declaringType, t -> new HashMap<>()).put(method, metadata); + assert oldData == null; + } + + private MethodMetadata[] getMethods(HostedType declaringType) { + Method[] jdkMethods = accessors.getDeclaredMethods(declaringType.getJavaClass()); + Map methodMetadata = methodData.getOrDefault(declaringType, Collections.emptyMap()); + return sortElements(jdkMethods, methodMetadata).toArray(new MethodMetadata[0]); } - private void registerConstructor(HostedType declaringType, ConstructorMetadata metadata) { + private void registerConstructor(HostedType declaringType, Object constructor, ConstructorMetadata metadata) { sortedTypes.add(declaringType); - constructorData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(metadata); + ConstructorMetadata oldData = constructorData.computeIfAbsent(declaringType, t -> new HashMap<>()).put(constructor, metadata); + assert oldData == null; + } + + private ConstructorMetadata[] getConstructors(HostedType declaringType) { + Constructor[] jdkConstructors = accessors.getDeclaredConstructors(declaringType.getJavaClass()); + Map constructorMetadata = constructorData.getOrDefault(declaringType, Collections.emptyMap()); + return sortElements(jdkConstructors, constructorMetadata).toArray(new ConstructorMetadata[0]); + } + + /* Sort elements in the same order as the JCK */ + private static List sortElements(T[] jdkElements, Map metadata) { + List orderedElements = new ArrayList<>(); + List trailingElements = new ArrayList<>(); + for (T element : jdkElements) { + if (metadata.containsKey(element)) { + M elementMetadata = metadata.remove(element); + if (element instanceof Method && ((Method) element).isBridge()) { + trailingElements.add(elementMetadata); + } else { + orderedElements.add(elementMetadata); + } + } + } + /* Add non-reflection metadata to the end of the list */ + orderedElements.addAll(metadata.values()); + orderedElements.addAll(trailingElements); + return orderedElements; } @Override @@ -194,9 +243,9 @@ public byte[] getReflectParametersEncoding(Executable object) { public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Class[] innerClasses) { Class javaClass = type.getHub().getHostedJavaClass(); Object[] enclosingMethodInfo = getEnclosingMethodInfo(javaClass); - HostedType enclosingMethodDeclaringClass = enclosingMethodInfo != null ? ((HostedMetaAccess) metaAccess).lookupJavaType((Class) enclosingMethodInfo[0]) : null; - String enclosingMethodName = enclosingMethodInfo != null ? (String) enclosingMethodInfo[1] : null; - String enclosingMethodDescriptor = enclosingMethodInfo != null ? (String) enclosingMethodInfo[2] : null; + if (enclosingMethodInfo != null) { + enclosingMethodInfo[0] = ((HostedMetaAccess) metaAccess).lookupJavaType((Class) enclosingMethodInfo[0]); + } RecordComponentMetadata[] recordComponents = getRecordComponents(metaAccess, type, javaClass); Class[] permittedSubclasses = getPermittedSubclasses(metaAccess, javaClass); Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(type); @@ -205,16 +254,16 @@ public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Cla /* Register string and class values in annotations */ encoders.sourceClasses.addObject(javaClass); if (enclosingMethodInfo != null) { - encoders.sourceClasses.addObject(enclosingMethodDeclaringClass.getJavaClass()); - encoders.sourceMethodNames.addObject(enclosingMethodName); - encoders.sourceMethodNames.addObject(enclosingMethodDescriptor); + encoders.sourceClasses.addObject(((HostedType) enclosingMethodInfo[0]).getJavaClass()); + encoders.sourceMethodNames.addObject((String) enclosingMethodInfo[1]); + encoders.sourceMethodNames.addObject((String) enclosingMethodInfo[2]); } HostedType[] innerTypes = registerClassValues(metaAccess, innerClasses); HostedType[] permittedSubtypes = (permittedSubclasses != null) ? registerClassValues(metaAccess, permittedSubclasses) : null; annotations = registerAnnotationValues(metaAccess, annotations); typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); - registerClass(type, new ClassMetadata(innerTypes, enclosingMethodDeclaringClass, enclosingMethodName, enclosingMethodDescriptor, recordComponents, permittedSubtypes, annotations, + registerClass(type, new ClassMetadata(innerTypes, enclosingMethodInfo, recordComponents, permittedSubtypes, annotations, typeAnnotations)); } @@ -276,8 +325,7 @@ public void addReflectionFieldMetadata(MetaAccessProvider metaAccess, HostedFiel annotations = registerAnnotationValues(metaAccess, annotations); typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); - registerField(declaringType, new FieldMetadata(declaringType, name, type, modifiers, trustedFinal, signature, - annotations, typeAnnotations, offset, deletedReason)); + registerField(declaringType, reflectField, new FieldMetadata(declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason)); } @Override @@ -330,10 +378,10 @@ public void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, Hoste } if (isMethod) { - registerMethod(declaringType, new MethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, - annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, accessorConstant)); + registerMethod(declaringType, reflectMethod, new MethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, + parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, accessorConstant)); } else { - registerConstructor(declaringType, new ConstructorMetadata(declaringType, parameterTypes, modifiers, exceptionTypes, signature, annotations, + registerConstructor(declaringType, reflectMethod, new ConstructorMetadata(declaringType, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessorConstant)); } } @@ -352,7 +400,7 @@ private static boolean isTrustedFinal(Field field) { } @Override - public void addHeapObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object) { + public void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object) { boolean isExecutable = object instanceof Executable; boolean isMethod = object instanceof Method; Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(object); @@ -377,19 +425,20 @@ public void addHeapObjectMetadata(MetaAccessProvider metaAccess, AccessibleObjec } } } - JavaConstant heapObjectConstant = SubstrateObjectConstant.forObject(getHolder(object)); + AccessibleObject holder = getHolder(object); + JavaConstant heapObjectConstant = SubstrateObjectConstant.forObject(holder); encoders.objectConstants.addObject(heapObjectConstant); AccessibleObjectMetadata metadata; if (isMethod) { metadata = new MethodMetadata(heapObjectConstant, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters); - registerMethod((HostedType) metaAccess.lookupJavaType(((Method) object).getDeclaringClass()), (MethodMetadata) metadata); + registerMethod((HostedType) metaAccess.lookupJavaType(((Method) object).getDeclaringClass()), holder, (MethodMetadata) metadata); } else if (isExecutable) { metadata = new ConstructorMetadata(heapObjectConstant, annotations, parameterAnnotations, typeAnnotations, reflectParameters); - registerConstructor((HostedType) metaAccess.lookupJavaType(((Constructor) object).getDeclaringClass()), (ConstructorMetadata) metadata); + registerConstructor((HostedType) metaAccess.lookupJavaType(((Constructor) object).getDeclaringClass()), holder, (ConstructorMetadata) metadata); } else { metadata = new FieldMetadata(heapObjectConstant, annotations, typeAnnotations); - registerField((HostedType) metaAccess.lookupJavaType(((Field) object).getDeclaringClass()), (FieldMetadata) metadata); + registerField((HostedType) metaAccess.lookupJavaType(((Field) object).getDeclaringClass()), holder, (FieldMetadata) metadata); } heapData.add(metadata); } @@ -519,7 +568,7 @@ private void registerAnnotationValue(Class type, Object value) { } @Override - public void addHidingMethodMetadata(HostedType declaringType, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType) { + public void addHidingMethodMetadata(AnalysisMethod analysisMethod, HostedType declaringType, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType) { /* Fill encoders with the necessary values. */ encoders.sourceMethodNames.addObject(name); for (HostedType parameterType : parameterTypes) { @@ -528,7 +577,7 @@ public void addHidingMethodMetadata(HostedType declaringType, String name, Hoste encoders.sourceClasses.addObject(returnType.getJavaClass()); sortedTypes.add(declaringType); - registerMethod(declaringType, new MethodMetadata(true, declaringType, name, parameterTypes, modifiers, returnType)); + registerMethod(declaringType, analysisMethod, new MethodMetadata(true, declaringType, name, parameterTypes, modifiers, returnType)); } @Override @@ -539,16 +588,17 @@ public void addReachableFieldMetadata(HostedField field) { /* Fill encoders with the necessary values. */ encoders.sourceMethodNames.addObject(name); - registerField(declaringType, new FieldMetadata(declaringType, name)); + registerField(declaringType, field, new FieldMetadata(declaringType, name)); } @Override - public void addReachableMethodMetadata(HostedMethod method) { - boolean isMethod = !method.isConstructor(); - HostedType declaringType = method.getDeclaringClass(); - String name = isMethod ? method.getName() : null; - HostedType[] parameterTypes = getParameterTypes(method); - int modifiers = method.getModifiers(); + public void addReachableExecutableMetadata(HostedMethod executable) { + boolean isMethod = !executable.isConstructor(); + 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) { @@ -557,11 +607,12 @@ public void addReachableMethodMetadata(HostedMethod method) { for (HostedType parameterType : parameterTypes) { encoders.sourceClasses.addObject(parameterType.getJavaClass()); } + encoders.sourceClasses.addObject(returnType.getJavaClass()); if (isMethod) { - registerMethod(declaringType, new MethodMetadata(false, declaringType, name, parameterTypes, modifiers, null)); + registerMethod(declaringType, executable, new MethodMetadata(false, declaringType, name, parameterTypes, modifiers, returnType)); } else { - registerConstructor(declaringType, new ConstructorMetadata(declaringType, parameterTypes, modifiers)); + registerConstructor(declaringType, executable, new ConstructorMetadata(declaringType, parameterTypes, modifiers)); } } @@ -679,43 +730,41 @@ private static Method getRecordComponentAccessor(Object recordComponent) { @Override public void encodeAllAndInstall() { UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - encodeAndAddCollection(buf, sortedTypes.toArray(new HostedType[0]), this::encodeType, (ignored) -> { - }, false); + int typesIndex = encodeAndAddCollection(buf, sortedTypes.toArray(new HostedType[0]), this::encodeType, false); + assert typesIndex == 0; for (HostedType declaringType : sortedTypes) { DynamicHub hub = declaringType.getHub(); ClassMetadata classMetadata = classData.get(declaringType); - encodeAndAddCollection(buf, classMetadata.classes, this::encodeType, hub::setClassesEncodingIndex, false); - encodeAndAddElement(buf, new Object[]{classMetadata.enclosingMethodDeclaringClass, classMetadata.enclosingMethodName, classMetadata.enclosingMethodDescriptor}, - this::encodeEnclosingMethod, hub::setEnclosingMethodInfoIndex); - if (JavaVersionUtil.JAVA_SPEC >= 17) { - encodeAndAddCollection(buf, classMetadata.recordComponents, this::encodeRecordComponent, hub::setRecordComponentsEncodingIndex, true); - encodeAndAddCollection(buf, classMetadata.permittedSubclasses, this::encodeType, hub::setPermittedSubclassesEncodingIndex, true); + + int enclosingMethodInfoIndex = encodeAndAddElement(buf, classMetadata.enclosingMethodInfo, this::encodeEnclosingMethod); + int annotationsIndex = encodeAndAddEncodedElement(buf, classMetadata.annotations, this::encodeAnnotations); + int typeAnnotationsIndex = encodeAndAddEncodedElement(buf, classMetadata.typeAnnotations, this::encodeTypeAnnotations); + 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 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; + if (anySet(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex)) { + hub.setReflectionMetadata(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex); } - encodeAndAddEncodedElement(buf, classMetadata.annotations, this::encodeAnnotations, hub::setAnnotationsEncodingIndex); - encodeAndAddEncodedElement(buf, classMetadata.typeAnnotations, this::encodeTypeAnnotations, - hub::setTypeAnnotationsEncodingIndex); - encodeAndAddCollection(buf, fieldData.getOrDefault(declaringType, Collections.emptySet()).toArray(new FieldMetadata[0]), this::encodeField, hub::setFieldsEncodingIndex, - false); - encodeAndAddCollection(buf, methodData.getOrDefault(declaringType, Collections.emptySet()).toArray(new MethodMetadata[0]), this::encodeExecutable, - hub::setMethodsEncodingIndex, false); - encodeAndAddCollection(buf, constructorData.getOrDefault(declaringType, Collections.emptySet()).toArray(new ConstructorMetadata[0]), this::encodeExecutable, - hub::setConstructorsEncodingIndex, false); } for (AccessibleObjectMetadata metadata : heapData) { AccessibleObject heapObject = (AccessibleObject) SubstrateObjectConstant.asObject(metadata.heapObject); - encodeAndAddHeapElement(metadata.annotations, this::encodeAnnotations, (array) -> annotationsEncodings.put(heapObject, array)); - encodeAndAddHeapElement(metadata.typeAnnotations, this::encodeTypeAnnotations, (array) -> typeAnnotationsEncodings.put(heapObject, array)); + annotationsEncodings.put(heapObject, encodeAnnotations(metadata.annotations)); + typeAnnotationsEncodings.put(heapObject, encodeTypeAnnotations(metadata.typeAnnotations)); if (metadata instanceof ExecutableMetadata) { - encodeAndAddHeapElement(((ExecutableMetadata) metadata).parameterAnnotations, this::encodeParameterAnnotations, - (array) -> parameterAnnotationsEncodings.put((Executable) heapObject, array)); + parameterAnnotationsEncodings.put((Executable) heapObject, encodeParameterAnnotations(((ExecutableMetadata) metadata).parameterAnnotations)); if (((ExecutableMetadata) metadata).reflectParameters != null) { - encodeAndAddHeapElement(((ExecutableMetadata) metadata).reflectParameters, this::encodeReflectParameters, - (array) -> reflectParametersEncodings.put((Executable) heapObject, array)); + reflectParametersEncodings.put((Executable) heapObject, encodeReflectParameters(((ExecutableMetadata) metadata).reflectParameters)); } if (metadata instanceof MethodMetadata && ((Method) SubstrateObjectConstant.asObject(metadata.heapObject)).getDeclaringClass().isAnnotation() && ((MethodMetadata) metadata).annotationDefault != null) { - encodeAndAddHeapElement(((MethodMetadata) metadata).annotationDefault, this::encodeMemberValue, - (array) -> annotationDefaultEncodings.put((Method) heapObject, array)); + annotationDefaultEncodings.put((Method) heapObject, encodeMemberValue(((MethodMetadata) metadata).annotationDefault)); } } } @@ -724,34 +773,37 @@ public void encodeAllAndInstall() { ImageSingletons.add(ReflectionMetadataEncoder.class, this); } - private static void encodeAndAddCollection(UnsafeArrayTypeWriter buf, T[] data, BiConsumer encodeCallback, Consumer saveCallback, boolean canBeNull) { - int offset = ReflectionMetadataDecoder.NULL_ARRAY; - if (!canBeNull || data != null) { - offset = TypeConversion.asS4(buf.getBytesWritten()); - encodeArray(buf, data, element -> encodeCallback.accept(buf, element)); + private static int encodeAndAddCollection(UnsafeArrayTypeWriter buf, T[] data, BiConsumer encodeCallback, boolean canBeNull) { + if (data == null || (!canBeNull && data.length == 0)) { + /* + * We must encode a zero-length array if it does not have the same meaning as a null + * array (e.g. for permitted classes) + */ + return NO_DATA; } - saveCallback.accept(offset); + int offset = TypeConversion.asS4(buf.getBytesWritten()); + encodeArray(buf, data, element -> encodeCallback.accept(buf, element)); + return offset; } - private static void encodeAndAddElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback, Consumer saveCallback) { + private static int encodeAndAddElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback) { byte[] encoding = encodeCallback.apply(data); - int offset = ReflectionMetadataDecoder.NULL_ARRAY; - if (encoding != null) { - offset = TypeConversion.asS4(buf.getBytesWritten()); - encodeBytes(buf, encoding); + if (encoding == null) { + return NO_DATA; } - saveCallback.accept(offset); - } - - private static void encodeAndAddEncodedElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback, Consumer saveCallback) { int offset = TypeConversion.asS4(buf.getBytesWritten()); - encodeByteArray(buf, encodeCallback.apply(data)); - saveCallback.accept(offset); + encodeBytes(buf, encoding); + return offset; } - private static void encodeAndAddHeapElement(T data, Function encodeCallback, Consumer saveCallback) { + private static int encodeAndAddEncodedElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback) { byte[] encoding = encodeCallback.apply(data); - saveCallback.accept(encoding); + if (encoding == null) { + return NO_DATA; + } + int offset = TypeConversion.asS4(buf.getBytesWritten()); + encodeByteArray(buf, encoding); + return offset; } private static void install(UnsafeArrayTypeWriter encodingBuffer) { @@ -760,7 +812,17 @@ private static void install(UnsafeArrayTypeWriter encodingBuffer) { ImageSingletons.lookup(ReflectionMetadataEncoding.class).setEncoding(encodingBuffer.toArray(dataEncoding)); } + private static boolean anySet(int... indices) { + for (int index : indices) { + if (index != NO_DATA) { + return true; + } + } + return false; + } + 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; int modifiers = field.modifiers; modifiers |= field.complete ? COMPLETE_FLAG_MASK : 0; @@ -786,6 +848,7 @@ private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) { private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata executable) { boolean isMethod = executable instanceof MethodMetadata; + /* 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; int modifiers = executable.modifiers; modifiers |= executable.complete ? COMPLETE_FLAG_MASK : 0; @@ -799,7 +862,7 @@ private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata exec encodeName(buf, ((MethodMetadata) executable).name); } encodeArray(buf, executable.parameterTypes, parameterType -> encodeType(buf, parameterType)); - if (isMethod && (executable.complete || ((MethodMetadata) executable).hiding)) { + if (isMethod) { encodeType(buf, ((MethodMetadata) executable).returnType); } if (executable.complete) { @@ -843,6 +906,10 @@ private static void encodeArray(UnsafeArrayTypeWriter buf, T[] array, Consum } private static void encodeByteArray(UnsafeArrayTypeWriter buf, byte[] array) { + if (array == null) { + buf.putUV(NO_DATA); + return; + } buf.putUV(array.length); encodeBytes(buf, array); } @@ -899,6 +966,9 @@ private static Parameter[] getRawParameters(Executable executable) { * {@link Target_sun_reflect_annotation_AnnotationParser}) */ public byte[] encodeAnnotations(Annotation[] annotations) { + if (annotations.length == 0) { + return null; + } UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); buf.putU2(annotations.length); for (Annotation annotation : annotations) { @@ -908,6 +978,9 @@ public byte[] encodeAnnotations(Annotation[] annotations) { } private byte[] encodeParameterAnnotations(Annotation[][] annotations) { + if (!hasAnnotation(annotations)) { + return null; + } UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); buf.putU1(annotations.length); for (Annotation[] parameterAnnotations : annotations) { @@ -919,6 +992,15 @@ private byte[] encodeParameterAnnotations(Annotation[][] annotations) { return buf.toArray(); } + private static boolean hasAnnotation(Annotation[][] parameterAnnotations) { + for (Annotation[] annotations : parameterAnnotations) { + if (annotations.length != 0) { + return true; + } + } + return false; + } + private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) { buf.putS4(encoders.sourceClasses.getIndex(annotation.annotationType())); AnnotationType type = AnnotationType.getInstance(annotation.annotationType()); @@ -939,7 +1021,7 @@ private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) private byte[] encodeMemberValue(Object value) { if (value == null) { - return new byte[0]; + return null; } UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); Class type = value.getClass(); @@ -1082,6 +1164,9 @@ private static byte tag(Class type) { } private byte[] encodeTypeAnnotations(TypeAnnotation[] annotations) { + if (annotations.length == 0) { + return null; + } UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); buf.putU2(annotations.length); for (TypeAnnotation typeAnnotation : annotations) { @@ -1154,6 +1239,8 @@ private static void encodeTargetInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.T buf.putU1(THROWS); buf.putU2(targetInfo.getCount()); break; + default: + throw GraalError.shouldNotReachHere("Unknown type annotation target: " + targetInfo.getTarget()); } } @@ -1177,7 +1264,7 @@ private static void encodeLocationInfo(UnsafeArrayTypeWriter buf, TypeAnnotation private byte[] encodeReflectParameters(ReflectParameterMetadata[] reflectParameters) { if (reflectParameters == null) { - return new byte[0]; + return null; } UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); encodeArray(buf, reflectParameters, reflectParameter -> encodeReflectParameter(buf, reflectParameter)); @@ -1198,15 +1285,54 @@ private void encodeRecordComponent(UnsafeArrayTypeWriter buf, RecordComponentMet encodeByteArray(buf, encodeTypeAnnotations(recordComponent.typeAnnotations)); } - private byte[] encodeEnclosingMethod(Object[] enclosingMethod) { - assert enclosingMethod.length == 3; - if (enclosingMethod[0] == null && enclosingMethod[1] == null && enclosingMethod[2] == null) { + private byte[] encodeEnclosingMethod(Object[] enclosingMethodInfo) { + if (enclosingMethodInfo == null) { return null; } + assert enclosingMethodInfo.length == 3; UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - encodeType(buf, (HostedType) enclosingMethod[0]); - encodeName(buf, (String) enclosingMethod[1]); - encodeName(buf, (String) enclosingMethod[2]); + encodeType(buf, (HostedType) enclosingMethodInfo[0]); + encodeName(buf, (String) enclosingMethodInfo[1]); + encodeName(buf, (String) enclosingMethodInfo[2]); return buf.toArray(); } + + static final class ReflectionDataAccessors { + private final Method privateGetDeclaredFields; + private final Method privateGetDeclaredMethods; + private final Method privateGetDeclaredConstructors; + + ReflectionDataAccessors() { + privateGetDeclaredFields = ReflectionUtil.lookupMethod(Class.class, "privateGetDeclaredFields", boolean.class); + privateGetDeclaredMethods = ReflectionUtil.lookupMethod(Class.class, "privateGetDeclaredMethods", boolean.class); + privateGetDeclaredConstructors = ReflectionUtil.lookupMethod(Class.class, "privateGetDeclaredConstructors", boolean.class); + } + + Field[] getDeclaredFields(Object obj) { + try { + return (Field[]) privateGetDeclaredFields.invoke(obj, false); + } catch (IllegalAccessException | InvocationTargetException e) { + /* Don't enforce an order if querying fails */ + return new Field[0]; + } + } + + Method[] getDeclaredMethods(Object obj) { + try { + return (Method[]) privateGetDeclaredMethods.invoke(obj, false); + } catch (IllegalAccessException | InvocationTargetException e) { + /* Don't enforce an order if querying fails */ + return new Method[0]; + } + } + + Constructor[] getDeclaredConstructors(Object obj) { + try { + return (Constructor[]) privateGetDeclaredConstructors.invoke(obj, false); + } catch (IllegalAccessException | InvocationTargetException e) { + /* Don't enforce an order if querying fails */ + return new Constructor[0]; + } + } + } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java deleted file mode 100644 index 42a75360b392..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2018, 2018, 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.reflect.hosted; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Parameter; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -import sun.reflect.generics.repository.AbstractRepository; -import sun.reflect.generics.scope.AbstractScope; - -public class ReflectionObjectReplacer implements Function { - - static class Identity { - private final Object wrapped; - - Identity(Object wrapped) { - this.wrapped = wrapped; - } - - @Override - public int hashCode() { - return System.identityHashCode(wrapped); - } - - @Override - public boolean equals(Object obj) { - return ((Identity) obj).wrapped == wrapped; - } - } - - private final Set scanned = ConcurrentHashMap.newKeySet(); - - @Override - public Object apply(Object original) { - if (original instanceof AccessibleObject || original instanceof Parameter || - original instanceof AbstractRepository || original instanceof AbstractScope) { - if (scanned.add(new Identity(original))) { - scan(original); - } - } - return original; - } - - private static void scan(Object original) { - if (original instanceof AbstractScope) { - AbstractScope abstractScope = (AbstractScope) original; - /* - * Lookup a type variable in the scope to trigger creation of - * sun.reflect.generics.scope.AbstractScope.enclosingScope. The looked-up value is not - * important, we just want to trigger creation of lazy internal state. The same eager - * initialization is triggered by - * sun.reflect.generics.repository.MethodRepository.getReturnType() called above, - * however if the AbstractScope is seen first by the heap scanner then a `null` value - * will be snapshotted for the `enclosingScope`. - */ - try { - abstractScope.lookup(""); - } catch (LinkageError | InternalError e) { - /* The lookup calls Class.getEnclosingClass() which may fail. */ - } - } - } -} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java index 7e16b70d7a33..dab8810a4e19 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java @@ -77,9 +77,11 @@ public String toString() { } } + private final ClassLoader classLoader; private final Map proxyCache; - public DynamicProxySupport() { + public DynamicProxySupport(ClassLoader classLoader) { + this.classLoader = classLoader; this.proxyCache = new ConcurrentHashMap<>(); } @@ -95,9 +97,13 @@ public void addProxyClass(Class... interfaces) { proxyCache.computeIfAbsent(key, k -> { Class clazz; try { - clazz = getJdkProxyClass(getCommonClassLoader(intfs), intfs); + clazz = getJdkProxyClass(classLoader, intfs); } catch (Throwable e) { - return e; + try { + clazz = getJdkProxyClass(getCommonClassLoader(intfs), intfs); + } catch (Throwable e2) { + return e; + } } /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java index 4596dad14460..c265f3d901c4 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java @@ -60,7 +60,7 @@ public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; ImageClassLoader imageClassLoader = access.getImageClassLoader(); - DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(); + DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(imageClassLoader.getClassLoader()); ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport); ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("resource configuration", imageClassLoader); ProxyRegistry proxyRegistry = new ProxyRegistry(typeResolver, dynamicProxySupport, imageClassLoader); 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 d3613b092898..2d25bb594e28 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 @@ -47,65 +47,13 @@ import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent; import com.oracle.svm.core.util.ByteArrayReader; +import com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl; /** - * The metadata for methods in the image is split into two arrays: one for the index and the other - * for data. The index contains an array of integers pointing to offsets in the data, and indexed by - * type ID. The data array contains arrays of method metadata, ordered by type ID, such that all - * methods declared by a class are stored consecutively, in the following format: + * This class performs the parsing of reflection metadata at runtime. The encoding formats are + * specified as comments above each parsing method. * - *
- * {
- *     int queriedMethodsCount;
- *     ReflectMethodEncoding[] queriedMethods[queriedMethodsCount];
- *     int hidingMethodsCount;
- *     SimpleMethodEncoding[] hidingMethods[hidingMethodsCount];
- *     int declaringTypeIndex;             // index in frameInfoSourceClasses
- *     int reachableMethodsCount;
- *     SimpleMethodEncoding[] reachableMethods[reachableMethodsCount];
- * } TypeEncoding;
- * 
- * - * The declaring class is encoded before the reachable methods to avoid having to be decoded when - * getting the queried and hiding methods, in which case the declaring class is available as an - * argument and doesn't need to be retrieved from the encoding. - * - * The data for a queried method is stored in the following format: - * - *
- * {
- *     int methodNameIndex;                // index in frameInfoSourceMethodNames ("" for constructors)
- *     int paramCount;
- *     int[] paramTypeIndices[paramCount]; // index in frameInfoSourceClasses
- *     int modifiers;
- *     int returnTypeIndex;                // index in frameInfoSourceClasses (void for constructors)
- *     int exceptionTypeCount;
- *     int[] exceptionTypeIndices[exceptionTypeCount]; // index in frameInfoSourceClasses
- *     int signatureIndex;                 // index in frameInfoSourceMethodNames
- *     int annotationsEncodingLength;
- *     byte[] annotationsEncoding[annotationsEncodingLength];
- *     int parameterAnnotationsEncodingLength;
- *     byte[] parameterAnnotationsEncoding[parameterAnnotationsEncodingLength];
- *     int typeAnnotationsEncodingLength;
- *     byte[] typeAnnotationsEncoding[typeAnnotationsEncodingLength];
- *     boolean hasRealParameterData;
- *     int reflectParameterCount;          // only if hasRealParameterData is true
- *     {
- *         int reflectParameterNameIndex;  // index in frameInfoSourceMethodNames
- *         int reflectParameterModifiers;
- *     } reflectParameters[reflectParameterCount];
- * } ReflectMethodEncoding;
- * 
- * - * The data for a hiding or reachable method is stored as follows: - * - *
- * {
- *     int methodNameIndex;                // index in frameInfoSourceMethodNames ("" for constructors)
- *     int paramCount;
- *     int[] paramTypeIndices[paramCount]; // index in frameInfoSourceClasses
- * } SimpleMethodEncoding;
- * 
+ * See {@link ReflectionMetadataEncoderImpl} for details about the emission of the metadata. */ public class ReflectionMetadataDecoderImpl implements ReflectionMetadataDecoder { public static final int NO_METHOD_METADATA = -1; @@ -121,6 +69,13 @@ static byte[] getEncoding() { return ImageSingletons.lookup(ReflectionMetadataEncoding.class).getEncoding(); } + /** + * Fields encoding. + * + *
+     * FieldMetadata[] fields
+     * 
+ */ @Override public Field[] parseFields(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); @@ -128,6 +83,13 @@ public Field[] parseFields(DynamicHub declaringType, int index, boolean publicOn return decodeArray(reader, Field.class, (i) -> decodeField(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); } + /** + * Methods encoding. + * + *
+     * MethodMetadata[] methods
+     * 
+ */ @Override public Method[] parseMethods(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); @@ -135,6 +97,13 @@ public Method[] parseMethods(DynamicHub declaringType, int index, boolean public return decodeArray(reader, Method.class, (i) -> decodeMethod(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); } + /** + * Constructors encoding. + * + *
+     * ConstructorMetadata[] constructors
+     * 
+ */ @Override public Constructor[] parseConstructors(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); @@ -142,6 +111,13 @@ public Constructor[] parseConstructors(DynamicHub declaringType, int index, b return decodeArray(reader, Constructor.class, (i) -> decodeConstructor(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); } + /** + * Inner classes encoding. + * + *
+     * ClassIndex[] innerClasses
+     * 
+ */ @Override public Class[] parseClasses(int index) { UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); @@ -149,6 +125,13 @@ public Class[] parseClasses(int index) { return decodeArray(reader, Class.class, (i) -> decodeType(reader, codeInfo)); } + /** + * Record components encoding. + * + *
+     * RecordComponentMetadata[] recordComponents
+     * 
+ */ @Override public Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, int index) { UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); @@ -156,6 +139,13 @@ public Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicH return decodeArray(reader, Target_java_lang_reflect_RecordComponent.class, (i) -> decodeRecordComponent(reader, codeInfo, DynamicHub.toClass(declaringType))); } + /** + * Parameters encoding for executables. + * + *
+     * ParameterMetadata[] parameters
+     * 
+ */ @Override public Parameter[] parseReflectParameters(Executable executable, byte[] encoding) { UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); @@ -163,6 +153,17 @@ public Parameter[] parseReflectParameters(Executable executable, byte[] encoding return decodeArray(reader, Parameter.class, (i) -> decodeReflectParameter(reader, codeInfo, executable, i)); } + /** + * Class enclosing method information. {@link Class#getEnclosingMethod()} + * + *
+     * EnclosingMethodInfo {
+     *     ClassIndex  declaringClass
+     *     StringIndex name
+     *     StringIndex descriptor
+     * }
+     * 
+ */ @Override public Object[] parseEnclosingMethod(int index) { UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); @@ -189,6 +190,42 @@ public long getMetadataByteLength() { return ImageSingletons.lookup(ReflectionMetadataEncoding.class).getEncoding().length; } + /** + * + * Complete field encoding. + * + *
+     * CompleteFieldMetadata : FieldMetadata {
+     *     int         modifiers               (including COMPLETE flag)
+     *     StringIndex name
+     *     ClassIndex  type
+     *     boolean     trustedFinal            (only on JDK 17 and later)
+     *     StringIndex signature
+     *     byte[]      annotationsEncoding
+     *     byte[]      typeAnnotationsEncoding
+     *     int         offset
+     *     StringIndex deletedReason
+     * }
+     * 
+ *

+ * Heap field encoding. + * + *

+     * HeapFieldMetadata : FieldMetadata {
+     *     int         modifiers   (including IN_HEAP flag)
+     *     ObjectIndex fieldObject
+     * }
+     * 
+ *

+ * Partial field encoding. + * + *

+     * PartialFieldMetadata : FieldMetadata {
+     *     int         modifiers
+     *     StringIndex name
+     * }
+     * 
+ */ 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; @@ -238,10 +275,83 @@ private static Field decodeField(UnsafeArrayTypeReader buf, CodeInfo info, Class return SubstrateUtil.cast(field, Field.class); } + /** + * Complete method encoding. + * + *
+     * CompleteMethodMetadata : MethodMetadata {
+     *     int          modifiers                    (including COMPLETE flag)
+     *     StringIndex  name
+     *     ClassIndex[] parameterTypes
+     *     ClassIndex   returnType
+     *     StringIndex  signature
+     *     byte[]       annotationsEncoding
+     *     byte[]       parameterAnnotationsEncoding
+     *     byte[]       annotationDefaultEncoding    (annotation methods only)
+     *     byte[]       typeAnnotationsEncoding
+     *     byte[]       reflectParametersEncoding    ({@link #decodeReflectParameter(UnsafeArrayTypeReader, CodeInfo, Executable, int)})
+     *     ObjectIndex  accessor                     (null if registered as queried only)
+     * }
+     * 
+ * + * Heap method encoding. + * + *
+     * HeapMethodMetadata : MethodMetadata {
+     *     int         modifiers    (including IN_HEAP flag)
+     *     ObjectIndex methodObject
+     * }
+     * 
+ * + * Partial method encoding. + * + *
+     * PartialMethodMetadata : MethodMetadata {
+     *     int          modifiers
+     *     StringIndex  name
+     *     ClassIndex[] parameterTypes
+     *     ClassIndex   returnType
+     * }
+     * 
+ */ private static Method decodeMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { return (Method) decodeExecutable(buf, info, declaringClass, publicOnly, reflectOnly, true); } + /** + * Complete constructor encoding. + * + *
+     * CompleteConstructorMetadata : ConstructorMetadata {
+     *     int          modifiers                    (including COMPLETE flag)
+     *     ClassIndex[] parameterTypes
+     *     StringIndex  signature
+     *     byte[]       annotationsEncoding
+     *     byte[]       parameterAnnotationsEncoding
+     *     byte[]       typeAnnotationsEncoding
+     *     byte[]       reflectParametersEncoding    ({@link #parseReflectParameters(Executable, byte[])})
+     *     ObjectIndex  accessor                     (null if registered as queried only)
+     * }
+     * 
+ * + * Heap constructor encoding. + * + *
+     * HeapConstructorMetadata : ConstructorMetadata {
+     *     int         modifiers         (including IN_HEAP flag)
+     *     ObjectIndex constructorObject
+     * }
+     * 
+ * + * Partial constructor encoding. + * + *
+     * PartialConstructorMetadata : ConstructorMetadata {
+     *     int          modifiers
+     *     ClassIndex[] parameterTypes
+     * }
+     * 
+ */ private static Constructor decodeConstructor(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { return (Constructor) decodeExecutable(buf, info, declaringClass, publicOnly, reflectOnly, false); } @@ -258,11 +368,12 @@ private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo i } 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 && hiding ? decodeType(buf, info) : null; + Class returnType = isMethod ? decodeType(buf, info) : null; if (!complete) { if (reflectOnly != hiding) { /* @@ -282,8 +393,6 @@ private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo i return SubstrateUtil.cast(constructor, Executable.class); } } - assert !hiding; - returnType = isMethod ? decodeType(buf, info) : null; Class[] exceptionTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf, info)); String signature = decodeName(buf, info); byte[] annotations = decodeByteArray(buf); @@ -313,6 +422,20 @@ private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo i return SubstrateUtil.cast(executable, Executable.class); } + /** + * Record component encoding. + * + *
+     * RecordComponentMetadata {
+     *     StringIndex name
+     *     ClassIndex  type
+     *     StringIndex signature
+     *     ObjectIndex accessor
+     *     byte[]      annotations
+     *     byte[]      typeAnnotations
+     * }
+     * 
+ */ private static Target_java_lang_reflect_RecordComponent decodeRecordComponent(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { String name = decodeName(buf, info); Class type = decodeType(buf, info); @@ -332,6 +455,17 @@ private static Target_java_lang_reflect_RecordComponent decodeRecordComponent(Un return recordComponent; } + /** + * Parameter encoding for executables. + * + *
+     * ParameterMetadata {
+     *     StringIndex name
+     *     int         modifiers
+     * }
+     * 
+ */ + private static Parameter decodeReflectParameter(UnsafeArrayTypeReader buf, CodeInfo info, Executable executable, int i) { String name = decodeName(buf, info); int modifiers = buf.getUVInt(); @@ -341,6 +475,9 @@ private static Parameter decodeReflectParameter(UnsafeArrayTypeReader buf, CodeI return SubstrateUtil.cast(parameter, Parameter.class); } + /** + * Types are encoded as indices in the frame info source classes array. + */ private static Class decodeType(UnsafeArrayTypeReader buf, CodeInfo info) { int classIndex = buf.getSVInt(); if (classIndex == NO_METHOD_METADATA) { @@ -349,6 +486,9 @@ private static Class decodeType(UnsafeArrayTypeReader buf, CodeInfo info) { return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), classIndex); } + /** + * Names are encoded as indices in the frame info source method names array. + */ private static String decodeName(UnsafeArrayTypeReader buf, CodeInfo info) { int nameIndex = buf.getSVInt(); String name = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), nameIndex); @@ -356,6 +496,10 @@ private static String decodeName(UnsafeArrayTypeReader buf, CodeInfo info) { return name == null ? null : name.intern(); } + /** + * Objects (method accessors and reflection objects in the heap) are encoded as indices in the + * frame info object constants array. + */ private static Object decodeObject(UnsafeArrayTypeReader buf, CodeInfo info) { int objectIndex = buf.getSVInt(); if (objectIndex == NULL_OBJECT) { @@ -364,6 +508,9 @@ private static Object decodeObject(UnsafeArrayTypeReader buf, CodeInfo info) { return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoObjectConstants(info), objectIndex); } + /** + * Arrays are encoded by their length followed by the elements encoded one after the other. + */ @SuppressWarnings("unchecked") private static T[] decodeArray(UnsafeArrayTypeReader buf, Class elementType, Function elementDecoder) { int length = buf.getUVInt(); @@ -380,7 +527,7 @@ private static T[] decodeArray(UnsafeArrayTypeReader buf, Class elementTy private static byte[] decodeByteArray(UnsafeArrayTypeReader buf) { int length = buf.getUVInt(); - if (length == 0) { + if (length == NO_DATA) { return null; } byte[] result = new byte[length]; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationInvocationHandler.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationInvocationHandler.java new file mode 100644 index 000000000000..25713cd62281 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationInvocationHandler.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2022, 2022, 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.reflect.target; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; + +import com.oracle.svm.core.annotate.Alias; +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.jdk.JDK17OrLater; + +@TargetClass(className = "sun.reflect.annotation.AnnotationInvocationHandler") +public final class Target_sun_reflect_annotation_AnnotationInvocationHandler { + + @Alias + static native String toSourceString(Class clazz); + + @Alias + static native String toSourceString(float clazz); + + @Alias + static native String toSourceString(double clazz); + + @Alias + @TargetElement(onlyWith = JDK17OrLater.class) + static native String toSourceString(byte clazz); + + @Alias + static native String toSourceString(char clazz); + + @Alias + static native String toSourceString(long clazz); + + @Alias + static native String toSourceString(String clazz); + + /* Prevent streams from ending up in simple images */ + @Substitute + private static String memberValueToString(Object value) { + Class type = value.getClass(); + if (!type.isArray()) { + // primitive value, string, class, enum const, or annotation + if (type == Class.class) { + return toSourceString((Class) value); + } else if (type == String.class) { + return toSourceString((String) value); + } else if (type == Character.class) { + return toSourceString((char) value); + } else if (type == Double.class) { + return toSourceString((double) value); + } else if (type == Float.class) { + return toSourceString((float) value); + } else if (type == Long.class) { + return toSourceString((long) value); + } else if (JavaVersionUtil.JAVA_SPEC >= 17 && type == Byte.class) { + return toSourceString((byte) value); + } else { + return value.toString(); + } + } else { + List stringList; + if (type == byte[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((byte[]) value); + } else if (type == char[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((char[]) value); + } else if (type == double[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((double[]) value); + } else if (type == float[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((float[]) value); + } else if (type == int[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((int[]) value); + } else if (type == long[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((long[]) value); + } else if (type == short[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((short[]) value); + } else if (type == boolean[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((boolean[]) value); + } else if (type == Class[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((Class[]) value); + } else if (type == String[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((String[]) value); + } else { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((Object[]) value); + } + + return Util_sun_reflect_annotation_AnnotationInvocationHandler.stringListToString(stringList); + } + } +} + +class Util_sun_reflect_annotation_AnnotationInvocationHandler { + static List convert(boolean[] values) { + List list = new ArrayList<>(values.length); + for (boolean b : values) { + list.add(Boolean.toString(b)); + } + return list; + } + + static List convert(byte[] values) { + List list = new ArrayList<>(values.length); + for (byte b : values) { + if (JavaVersionUtil.JAVA_SPEC >= 17) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(b)); + } else { + list.add(Byte.toString(b)); + } + } + return list; + } + + static List convert(short[] values) { + List list = new ArrayList<>(values.length); + for (short s : values) { + list.add(Short.toString(s)); + } + return list; + } + + static List convert(char[] values) { + List list = new ArrayList<>(values.length); + for (char c : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(c)); + } + return list; + } + + static List convert(int[] values) { + List list = new ArrayList<>(values.length); + for (int i : values) { + list.add(String.valueOf(i)); + } + return list; + } + + static List convert(long[] values) { + List list = new ArrayList<>(values.length); + for (long l : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(l)); + } + return list; + } + + static List convert(float[] values) { + List list = new ArrayList<>(values.length); + for (float f : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(f)); + } + return list; + } + + static List convert(double[] values) { + List list = new ArrayList<>(values.length); + for (double d : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(d)); + } + return list; + } + + static List convert(Class[] values) { + List list = new ArrayList<>(values.length); + for (Class clazz : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(clazz)); + } + return list; + } + + static List convert(String[] values) { + List list = new ArrayList<>(values.length); + for (String string : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(string)); + } + return list; + } + + static List convert(Object[] values) { + List list = new ArrayList<>(values.length); + for (Object obj : values) { + list.add(Objects.toString(obj)); + } + return list; + } + + static String stringListToString(List list) { + StringBuilder string = new StringBuilder(); + string.append("{"); + for (int i = 0; i < list.size(); ++i) { + string.append(list.get(i)); + if (i < list.size() - 1) { + string.append(", "); + } + } + string.append("}"); + return string.toString(); + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java deleted file mode 100644 index 7ac4bc2274b3..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2021, 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.reflect.target; - -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.AnnotatedType; -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.config.ConfigurationValues; -import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; - -import sun.reflect.annotation.TypeAnnotation; -import sun.reflect.annotation.TypeAnnotationParser; - -@TargetClass(TypeAnnotationParser.class) -public final class Target_sun_reflect_annotation_TypeAnnotationParser { - @Alias - public static native AnnotatedType buildAnnotatedType(byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool cp, - AnnotatedElement decl, - Class container, - Type type, - TypeAnnotation.TypeAnnotationTarget filter); - - @Alias - public static native AnnotatedType[] buildAnnotatedTypes(byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool cp, - AnnotatedElement decl, - Class container, - Type[] types, - TypeAnnotation.TypeAnnotationTarget filter); - - @Alias - // Checkstyle: stop - private static TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY; - // Checkstyle: resume - - @Substitute - private static TypeAnnotation[] parseTypeAnnotations(byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool cp, - AnnotatedElement baseDecl, - Class container) { - if (rawAnnotations == null) { - return EMPTY_TYPE_ANNOTATION_ARRAY; - } - - ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); - buf.order(ConfigurationValues.getTarget().arch.getByteOrder()); - int annotationCount = buf.getShort() & 0xFFFF; - List typeAnnotations = new ArrayList<>(annotationCount); - - // Parse each TypeAnnotation - for (int i = 0; i < annotationCount; i++) { - TypeAnnotation ta = parseTypeAnnotation(buf, cp, baseDecl, container); - if (ta != null) { - typeAnnotations.add(ta); - } - } - - return typeAnnotations.toArray(EMPTY_TYPE_ANNOTATION_ARRAY); - } - - @Alias // - private static native TypeAnnotation parseTypeAnnotation(ByteBuffer buf, - Target_jdk_internal_reflect_ConstantPool cp, - AnnotatedElement baseDecl, - Class container); -} diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index f749eb14ebcc..b5ccf33de4b3 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -784,6 +784,9 @@ public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField origina final class Target_java_lang_ProcessBuilder { } +/* + * Ensure ProcessBuilder is not reachable through the enclosing class of Redirect. + */ @Delete @TargetClass(className = "java.lang.ProcessBuilder", innerClass = "Redirect", onlyWith = {TruffleBaseFeature.IsEnabled.class, TruffleBaseFeature.IsCreateProcessDisabled.class})