From 13f3c60c25b1d133ba197a8276fac166fc0ac8f3 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Mon, 17 Jan 2022 16:27:53 +0100 Subject: [PATCH] WIP --- .../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 | 733 +++++++++--------- .../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 | 79 +- .../UnsafeAutomaticSubstitutionProcessor.java | 4 +- .../svm/reflect/hosted/MethodMetadata.java | 89 --- .../hosted/MethodMetadataEncoderImpl.java | 730 +++++++++++++---- .../reflect/hosted/ReflectionDataBuilder.java | 565 +++++--------- .../svm/reflect/hosted/ReflectionFeature.java | 6 +- .../reflect/hosted/ReflectionMetadata.java | 222 ++++++ .../hosted/ReflectionObjectReplacer.java | 116 +-- .../target/MethodMetadataDecoderImpl.java | 272 ++++--- ...et_java_lang_reflect_AccessibleObject.java | 28 +- .../Target_java_lang_reflect_Constructor.java | 104 +-- .../Target_java_lang_reflect_Executable.java | 285 +------ .../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 | 104 +-- ...flect_annotation_TypeAnnotationParser.java | 1 + 37 files changed, 1979 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 fbe7c9ce94a40..bcb42968c32af 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 47f1d3d4afa31..64b961a5322a4 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 7f5f5d0e8e532..8d8bd04819886 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 875eb3c724166..1d9a98e7b40fc 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 d1d678f55f084..9c7b3ca569829 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 @@ -388,7 +388,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 1ee936f59eb95..b1511887ed893 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 @@ -717,10 +717,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 2b91ee63e9aaa..f6517ab6b7059 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 cfb56b00a6921..974710ed6cc31 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,15 @@ import java.lang.reflect.TypeVariable; import java.net.URL; import java.security.ProtectionDomain; +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 +61,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 +71,24 @@ 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.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 +265,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 +281,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 +299,39 @@ 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; + + private Object[] 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 +411,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 +421,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 +454,56 @@ 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; + } + /** Executed at runtime. */ private static Object initEnumConstantsAtRuntime(Method values) { try { @@ -704,20 +757,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(); @@ -802,86 +849,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 @@ -889,11 +870,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 @@ -920,11 +899,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. @@ -932,95 +909,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(); @@ -1030,61 +928,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(); @@ -1104,32 +961,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) @@ -1138,30 +983,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") @@ -1201,49 +1033,27 @@ private static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactor @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 { @@ -1307,11 +1117,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(); @@ -1364,10 +1172,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(); @@ -1474,11 +1280,24 @@ public Optional describeConstable() { @Delete static native Class getPrimitiveClass(String name); - @Delete - private native Object[] getEnclosingMethod0(); + @Substitute + private Object[] getEnclosingMethod0() { + if (enclosingMethodInfo != null) { + PredefinedClassesSupport.throwIfUnresolvable(toClass((DynamicHub) enclosingMethodInfo[0]), getClassLoader0()); + } + return enclosingMethodInfo; + } - @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) { @@ -1488,29 +1307,45 @@ 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); + } - @Delete - private native Method[] getDeclaredMethods0(boolean publicOnly); + @Substitute + private Method[] getDeclaredMethods0(boolean publicOnly) { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseMethods(this, methodsEncoding, publicOnly); + } - @Delete - private native Constructor[] getDeclaredConstructors0(boolean publicOnly); + @Substitute + private Constructor[] getDeclaredConstructors0(boolean publicOnly) { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseConstructors(this, constructorsEncoding, publicOnly); + } - @Delete - private native Class[] getDeclaredClasses0(); + @Substitute + private Class[] getDeclaredClasses0() { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseClasses(classesEncoding); + } @Delete private static native boolean desiredAssertionStatus0(Class clazz); @@ -1523,6 +1358,115 @@ 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; + } + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseClasses(permittedSubclassesEncoding); + } + + @Substitute @InjectAccessors(CachedConstructorAccessors.class) // + private Constructor cachedConstructor; + + @Substitute @InjectAccessors(NewInstanceCallerCacheAccessors.class) // + @TargetElement(onlyWith = JDK11OrEarlier.class) // + private Class newInstanceCallerCache; + + @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 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); + } + } } /** @@ -1592,6 +1536,49 @@ public static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactory } } -@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 { +} + +@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 42594d0c45a14..ef1544e34b35b 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 45ab0cc897867..16687d21aae1d 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 3da4274f6beb3..b642e03196537 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 4487a6dab0202..23930ef552fb9 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,24 @@ */ 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(); - - long getMetadataByteLength(); - - class MethodDescriptor { - private final Class declaringClass; - private final String name; - private final Class[] parameterTypes; + Field[] parseFields(DynamicHub declaringType, byte[] encoding, boolean publicOnly); - public MethodDescriptor(Class declaringClass, String name, Class[] parameterTypes) { - this.declaringClass = declaringClass; - this.name = name; - this.parameterTypes = parameterTypes; - } + Method[] parseMethods(DynamicHub declaringType, byte[] encoding, boolean publicOnly); - public Class getDeclaringClass() { - return declaringClass; - } + Constructor[] parseConstructors(DynamicHub declaringType, byte[] encoding, boolean publicOnly); - public String getName() { - return name; - } + Class[] parseClasses(byte[] encoding); - public Class[] getParameterTypes() { - return parameterTypes; - } - } + Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, byte[] encoding); + Parameter[] parseReflectParameters(Executable executable, byte[] encoding); } 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 f5945a6e29892..2e31c3e0ed628 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 722282ca1dcb4..b2d41f4962880 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 47411c8fd4439..ec4de22a3e5d0 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]); - try { - access.registerAsCompiled(DynamicHub.class.getDeclaredMethod("newInstanceInstantiationError", Object.class)); - access.registerAsCompiled(DynamicHub.class.getDeclaredMethod("newInstanceReachableError", Object.class)); - } 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 4e606a600861f..0f8c6bd32dd5d 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 @@ -67,7 +67,6 @@ import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.code.CodeInfoTable; 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.code.CompileQueue.CompileTask; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; @@ -102,6 +101,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; @@ -199,6 +199,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; @@ -486,7 +490,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 165b453ea75e9..ff4c2f3c98f62 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 96d6211b62985..9ce497e1d0e35 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 @@ -580,20 +580,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() && !GraalUnsafeAccess.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 81431b5a7d82e..a71281913047e 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; @@ -136,4 +138,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 fe31a58fe5a32..60e3b90e0fab4 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); @@ -245,11 +282,19 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code parameterTypes[i] = imageHeap.getUniverse().lookup(analysisParameterTypes[i]); } methodMetadataEncoder.addHidingMethodMetadata(declaringType, name, parameterTypes); + 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 +600,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); + 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 3cada6d5aee39..6f1cd9289eb19 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 da61ae7c6f935..0000000000000 --- 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 276a118393d9d..609df6e70f63f 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,12 @@ */ 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.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; @@ -39,30 +44,47 @@ 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.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; @@ -84,7 +106,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 +119,143 @@ 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 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(); + 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 */ + 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, recordComponents, permittedSubtypes, annotations, typeAnnotations)); + } + + 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; } - if (SubstrateOptions.IncludeMethodData.getValue()) { - this.reachableMethodData = new HashMap<>(); + 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(); } } - private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); - private static final Method hasRealParameterData = ReflectionUtil.lookupMethod(Executable.class, "hasRealParameterData"); + @Override + 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 addReflectionMethodMetadata(MetaAccessProvider metaAccess, HostedMethod hostedMethod, Executable reflectMethod) { + 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 +264,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(); + } + } + + @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 void registerAnnotationValues(Annotation... annotations) { + 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(); + } + } + + 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(); + } + } + + 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 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") @@ -224,27 +485,42 @@ public void addHidingMethodMetadata(HostedType declaringType, String name, Hoste } sortedTypes.add(declaringType); - hidingMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(new MethodMetadata(declaringType, name, parameterTypes)); + registerMethod(declaringType, new MethodMetadata(declaringType, name, parameterTypes)); + } + + @Override + public void addReachableFieldMetadata(HostedField field) { + HostedType declaringType = field.getDeclaringClass(); + String name = field.getName(); + HostedType type = field.getType(); + + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + encoders.sourceClasses.addObject(type.getJavaClass()); + + registerField(declaringType, new FieldMetadata(declaringType, name, type)); } @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); /* 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(declaringType, name, parameterTypes)); + } else { + registerConstructor(declaringType, new ConstructorMetadata(declaringType, parameterTypes)); + } } private static HostedType[] getParameterTypes(HostedMethod method) { @@ -264,96 +540,212 @@ 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(); + } + } + + 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(); + } + } + + private static Class getRecordComponentType(Object recordComponent) { + try { + return (Class) getRecordComponentType.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); + } + } + + private static String getRecordComponentSignature(Object recordComponent) { + try { + return (String) getRecordComponentSignature.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); + } + } + + private static Method getRecordComponentAccessor(Object recordComponent) { + try { + return (Method) getRecordComponentAccessor.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); + } } /** * 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 (JavaVersionUtil.JAVA_SPEC >= 17) { + metadataByteLength += encodeAndInstallCollection(classMetadata.recordComponents, this::encodeRecordComponent, hub::setRecordComponentsEncoding, true); + metadataByteLength += encodeAndInstallCollection(classMetadata.permittedSubclasses, this::encodeType, hub::setPermittedSubclassesEncoding, true); } - assert nextTypeId == typeID; + 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)); + metadataByteLength += encodeAndInstall(((ExecutableMetadata) metadata).reflectParameters, this::encodeReflectParameters, + (array) -> reflectParametersEncodings.put((Executable) heapObject, array)); + if (metadata instanceof MethodMetadata && ((Method) SubstrateObjectConstant.asObject(metadata.heapObject)).getDeclaringClass().isAnnotation()) { + metadataByteLength += encodeAndInstall(((MethodMetadata) metadata).annotationDefault, this::encodeMemberValue, + (array) -> annotationDefaultEncodings.put((Method) heapObject, array)); + } + } + } + /* Enable field recomputers in reflection objects to see the computed values */ + ImageSingletons.add(MethodMetadataEncoder.class, this); + ProgressReporter.singleton().setMetadataByteLength(metadataByteLength); + } - long index = dataEncodingBuffer.getBytesWritten(); - indexEncodingBuffer.putS4(index); - nextTypeId++; + 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); + } - 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); - }); + 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); + } - Set hidingMethods = hidingMethodData.getOrDefault(declaringType, Collections.emptySet()); - encodeArray(dataEncodingBuffer, hidingMethods.toArray(new MethodMetadata[0]), hidingMethod -> encodeSimpleMethod(dataEncodingBuffer, hidingMethod)); - } + 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; + } - 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); + private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) { + assert (field.modifiers & COMPLETE_FLAG_MASK) == 0 && (field.modifiers & IN_HEAP_FLAG_MASK) == 0; + buf.putUV(field.modifiers | (field.complete ? COMPLETE_FLAG_MASK : 0) | (field.heapObject != null ? IN_HEAP_FLAG_MASK : 0)); + if (field.heapObject != null) { + encodeObject(buf, field.heapObject); + } else { + encodeName(buf, field.name); + encodeType(buf, field.type); + if (field.complete) { + 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); } } - for (; nextTypeId <= ImageSingletons.lookup(DynamicHubSupport.class).getMaxTypeId(); nextTypeId++) { - indexEncodingBuffer.putS4(MethodMetadataDecoderImpl.NO_METHOD_METADATA); - } - - methodDataEncoding = new byte[TypeConversion.asS4(dataEncodingBuffer.getBytesWritten())]; - dataEncodingBuffer.toArray(methodDataEncoding); - methodDataIndexEncoding = new byte[TypeConversion.asS4(indexEncodingBuffer.getBytesWritten())]; - indexEncodingBuffer.toArray(methodDataIndexEncoding); } - 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 void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata executable) { + assert (executable.modifiers & COMPLETE_FLAG_MASK) == 0 && (executable.modifiers & IN_HEAP_FLAG_MASK) == 0; + buf.putUV(executable.modifiers | (executable.complete ? COMPLETE_FLAG_MASK : 0) | (executable.heapObject != null ? IN_HEAP_FLAG_MASK : 0)); + if (executable.heapObject != null) { + encodeObject(buf, executable.heapObject); + } else { + boolean isMethod = executable instanceof MethodMetadata; + if (isMethod) { + encodeName(buf, ((MethodMetadata) executable).name); + } + encodeArray(buf, executable.parameterTypes, parameterType -> encodeType(buf, parameterType)); + if (executable.complete) { + if (isMethod) { + encodeType(buf, ((MethodMetadata) executable).returnType); + } + 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 encodeSimpleMethod(UnsafeArrayTypeWriter buf, MethodMetadata method) { - encodeName(buf, method.name); - encodeArray(buf, method.parameterTypes, parameterType -> encodeType(buf, parameterType)); - } - private void encodeType(UnsafeArrayTypeWriter buf, HostedType type) { buf.putSV(encoders.sourceClasses.getIndex(type.getJavaClass())); } @@ -362,7 +754,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,22 +778,53 @@ 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(Field field) { + try { + return (String) getFieldSignature.invoke(field); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); + } + } + + 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(); + } + } - private static String getSignature(Executable method) { + private static Parameter[] getRawParameters(Executable executable) { try { - return (String) (method instanceof Method ? getMethodSignature.invoke(method) : getConstructorSignature.invoke(method)); + return (Parameter[]) getExecutableParameters.invoke(executable); } catch (IllegalAccessException | InvocationTargetException e) { throw GraalError.shouldNotReachHere(); } } + public byte[] encodeClasses(HostedType[] classes) { + if (classes == null) { + return new byte[0]; + } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + encodeArray(buf, classes, clazz -> encodeType(buf, clazz)); + return buf.toArray(); + } + /** * The following methods encode annotations attached to a method or parameter in a format based * on the one used internally by the JDK ({@link sun.reflect.annotation.AnnotationParser}). The @@ -404,7 +842,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 +851,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); @@ -440,6 +878,15 @@ private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) } } + private byte[] encodeMemberValue(Object value) { + if (value == null) { + return new byte[0]; + } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); + encodeValue(buf, value, value.getClass()); + return buf.toArray(); + } + private void encodeValue(UnsafeArrayTypeWriter buf, Object value, Class type) { buf.putU1(tag(type)); if (type.isAnnotation()) { @@ -567,7 +1014,7 @@ private static byte tag(Class type) { } 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); @@ -659,4 +1106,27 @@ private static void encodeLocationInfo(UnsafeArrayTypeWriter buf, TypeAnnotation throw GraalError.shouldNotReachHere(); } } + + 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)); + } } 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 76a494f4596a6..d51ce88a7e616 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,10 @@ */ 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; @@ -37,17 +40,12 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; 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; @@ -57,93 +55,53 @@ 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.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.annotation.AnnotationSubstitutionType; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; -import com.oracle.svm.util.ReflectionUtil; 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.TypeAnnotation; -import sun.reflect.annotation.TypeAnnotationParser; 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 +121,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 +159,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); @@ -283,25 +239,51 @@ 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); - } + 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); } - queriedMethods = newQueriedMethods; - for (Executable method : reflectionMethods) { - if (!SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { - AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(method); + } + for (Executable method : reflectionMethods.keySet()) { + if (!registeredMethods.contains(method) && !SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { + AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(method); + registerTypesForMethod(access, analysisMethod, method); + registerHidingSubTypeMethods(access, analysisMethod, analysisMethod.getDeclaringClass()); + if (reflectionMethods.get(method) == ExecutableAccessibility.Accessed) { + methodAccessors.putIfAbsent(method, ImageSingletons.lookup(ReflectionFeature.class).getOrCreateAccessor(method)); + } + registeredMethods.add(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 +292,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 +336,59 @@ 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) { + makeTypeReachable(access, clazz.getGenericSuperclass()); + for (Type genericInterface : clazz.getGenericInterfaces()) { + makeTypeReachable(access, genericInterface); + } + 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); + + if (!analysisField.isUnsafeAccessed()) { + analysisField.registerAsAccessed(); + analysisField.registerAsUnsafeAccessed(); + } + + makeAnalysisTypeReachable(access, analysisField.getDeclaringClass()); + makeAnalysisTypeReachable(access, analysisField.getType()); + makeTypeReachable(access, reflectField.getGenericType()); - private static void registerTypesForQueriedMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod, Executable reflectMethod) { + /* + * 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 +408,25 @@ 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()); + makeAnalysisTypeReachable(access, analysisField.getType()); + } + private static void registerTypesForReachableMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod) { makeAnalysisTypeReachable(access, analysisMethod.getDeclaringClass()); for (JavaType paramType : analysisMethod.toParameterTypes()) { @@ -403,14 +434,11 @@ 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)) { + private void makeTypeReachable(DuringAnalysisAccessImpl access, Type type) { + if (type == null || processedTypes.contains(type)) { return; } - 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,9 +474,27 @@ 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 @@ -514,64 +560,14 @@ 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); + if (clazz.getEnclosingClass() != null) { + innerClasses.computeIfAbsent(access.getMetaAccess().lookupJavaType(clazz.getEnclosingClass()).getJavaClass(), (enclosingType) -> ConcurrentHashMap.newKeySet()).add(clazz); + } + } if (type.isAnnotation()) { /* @@ -579,24 +575,21 @@ 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 */ } } - private static T query(Callable callable, List errors) { - try { - return callable.call(); - } catch (TypeNotPresentException | LinkageError e) { - errors.add(e); - } catch (Exception e) { - throw VMError.shouldNotReachHere(e); - } - return null; - } - private Object[] buildRecordComponents(Class clazz, DuringAnalysisAccessImpl access) { RecordSupport support = RecordSupport.singleton(); if (!support.isRecord(clazz)) { @@ -615,22 +608,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; - } - } - - private static void reportLinkingErrors(Class clazz, List errors) { - if (errors.isEmpty()) { - return; + for (Method method : allMethods) { + if (!reflectionMethods.containsKey(method) || SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { + return null; + } } - String messages = errors.stream().map(e -> e.getClass().getTypeName() + ": " + e.getMessage()) - .distinct().collect(Collectors.joining(", ")); - System.out.println("Warning: Could not register complete reflection metadata for " + clazz.getTypeName() + ". Reason(s): " + messages); + return support.getRecordComponents(clazz); } protected void afterAnalysis() { @@ -645,18 +628,6 @@ 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) { Method enclosingMethod; Constructor enclosingConstructor; @@ -689,89 +660,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 +722,16 @@ public int getReflectionClassesCount() { @Override public int getReflectionMethodsCount() { - return reflectionMethods.size(); + return registeredMethods.size(); } @Override public int getReflectionFieldsCount() { - return reflectionFields.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); - } - } + return registeredFields.size(); + } - 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 19005af6a4eed..68799ef6dd2fe 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 0000000000000..1b792484eae78 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java @@ -0,0 +1,222 @@ +/* + * 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 RecordComponentMetadata[] recordComponents; + final HostedType[] permittedSubclasses; + + ClassMetadata(HostedType[] classes, RecordComponentMetadata[] recordComponents, HostedType[] permittedSubclasses, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + super(annotations, typeAnnotations); + this.classes = classes; + 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, HostedType type) { + this(false, null, declaringType, name, type, 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 String name; + final HostedType returnType; + final Object annotationDefault; + + MethodMetadata(boolean complete, 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.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, 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, heapObject, null, null, null, 0, null, null, null, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, null); + } + + MethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes) { + this(false, null, declaringClass, name, parameterTypes, 0, null, null, null, null, null, null, null, null, null); + } + } + + static class ConstructorMetadata extends ExecutableMetadata { + + ConstructorMetadata(boolean complete, JavaConstant heapObject, HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, + 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) { + this(false, null, declaringClass, parameterTypes, 0, 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 7102fdad5c201..42a75360b3921 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/target/MethodMetadataDecoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java index d140c440f1e2b..79427ad7db02a 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,176 @@ */ 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; - @Fold - static boolean hasQueriedMethods() { - return !ImageSingletons.lookup(RuntimeReflectionSupport.class).getQueriedOnlyMethods().isEmpty(); + @Override + public Field[] parseFields(DynamicHub declaringType, byte[] encoding, boolean publicOnly) { + 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)); } - /** - * 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) { + 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)); + } - 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) { + 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)); } @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 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)); + } + + private static Field decodeField(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly) { + 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; } - return allMethods.toArray(new MethodDescriptor[0]); + boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; + modifiers &= ~COMPLETE_FLAG_MASK; + + String name = decodeName(buf, info); + Class type = decodeType(buf, info); + if (!complete) { + return null; + } + 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); } - @Override - public long getMetadataByteLength() { - MethodMetadataEncoding encoding = ImageSingletons.lookup(MethodMetadataEncoding.class); - return encoding.getMethodsEncoding().length + encoding.getIndexEncoding().length; + private static Method decodeMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly) { + return (Method) decodeExecutable(buf, info, declaringClass, publicOnly, true); } - 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(); + private static Constructor decodeConstructor(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly) { + return (Constructor) decodeExecutable(buf, info, declaringClass, publicOnly, false); } - private static Executable decodeReflectionMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { - String name = decodeName(buf, info); - Class[] parameterTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); + private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, 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; + modifiers &= ~COMPLETE_FLAG_MASK; + + String name = isMethod ? decodeName(buf, info) : null; + Class[] parameterTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf, info)); + if (!complete) { + return null; + } + Class 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.length > 0 ? reflectParameters : null; + 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,14 +295,26 @@ 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) { @@ -257,28 +325,4 @@ private static byte[] decodeByteArray(UnsafeArrayTypeReader buf) { } 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 e988e4f00540e..a9da42b99f9bb 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 4527c1b7ac153..f1778a3b3937a 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 d9345e90d191e..88edd4e8a1610 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,307 +25,58 @@ 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.option.SubstrateOptionsParser; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.NativeImageOptions; -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; + private Parameter[] getParameters0() { + return ImageSingletons.lookup(MethodMetadataDecoder.class).parseReflectParameters(SubstrateUtil.cast(this, Executable.class), rawParameters); } - @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())) { - 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(); - } - - @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(); - } - - @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(); - } - - @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; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return executable.getParameterAnnotations(); - } + byte[] getTypeAnnotationBytes0() { + return SubstrateUtil.cast(this, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations; } - 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, receiver); - } - } + static class RawParametersComputer implements RecomputeFieldValue.CustomFieldValueComputer { - public static final class AnnotatedParameterTypesComputer 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 AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedParameterTypes, receiver); - } - } - - 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, receiver); - } - } - - 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, receiver); - } - } - - private static final class AnnotatedTypeEncoder { - static Object encodeAnnotationTypes(Supplier supplier, Object receiver) { - try { - return supplier.get(); - } catch (InternalError e) { - return e; - } catch (LinkageError e) { - if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { - return e; - } - Executable culprit = (Executable) receiver; - String message = "Encountered an error while processing annotated types for type: " + culprit.getDeclaringClass().getName() + ", executable: " + culprit.getName() + ". " + - "To avoid the issue at build time, use " + SubstrateOptionsParser.commandArgument(NativeImageOptions.AllowIncompleteClasspath, "+") + ". " + - "The error is then reported at runtime when these annotated types are first accessed."; - 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 7df97126af7ed..045580fa65991 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 6163dfe0e6e92..7707edfcbe196 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 d4483747a8709..9b9d03d68b313 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 a240a6c7d8cb5..f46bed4838f1d 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 9032338213bdf..937c24acc2277 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,16 @@ 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.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.VMError; import com.oracle.svm.reflect.hosted.MethodMetadataEncoderImpl; @@ -53,82 +50,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 +115,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 +128,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; @@ -256,7 +178,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 +197,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 +216,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 +235,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 +254,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 +273,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 +292,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 +311,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 +335,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 432d09b858e50..7ac4bc2274b33 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;