diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java index fbe7c9ce94a4..bcb42968c32a 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/UnsafeArrayTypeWriter.java @@ -74,7 +74,13 @@ protected Chunk(int arrayLength) { protected int totalSize; public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess) { - if (supportsUnalignedMemoryAccess) { + return create(supportsUnalignedMemoryAccess, false); + } + + public static UnsafeArrayTypeWriter create(boolean supportsUnalignedMemoryAccess, boolean bigEndian) { + if (bigEndian) { + return new BigEndianUnsafeArrayTypeWriter(); + } else if (supportsUnalignedMemoryAccess) { return new UnalignedUnsafeArrayTypeWriter(); } else { return new AlignedUnsafeArrayTypeWriter(); @@ -287,3 +293,35 @@ protected void putS8(long value, Chunk chunk, long offset) { UNSAFE.putByte(chunk.data, offset + 7, (byte) (value >> 56)); } } + +final class BigEndianUnsafeArrayTypeWriter extends UnsafeArrayTypeWriter { + private static final Unsafe UNSAFE = getUnsafe(); + + @Override + protected void putS2(long value, Chunk chunk, long offset) { + assert TypeConversion.isS2(value); + UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 8)); + UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 0)); + } + + @Override + protected void putS4(long value, Chunk chunk, long offset) { + assert TypeConversion.isS4(value); + UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 24)); + UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 16)); + UNSAFE.putByte(chunk.data, offset + 2, (byte) (value >> 8)); + UNSAFE.putByte(chunk.data, offset + 3, (byte) (value >> 0)); + } + + @Override + protected void putS8(long value, Chunk chunk, long offset) { + UNSAFE.putByte(chunk.data, offset + 0, (byte) (value >> 56)); + UNSAFE.putByte(chunk.data, offset + 1, (byte) (value >> 48)); + UNSAFE.putByte(chunk.data, offset + 2, (byte) (value >> 40)); + UNSAFE.putByte(chunk.data, offset + 3, (byte) (value >> 32)); + UNSAFE.putByte(chunk.data, offset + 4, (byte) (value >> 24)); + UNSAFE.putByte(chunk.data, offset + 5, (byte) (value >> 16)); + UNSAFE.putByte(chunk.data, offset + 6, (byte) (value >> 8)); + UNSAFE.putByte(chunk.data, offset + 7, (byte) (value >> 0)); + } +} diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java index 47f1d3d4afa3..64b961a5322a 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/BoxingSnippets.java @@ -221,7 +221,12 @@ private static LocationIdentity getCacheLocation(CoreProviders providers, JavaKi if (innerClasses == null || innerClasses.length == 0) { throw GraalError.shouldNotReachHere("Inner classes must exist"); } - return new FieldLocationIdentity(providers.getMetaAccess().lookupJavaField(innerClasses[0].getDeclaredField("cache"))); + for (Class innerClass : innerClasses) { + if (innerClass.getName().endsWith("Cache")) { + return new FieldLocationIdentity(providers.getMetaAccess().lookupJavaField(innerClass.getDeclaredField("cache"))); + } + } + throw GraalError.shouldNotReachHere("No cache inner class found"); } catch (Throwable e) { throw GraalError.shouldNotReachHere(e); } diff --git a/docs/reference-manual/native-image/BuildOutput.md b/docs/reference-manual/native-image/BuildOutput.md index 5b70d24b2c0a..f920f787ac74 100644 --- a/docs/reference-manual/native-image/BuildOutput.md +++ b/docs/reference-manual/native-image/BuildOutput.md @@ -99,7 +99,7 @@ To reduce overhead, please ensure that the classpath only contains entries that #### Reflection Registrations The number of classes, fields, and methods that are registered for reflection. -Large numbers can cause significant reflection overheads, slow down the build process, and increase the size of the native image (see [method metadata](#glossary-method-metadata)). +Large numbers can cause significant reflection overheads, slow down the build process, and increase the size of the native image (see [reflection metadata](#glossary-reflection-metadata)). #### JNI Access Registrations The number of classes, fields, and methods that are registered for [JNI][doc_jni] access. @@ -136,16 +136,16 @@ Therefore, reducing the number of [reachable methods](#glossary-reachability) al The image heap contains reachable objects such as static application data, metadata, and `byte[]` for different purposes. ##### General Heap Data Stored in `byte[]` -The total size of all `byte[]` objects that are neither used for `java.lang.String`, nor [code metadata](#glossary-code-metadata), nor [method metadata](#glossary-method-metadata), nor [graph encodings](#glossary-graph-encodings). +The total size of all `byte[]` objects that are neither used for `java.lang.String`, nor [code metadata](#glossary-code-metadata), nor [reflection metadata](#glossary-reflection-metadata), nor [graph encodings](#glossary-graph-encodings). Therefore, this can also include `byte[]` objects from application code. ##### Code Metadata Stored in `byte[]` The total size of all `byte[]` objects used for metadata for the [code area](#glossary-code-area). Therefore, reducing the number of [reachable methods](#glossary-reachability) also reduces the size of this metadata. -##### Method Metadata Stored in `byte[]` -The total size of all `byte[]` objects used for method metadata, a type of reflection metadata. -To reduce the amount of method metadata, reduce the number of [classes registered for reflection](#glossary-reflection-classes). +##### Reflection Metadata Stored in `byte[]` +The total size of all `byte[]` objects used for reflection metadata, including class, field, method and constructor data. +To reduce the amount of reflection metadata, reduce the number of [elements registered for reflection](#glossary-reflection-registrations). ##### Graph Encodings Stored in `byte[]` The total size of all `byte[]` objects used for graph encodings. diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/CContext.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/CContext.java index 7c9ae99abcf6..8b4e5690e15c 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/CContext.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/CContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -47,6 +47,8 @@ import java.util.Collections; import java.util.List; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CLibrary; /** @@ -58,6 +60,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) +@Platforms(Platform.HOSTED_ONLY.class) public @interface CContext { /** diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java index 7f5f5d0e8e53..c4d0f95765ea 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java @@ -40,18 +40,36 @@ */ 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 registerHeapDynamicHub(Object hub); + + Set getHeapDynamicHubs(); + + void registerHeapReflectionObject(AccessibleObject object); + + Set getHeapReflectionObjects(); int getReflectionClassesCount(); diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index dde0dd16cf7b..0586015359ad 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -194,7 +194,9 @@ "sun.invoke.util", "sun.net", "sun.reflect.annotation", + "sun.reflect.generics.factory", "sun.reflect.generics.reflectiveObjects", + "sun.reflect.generics.repository", "sun.reflect.generics.tree", "sun.security.jca", "sun.security.ssl", diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index d3de39c1e64a..486a09e05fa6 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -159,6 +159,9 @@ protected final void scanField(AnalysisField field, JavaConstant receiver, ScanR throw AnalysisError.shouldNotReachHere("Could not find field " + field.format("%H.%n") + (receiver == null ? "" : " on " + constantType(bb, receiver).toJavaName()) + System.lineSeparator() + backtrace); + } else if (fieldValue.getJavaKind() == JavaKind.Illegal) { + /* The value is not available yet */ + return; } if (fieldValue.getJavaKind() == JavaKind.Object && bb.getHostVM().isRelocatedPointer(constantAsObject(bb, fieldValue))) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index 6d95bffa8b08..492823af7c11 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -400,7 +400,7 @@ private boolean notifyAnalysis(JavaConstant array, AnalysisType arrayType, JavaC return analysisModified; } - void onObjectReachable(ImageHeapObject imageHeapObject) { + protected void onObjectReachable(ImageHeapObject imageHeapObject) { AnalysisType objectType = metaAccess.lookupJavaType(imageHeapObject.getObject()); imageHeap.add(objectType, imageHeapObject); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index b195673ca765..73f32838ba0b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -720,8 +720,9 @@ protected void onValueUpdate(EconomicMap, Object> values, String ol } }; + @SuppressWarnings("unused")// @APIOption(name = "configure-reflection-metadata")// - @Option(help = "Enable runtime instantiation of reflection objects for non-invoked methods.", type = OptionType.Expert)// + @Option(help = "Enable runtime instantiation of reflection objects for non-invoked methods.", type = OptionType.Expert, deprecated = true)// public static final HostedOptionKey ConfigureReflectionMetadata = new HostedOptionKey<>(true); @Option(help = "Include a list of methods included in the image for runtime inspection.", type = OptionType.Expert)// diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java index 2b91ee63e9aa..f6517ab6b705 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java @@ -106,7 +106,7 @@ public void addToReferenceMapSize(long size) { } public static final class Encoders { - final FrequencyEncoder objectConstants; + public final FrequencyEncoder objectConstants; public final FrequencyEncoder> sourceClasses; public final FrequencyEncoder sourceMethodNames; final FrequencyEncoder names; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index a0d10a71879e..cfd051289163 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -24,11 +24,14 @@ */ package com.oracle.svm.core.hub; +import static com.oracle.svm.core.reflect.ReflectionMetadataDecoder.NO_DATA; + import java.io.InputStream; import java.io.Serializable; import java.lang.annotation.Annotation; 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; @@ -42,12 +45,17 @@ import java.lang.reflect.TypeVariable; import java.net.URL; import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.StringJoiner; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.SuppressFBWarnings; +import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunctionPointer; @@ -58,7 +66,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 +76,25 @@ import com.oracle.svm.core.annotate.UnknownObjectField; import com.oracle.svm.core.classinitialization.ClassInitializationInfo; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; +import com.oracle.svm.core.jdk.JDK11OrEarlier; import com.oracle.svm.core.jdk.JDK17OrLater; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.meta.SharedType; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; +import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent; +import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.LazyFinalReference; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; -import jdk.internal.reflect.ConstantPool; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.Reflection; +import jdk.internal.reflect.ReflectionFactory; import jdk.vm.ci.meta.JavaKind; +import sun.reflect.annotation.AnnotationType; +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.repository.ClassRepository; @Hybrid(canHybridFieldsBeDuplicated = false) @Substitute @@ -253,13 +271,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 +287,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 +305,31 @@ public void setModule(Module module) { private final DynamicHubCompanion companion; + private String signature; + + @Substitute @InjectAccessors(ReflectionDataAccessors.class) // + private SoftReference> reflectionData; + + @Substitute @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) // + private int classRedefinedCount; + + @Substitute @InjectAccessors(AnnotationDataAccessors.class) // + private Target_java_lang_Class_AnnotationData annotationData; + + @Substitute @InjectAccessors(AnnotationTypeAccessors.class) // + private AnnotationType annotationType; + + @Substitute @InjectAccessors(CachedConstructorAccessors.class) // + private Constructor cachedConstructor; + + @Substitute @InjectAccessors(NewInstanceCallerCacheAccessors.class) // + @TargetElement(onlyWith = JDK11OrEarlier.class) // + private Class newInstanceCallerCache; + + @UnknownObjectField(types = DynamicHubMetadata.class, canBeNull = true) private DynamicHubMetadata hubMetadata; + + @UnknownObjectField(types = ReflectionMetadata.class, canBeNull = true) private ReflectionMetadata reflectionMetadata; + @Platforms(Platform.HOSTED_ONLY.class) public DynamicHub(Class hostedJavaClass, String name, HubType hubType, ReferenceType referenceType, Object isLocalClass, Object isAnonymousClass, DynamicHub superType, DynamicHub componentHub, String sourceFileName, int modifiers, ClassLoader classLoader, boolean isHidden, boolean isRecord, Class nestHost, boolean assertionStatus, @@ -376,26 +409,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 +419,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 +452,26 @@ 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 setHubMetadata(int enclosingMethodInfoIndex, int annotationsIndex, int typeAnnotationsIndex, int classesEncodingIndex, int permittedSubclassesEncodingIndex) { + this.hubMetadata = new DynamicHubMetadata(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesEncodingIndex); + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setReflectionMetadata(int fieldsEncodingIndex, int methodsEncodingIndex, int constructorsEncodingIndex, int recordComponentsEncodingIndex) { + this.reflectionMetadata = new ReflectionMetadata(fieldsEncodingIndex, methodsEncodingIndex, constructorsEncodingIndex, recordComponentsEncodingIndex); + } + /** Executed at runtime. */ private static Object initEnumConstantsAtRuntime(Method values) { try { @@ -707,20 +728,14 @@ void setClassLoaderAtRuntime(ClassLoader loader) { companion.setClassLoader(loader); } - @Substitute - private String getSimpleName() { - return getSimpleName0(); - } + @KeepOriginal + private native String getSimpleName(); @KeepOriginal // private native String getSimpleName0(); - @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel string comparison") - @Substitute - private String getCanonicalName() { - String canonicalName = getCanonicalName0(); - return canonicalName == Target_java_lang_Class_ReflectionData.NULL_SENTINEL ? null : canonicalName; - } + @KeepOriginal + private native String getCanonicalName(); @KeepOriginal private native String getCanonicalName0(); @@ -805,86 +820,20 @@ private Object getDeclaringClassInternal() { } } - @Substitute - public DynamicHub[] getInterfaces() { - return getInterfaces(this, true); - } - - @Substitute - private DynamicHub[] getInterfaces(boolean cloneArray) { - return getInterfaces(this, cloneArray); - } - - private static DynamicHub[] getInterfaces(DynamicHub hub, boolean cloneArray) { - if (hub.interfacesEncoding == null) { - return new DynamicHub[0]; - } else if (hub.interfacesEncoding instanceof DynamicHub) { - return new DynamicHub[]{(DynamicHub) hub.interfacesEncoding}; - } else { - /* The caller is allowed to modify the array, so we have to make a copy. */ - return cloneArray ? ((DynamicHub[]) hub.interfacesEncoding).clone() : (DynamicHub[]) hub.interfacesEncoding; - } - } - - @Substitute - public Object newInstance() throws Throwable { - /* - * The JavaDoc for the original method states that "The class is initialized if it has not - * already been initialized". However, it doesn't specify if the absence of a nullary - * constructor will result in an InstantiationException before the class is initialized. We - * eagerly initialize it to conform with JCK tests. - */ - ensureInitialized(); - final Constructor nullaryConstructor = rd.nullaryConstructor; - if (nullaryConstructor == null) { - throw new InstantiationException("Type `" + this.getCanonicalName() + - "` can not be instantiated reflectively as it does not have a no-parameter constructor or the no-parameter constructor has not been added explicitly to the native image."); - } - try { - return nullaryConstructor.newInstance((Object[]) null); - } catch (InvocationTargetException e) { - throw e.getCause(); - } - } - - /** - * Used for reporting errors related to reflective instantiation ({@code Class.newInstance}). - * This method handles: i) abstract classes, ii) interfaces, and iii) primitive types. - * - * @param instance Allocated instance of a type. The instance is used to report errors with a - * proper type name. - * @return Always nothing. - * @throws InstantiationException always. - */ - private static Object newInstanceInstantiationError(Object instance) throws InstantiationException { - if (instance == null) { - throw VMError.shouldNotReachHere("This case should be handled by the `DynamicNewInstance` lowering."); - } else { - throw new InstantiationException("Type `" + instance.getClass().getCanonicalName() + "` can not be instantiated reflectively as it does not have a no-parameter constructor."); - } + @KeepOriginal + public native DynamicHub[] getInterfaces(); - } + @KeepOriginal + private native DynamicHub[] getInterfaces(boolean cloneArray); - /** - * Used for reporting errors related to reflective instantiation ({@code Class.newInstance}) - * when a constructor is removed by reachability analysis. - * - * @param instance Allocated instance of a type. The instance is used to report errors with a - * proper type name. - * @return Always nothing. - */ - private static Object newInstanceReachableError(Object instance) { - throw new RuntimeException("Constructor of `" + instance.getClass().getCanonicalName() + - "` was removed by reachability analysis. Use `Feature.BeforeAnalysisAccess.registerForReflectiveInstantiation` to register the type for reflective instantiation."); - } + @KeepOriginal + public native Object newInstance() throws Throwable; // Checkstyle: allow direct annotation access (false positives) - @Substitute + @KeepOriginal @Override - public T getAnnotation(Class annotationClass) { - return AnnotationsEncoding.decodeAnnotations(annotationsEncoding).getAnnotation(annotationClass); - } + public native T getAnnotation(Class annotationClass); @Substitute @Override @@ -892,11 +841,9 @@ public boolean isAnnotationPresent(Class annotationClass) return getAnnotation(annotationClass) != null; } - @Substitute + @KeepOriginal @Override - public Annotation[] getAnnotations() { - return AnnotationsEncoding.decodeAnnotations(annotationsEncoding).getAnnotations(); - } + public native Annotation[] getAnnotations(); @Substitute @Override @@ -923,11 +870,9 @@ public T[] getAnnotationsByType(Class annotationClass) return result; } - @Substitute + @KeepOriginal @Override - public Annotation[] getDeclaredAnnotations() { - return AnnotationsEncoding.decodeAnnotations(annotationsEncoding).getDeclaredAnnotations(); - } + public native Annotation[] getDeclaredAnnotations(); /** * In JDK this method uses a lazily computed map of annotations. @@ -935,95 +880,16 @@ public Annotation[] getDeclaredAnnotations() { * In SVM we have a pre-initialized array so we use a less efficient implementation from * {@link AnnotatedElement} that does the same. */ - @Substitute + @KeepOriginal @Override - public A[] getDeclaredAnnotationsByType(Class annotationClass) { - return GenericDeclaration.super.getDeclaredAnnotationsByType(annotationClass); - } + public native A[] getDeclaredAnnotationsByType(Class annotationClass); - @Substitute + @KeepOriginal @Override - public T getDeclaredAnnotation(Class annotationClass) { - return AnnotationsEncoding.decodeAnnotations(annotationsEncoding).getDeclaredAnnotation(annotationClass); - } + public native T getDeclaredAnnotation(Class annotationClass); // Checkstyle: disallow direct annotation access - /** - * This class stores similar information as the non-public class java.lang.Class.ReflectionData. - */ - public static final class ReflectionData { - static final ReflectionData EMPTY = new ReflectionData(new Field[0], new Field[0], new Field[0], new Method[0], new Method[0], new Constructor[0], new Constructor[0], null, new Field[0], - new Method[0], new Class[0], null, new Class[0], null, null); - - public static ReflectionData get(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods, - Constructor[] declaredConstructors, Constructor[] publicConstructors, Constructor nullaryConstructor, Field[] declaredPublicFields, - Method[] declaredPublicMethods, Class[] declaredClasses, Class[] permittedSubclasses, Class[] publicClasses, Executable enclosingMethodOrConstructor, - Object[] recordComponents) { - - if (z(declaredFields) && z(publicFields) && z(publicUnhiddenFields) && z(declaredMethods) && z(publicMethods) && z(declaredConstructors) && - z(publicConstructors) && nullaryConstructor == null && z(declaredPublicFields) && z(declaredPublicMethods) && z(declaredClasses) && - permittedSubclasses == null && z(publicClasses) && enclosingMethodOrConstructor == null && (recordComponents == null || z(recordComponents))) { - return EMPTY; // avoid redundant objects in image heap - } - return new ReflectionData(declaredFields, publicFields, publicUnhiddenFields, declaredMethods, publicMethods, declaredConstructors, publicConstructors, nullaryConstructor, - declaredPublicFields, declaredPublicMethods, declaredClasses, permittedSubclasses, publicClasses, enclosingMethodOrConstructor, recordComponents); - } - - private static boolean z(Object[] array) { // for better readability above - return array.length == 0; - } - - final Field[] declaredFields; - final Field[] publicFields; - final Field[] publicUnhiddenFields; - final Method[] declaredMethods; - final Method[] publicMethods; - final Constructor[] declaredConstructors; - final Constructor[] publicConstructors; - final Constructor nullaryConstructor; - final Field[] declaredPublicFields; - final Method[] declaredPublicMethods; - final Class[] declaredClasses; - final Class[] permittedSubclasses; - final Class[] publicClasses; - final Object[] recordComponents; - - /** - * The result of {@link Class#getEnclosingMethod()} or - * {@link Class#getEnclosingConstructor()}. - */ - final Executable enclosingMethodOrConstructor; - - ReflectionData(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods, Constructor[] declaredConstructors, - Constructor[] publicConstructors, Constructor nullaryConstructor, Field[] declaredPublicFields, Method[] declaredPublicMethods, Class[] declaredClasses, - Class[] permittedSubclasses, Class[] publicClasses, Executable enclosingMethodOrConstructor, - Object[] recordComponents) { - this.declaredFields = declaredFields; - this.publicFields = publicFields; - this.publicUnhiddenFields = publicUnhiddenFields; - this.declaredMethods = declaredMethods; - this.publicMethods = publicMethods; - this.declaredConstructors = declaredConstructors; - this.publicConstructors = publicConstructors; - this.nullaryConstructor = nullaryConstructor; - this.declaredPublicFields = declaredPublicFields; - this.declaredPublicMethods = declaredPublicMethods; - this.declaredClasses = declaredClasses; - this.permittedSubclasses = permittedSubclasses; - this.publicClasses = publicClasses; - this.enclosingMethodOrConstructor = enclosingMethodOrConstructor; - this.recordComponents = recordComponents; - } - } - - ReflectionData rd = ReflectionData.EMPTY; - - @Platforms(Platform.HOSTED_ONLY.class) - public void setReflectionData(ReflectionData rd) { - this.rd = rd; - } - @KeepOriginal private native Field[] getFields(); @@ -1033,61 +899,20 @@ public void setReflectionData(ReflectionData rd) { @KeepOriginal private native Constructor[] getConstructors(); - @Substitute - private Field getField(@SuppressWarnings("hiding") String name) throws NoSuchFieldException { - /* - * The original code of getField() does a recursive search to avoid creating objects for all - * public fields. We prepare them during the image build and can just iterate here. - * - * Note that we only search those fields which are not hidden by other fields which are - * possibly not registered for reflective access. For example: - * - * class A { public int field; public static int staticField; } // both registered - * - * class B extends A { public int field; public static int staticField; } // not registered - * - * Here, we do not want B.class.getField("field") to return A.field; same applies to - * staticField. Note that hidden fields of A are still returned by B.class.getFields(). - */ - for (Field field : rd.publicUnhiddenFields) { - if (field.getName().equals(name)) { - return field; - } - } - throw new NoSuchFieldException(name); - } + @KeepOriginal + private native Field getField(@SuppressWarnings("hiding") String name) throws NoSuchMethodException; - @Substitute - private Method getMethod(@SuppressWarnings("hiding") String name, Class... parameterTypes) throws NoSuchMethodException { - /* - * The original code of getMethods() does a recursive search to avoid creating objects for - * all public methods. We prepare them during the image build and can just iterate here. - */ - Method method = searchMethods(companion.getCompleteReflectionData(this).publicMethods, name, parameterTypes); - if (method == null) { - throw new NoSuchMethodException(describeMethod(getName() + "." + name + "(", parameterTypes, ")")); - } - return method; - } + @KeepOriginal + private native Method getMethod(@SuppressWarnings("hiding") String name, Class... parameterTypes) throws NoSuchMethodException; @KeepOriginal private native Constructor getConstructor(Class... parameterTypes); - @Substitute - private Class[] getDeclaredClasses() { - for (Class clazz : rd.declaredClasses) { - PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); - } - return rd.declaredClasses; - } + @KeepOriginal + private native Class[] getDeclaredClasses(); - @Substitute - private Class[] getClasses() { - for (Class clazz : rd.publicClasses) { - PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); - } - return rd.publicClasses; - } + @KeepOriginal + private native Class[] getClasses(); @KeepOriginal private native Field[] getDeclaredFields(); @@ -1107,32 +932,20 @@ private Class[] getClasses() { @KeepOriginal private native Constructor getDeclaredConstructor(Class... parameterTypes); - @Substitute - private Constructor[] privateGetDeclaredConstructors(boolean publicOnly) { - ReflectionData reflectionData = companion.getCompleteReflectionData(this); - return publicOnly ? reflectionData.publicConstructors : reflectionData.declaredConstructors; - } + @KeepOriginal + private native Constructor[] privateGetDeclaredConstructors(boolean publicOnly); - @Substitute - private Field[] privateGetDeclaredFields(boolean publicOnly) { - return publicOnly ? rd.declaredPublicFields : rd.declaredFields; - } + @KeepOriginal + private native Field[] privateGetDeclaredFields(boolean publicOnly); - @Substitute - private Method[] privateGetDeclaredMethods(boolean publicOnly) { - ReflectionData reflectionData = companion.getCompleteReflectionData(this); - return publicOnly ? reflectionData.declaredPublicMethods : reflectionData.declaredMethods; - } + @KeepOriginal + private native Method[] privateGetDeclaredMethods(boolean publicOnly); - @Substitute - private Field[] privateGetPublicFields() { - return rd.publicFields; - } + @KeepOriginal + private native Field[] privateGetPublicFields(); - @Substitute - Method[] privateGetPublicMethods() { - return companion.getCompleteReflectionData(this).publicMethods; - } + @KeepOriginal + native Method[] privateGetPublicMethods(); @KeepOriginal @TargetElement(onlyWith = JDK17OrLater.class) @@ -1141,30 +954,17 @@ Method[] privateGetPublicMethods() { @Substitute @TargetElement(onlyWith = JDK17OrLater.class) private Target_java_lang_reflect_RecordComponent[] getRecordComponents0() { - Object[] result = rd.recordComponents; - if (result == null) { + if (reflectionMetadata == null || reflectionMetadata.recordComponentsEncodingIndex == NO_DATA) { /* See ReflectionDataBuilder.buildRecordComponents() for details. */ throw VMError.unsupportedFeature("Record components not available for record class " + getTypeName() + ". " + "All record component accessor methods of this record class must be included in the reflection configuration at image build time, then this method can be called."); } - return (Target_java_lang_reflect_RecordComponent[]) result; + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseRecordComponents(this, reflectionMetadata.recordComponentsEncodingIndex); } - @Substitute + @KeepOriginal @TargetElement(onlyWith = JDK17OrLater.class) - private Class[] getPermittedSubclasses() { - /* - * We make several assumptions here: we precompute this value by using the cached value from - * image build time, agent run / custom reflection configuration is required, we ignore all - * classloader checks, and assume that cached result would be valid. - */ - if (rd.permittedSubclasses != null) { - for (Class clazz : rd.permittedSubclasses) { - PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); - } - } - return rd.permittedSubclasses; - } + private native Class[] getPermittedSubclasses(); @Substitute @SuppressWarnings("unused") @@ -1179,15 +979,29 @@ private void checkPackageAccess(SecurityManager sm, ClassLoader ccl, boolean che } @Substitute - private static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactory() { + private static ReflectionFactory getReflectionFactory() { return Target_jdk_internal_reflect_ReflectionFactory.getReflectionFactory(); } @KeepOriginal private static native Field searchFields(Field[] fields, String name); - @KeepOriginal - private static native Method searchMethods(Method[] methods, String name, Class[] parameterTypes); + /** + * @see #filterHidingMethods(Method...) + */ + @Substitute + private static Method searchMethods(Method[] allMethods, String name, Class[] parameterTypes) { + Method[] methods = filterHidingMethods(allMethods); + ReflectionFactory fact = getReflectionFactory(); + Method res = null; + for (Method m : methods) { + if (m.getName().equals(name) && arrayContentsEq(parameterTypes, fact.getExecutableSharedParameterTypes(m)) && + (res == null || (res.getReturnType() != m.getReturnType() && res.getReturnType().isAssignableFrom(m.getReturnType())))) { + res = m; + } + } + return res; + } @KeepOriginal private native Constructor getConstructor0(Class[] parameterTypes, int which); @@ -1198,55 +1012,44 @@ private static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactor @KeepOriginal private static native Field[] copyFields(Field[] arg); - @KeepOriginal - private static native Method[] copyMethods(Method[] arg); + /** + * @see #filterHidingMethods(Method...) + */ + @Substitute + private static Method[] copyMethods(Method[] original) { + Method[] arg = filterHidingMethods(original); + Method[] out = new Method[arg.length]; + ReflectionFactory fact = getReflectionFactory(); + for (int i = 0; i < arg.length; i++) { + out[i] = fact.copyMethod(arg[i]); + } + return out; + } @KeepOriginal private static native Constructor[] copyConstructors(Constructor[] arg); - @Substitute + @KeepOriginal @Override - public TypeVariable[] getTypeParameters() { - return genericInfo.getTypeParameters(); - } + public native TypeVariable[] getTypeParameters(); - @Substitute - public Type[] getGenericInterfaces() { - return genericInfo.hasGenericInterfaces() ? genericInfo.getGenericInterfaces() : getInterfaces(); - } + @KeepOriginal + public native Type[] getGenericInterfaces(); - @Substitute - public Type getGenericSuperclass() { - return genericInfo.hasGenericSuperClass() ? genericInfo.getGenericSuperClass() : getSuperHub(); - } + @KeepOriginal + public native Type getGenericSuperclass(); - @Substitute - public AnnotatedType getAnnotatedSuperclass() { - return annotatedSuperInfo.getAnnotatedSuperclass(); - } + @KeepOriginal + public native AnnotatedType getAnnotatedSuperclass(); - @Substitute - public AnnotatedType[] getAnnotatedInterfaces() { - return annotatedSuperInfo.getAnnotatedInterfaces(); - } + @KeepOriginal + public native AnnotatedType[] getAnnotatedInterfaces(); - @Substitute - private Method getEnclosingMethod() { - if (rd.enclosingMethodOrConstructor instanceof Method) { - PredefinedClassesSupport.throwIfUnresolvable(rd.enclosingMethodOrConstructor.getDeclaringClass(), getClassLoader0()); - return (Method) rd.enclosingMethodOrConstructor; - } - return null; - } + @KeepOriginal + private native Method getEnclosingMethod(); - @Substitute - private Constructor getEnclosingConstructor() { - if (rd.enclosingMethodOrConstructor instanceof Constructor) { - PredefinedClassesSupport.throwIfUnresolvable(rd.enclosingMethodOrConstructor.getDeclaringClass(), getClassLoader0()); - return (Constructor) rd.enclosingMethodOrConstructor; - } - return null; - } + @KeepOriginal + private native Constructor getEnclosingConstructor(); @Substitute public static Class forName(String className) throws ClassNotFoundException { @@ -1310,11 +1113,9 @@ String computePackageName() { return pn; } + @KeepOriginal @Override - @Substitute - public String toString() { - return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName(); - } + public native String toString(); @KeepOriginal public native String toGenericString(); @@ -1367,10 +1168,8 @@ private static String describeMethod(String prefix, Class[] argTypes, String return sj.toString(); } - @Substitute // - private Target_java_lang_Class_ReflectionData reflectionData() { - throw VMError.unsupportedFeature("DynamicHub.reflectionData()"); - } + @KeepOriginal // + private native Target_java_lang_Class_ReflectionData reflectionData(); @KeepOriginal private native boolean isTopLevelClass(); @@ -1394,9 +1193,22 @@ private String getSimpleBinaryName0() { /* See open/src/hotspot/share/prims/jvm.cpp#1522. */ } - @KeepOriginal // + /** + * @see #filterHidingMethods(Method...) + */ + @Substitute // @SuppressWarnings({"unused"}) - private native List getDeclaredPublicMethods(String nameArg, Class... parameterTypes); + List getDeclaredPublicMethods(String methodName, Class... parameterTypes) { + Method[] methods = filterHidingMethods(privateGetDeclaredMethods(/* publicOnly */ true)); + ReflectionFactory factory = getReflectionFactory(); + List result = new ArrayList<>(); + for (Method method : methods) { + if (method.getName().equals(methodName) && Arrays.equals(factory.getExecutableSharedParameterTypes(method), parameterTypes)) { + result.add(factory.copyMethod(method)); + } + } + return result; + } @Substitute public Class getNestHost() { @@ -1477,11 +1289,28 @@ public Optional describeConstable() { @Delete static native Class getPrimitiveClass(String name); - @Delete - private native Object[] getEnclosingMethod0(); + @Substitute + private Object[] getEnclosingMethod0() { + if (hubMetadata == null || hubMetadata.enclosingMethodInfoIndex == NO_DATA) { + return null; + } + Object[] enclosingMethod = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseEnclosingMethod(hubMetadata.enclosingMethodInfoIndex); + if (enclosingMethod != null) { + PredefinedClassesSupport.throwIfUnresolvable((Class) enclosingMethod[0], getClassLoader0()); + } + return enclosingMethod; + } - @Delete - private native Class[] getInterfaces0(); + @Substitute + private DynamicHub[] getInterfaces0() { + if (interfacesEncoding == null) { + return new DynamicHub[0]; + } else if (interfacesEncoding instanceof DynamicHub) { + return new DynamicHub[]{(DynamicHub) interfacesEncoding}; + } else { + return (DynamicHub[]) interfacesEncoding; + } + } @Substitute private void setSigners(@SuppressWarnings("unused") Object[] signers) { @@ -1491,29 +1320,67 @@ 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() { + if (hubMetadata == null || hubMetadata.annotationsIndex == NO_DATA) { + return null; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseByteArray(hubMetadata.annotationsIndex); + } - @Delete - native byte[] getRawTypeAnnotations(); + @Substitute + byte[] getRawTypeAnnotations() { + if (hubMetadata == null || hubMetadata.typeAnnotationsIndex == NO_DATA) { + return null; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseByteArray(hubMetadata.typeAnnotationsIndex); + } - @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) { + if (reflectionMetadata == null || reflectionMetadata.fieldsEncodingIndex == NO_DATA) { + return new Field[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, reflectionMetadata.fieldsEncodingIndex, publicOnly, true); + } - @Delete - private native Method[] getDeclaredMethods0(boolean publicOnly); + @Substitute + private Method[] getDeclaredMethods0(boolean publicOnly) { + if (reflectionMetadata == null || reflectionMetadata.methodsEncodingIndex == NO_DATA) { + return new Method[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, reflectionMetadata.methodsEncodingIndex, publicOnly, true); + } - @Delete - private native Constructor[] getDeclaredConstructors0(boolean publicOnly); + @Substitute + private Constructor[] getDeclaredConstructors0(boolean publicOnly) { + if (reflectionMetadata == null || reflectionMetadata.constructorsEncodingIndex == NO_DATA) { + return new Constructor[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, reflectionMetadata.constructorsEncodingIndex, publicOnly, true); + } - @Delete - private native Class[] getDeclaredClasses0(); + @Substitute + private Class[] getDeclaredClasses0() { + if (hubMetadata == null || hubMetadata.classesEncodingIndex == NO_DATA) { + return new Class[0]; + } + Class[] declaredClasses = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseClasses(hubMetadata.classesEncodingIndex); + for (Class clazz : declaredClasses) { + PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); + } + return declaredClasses; + } @Delete private static native boolean desiredAssertionStatus0(Class clazz); @@ -1526,6 +1393,197 @@ 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 (hubMetadata == null || hubMetadata.permittedSubclassesEncodingIndex == NO_DATA) { + return null; + } + Class[] permittedSubclasses = ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseClasses(hubMetadata.permittedSubclassesEncodingIndex); + for (Class clazz : permittedSubclasses) { + PredefinedClassesSupport.throwIfUnresolvable(clazz, getClassLoader0()); + } + return permittedSubclasses; + } + + @KeepOriginal + private native GenericsFactory getFactory(); + + @KeepOriginal + private native Method getMethod0(String methodName, Class[] parameterTypes); + + @KeepOriginal + private static native void addAll(Collection c, Field[] o); + + @KeepOriginal + private native Target_java_lang_PublicMethods_MethodList getMethodsRecursive(String methodName, Class[] parameterTypes, boolean includeStatic); + + @KeepOriginal + private native Field getField0(String fieldName); + + @KeepOriginal + native AnnotationType getAnnotationType(); + + @KeepOriginal + static native byte[] getExecutableTypeAnnotationBytes(Executable ex); + + /* + * We need to filter out hiding methods at the last moment. This ensures that the JDK internals + * see them as regular methods and ensure the visibility of methods is correct, but they should + * not be returned to application code. + */ + private static Method[] filterHidingMethods(Method... methods) { + List filtered = new ArrayList<>(); + for (Method method : methods) { + if (!ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHidingMethod(method.getModifiers())) { + filtered.add(method); + } + } + return filtered.toArray(new Method[0]); + } + + private static class ReflectionDataAccessors { + @SuppressWarnings("unused") + private static SoftReference> getReflectionData(DynamicHub that) { + return that.companion.getReflectionData(); + } + } + + private static class AnnotationDataAccessors { + @SuppressWarnings("unused") + private static Target_java_lang_Class_AnnotationData getAnnotationData(DynamicHub that) { + return that.companion.getAnnotationData(); + } + } + + private static class AnnotationTypeAccessors { + @SuppressWarnings("unused") + private static AnnotationType getAnnotationType(DynamicHub that) { + return that.companion.getAnnotationType(); + } + } + + private static class CachedConstructorAccessors { + @SuppressWarnings("unused") + private static Constructor getCachedConstructor(DynamicHub that) { + /* + * The JavaDoc for the Class.newInstance method states that "The class is initialized if + * it has not already been initialized". However, it doesn't specify if the absence of a + * nullary constructor will result in an InstantiationException before the class is + * initialized. We eagerly initialize the class to conform with JCK tests. + */ + that.ensureInitialized(); + return that.companion.getCachedConstructor(); + } + + @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); + } + } + + private static final class DynamicHubMetadata { + final int enclosingMethodInfoIndex; + + final int annotationsIndex; + + final int typeAnnotationsIndex; + + final int classesEncodingIndex; + + @TargetElement(onlyWith = JDK17OrLater.class)// + final int permittedSubclassesEncodingIndex; + + private DynamicHubMetadata(int enclosingMethodInfoIndex, int annotationsIndex, int typeAnnotationsIndex, int classesEncodingIndex, int permittedSubclassesEncodingIndex) { + this.enclosingMethodInfoIndex = enclosingMethodInfoIndex; + this.annotationsIndex = annotationsIndex; + this.typeAnnotationsIndex = typeAnnotationsIndex; + this.classesEncodingIndex = classesEncodingIndex; + this.permittedSubclassesEncodingIndex = permittedSubclassesEncodingIndex; + } + } + + private static final class ReflectionMetadata { + final int fieldsEncodingIndex; + + final int methodsEncodingIndex; + + final int constructorsEncodingIndex; + + @TargetElement(onlyWith = JDK17OrLater.class)// + final int recordComponentsEncodingIndex; + + private ReflectionMetadata(int fieldsEncodingIndex, int methodsEncodingIndex, int constructorsEncodingIndex, int recordComponentsEncodingIndex) { + this.fieldsEncodingIndex = fieldsEncodingIndex; + this.methodsEncodingIndex = methodsEncodingIndex; + this.constructorsEncodingIndex = constructorsEncodingIndex; + this.recordComponentsEncodingIndex = recordComponentsEncodingIndex; + } + } + + public Field[] getReachableFields() { + if (reflectionMetadata == null || reflectionMetadata.fieldsEncodingIndex == NO_DATA) { + return new Field[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseFields(this, reflectionMetadata.fieldsEncodingIndex, false, false); + } + + public Method[] getReachableMethods() { + if (reflectionMetadata == null || reflectionMetadata.methodsEncodingIndex == NO_DATA) { + return new Method[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseMethods(this, reflectionMetadata.methodsEncodingIndex, false, false); + } + + public Constructor[] getReachableConstructors() { + if (reflectionMetadata == null || reflectionMetadata.constructorsEncodingIndex == NO_DATA) { + return new Constructor[0]; + } + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseConstructors(this, reflectionMetadata.constructorsEncodingIndex, false, false); + } } /** @@ -1583,18 +1641,83 @@ final class Target_java_lang_Class_ReflectionData { final class Target_jdk_internal_reflect_ReflectionFactory { @Alias // - private static Target_jdk_internal_reflect_ReflectionFactory soleInstance; + private static ReflectionFactory soleInstance; /** * This substitution eliminates the SecurityManager check in the original method, which would * make some build-time verifications fail. */ @Substitute - public static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactory() { + public static ReflectionFactory getReflectionFactory() { return soleInstance; } } -@TargetClass(className = "java.lang.reflect.RecordComponent", onlyWith = JDK17OrLater.class) -final class Target_java_lang_reflect_RecordComponent { +@TargetClass(className = "java.lang.Class", innerClass = "EnclosingMethodInfo") +final class Target_java_lang_Class_EnclosingMethodInfo { +} + +@TargetClass(className = "java.lang.Class", innerClass = "AnnotationData") +final class Target_java_lang_Class_AnnotationData { +} + +@TargetClass(className = "java.lang.PublicMethods", innerClass = "MethodList") +final class Target_java_lang_PublicMethods_MethodList { + @Alias // + Method method; + + @Alias // + Target_java_lang_PublicMethods_MethodList next; + + @Substitute + Method getMostSpecific() { + Method m = method; + Class rt = m.getReturnType(); + for (Target_java_lang_PublicMethods_MethodList ml = next; ml != null; ml = ml.next) { + Method m2 = ml.method; + Class rt2 = m2.getReturnType(); + if (rt2 != rt && rt.isAssignableFrom(rt2)) { + // found more specific return type + m = m2; + rt = rt2; + } + } + /* Filter out hiding methods after the retursive lookup is done */ + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).isHidingMethod(m.getModifiers()) ? null : m; + } +} + +@TargetClass(className = "java.lang.Class", innerClass = "Atomic") +final class Target_java_lang_Class_Atomic { + @Delete static Unsafe unsafe; + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FieldOffset, declClass = DynamicHubCompanion.class, name = "reflectionData") // + private static long reflectionDataOffset; + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FieldOffset, declClass = DynamicHubCompanion.class, name = "annotationType") // + private static long annotationTypeOffset; + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FieldOffset, declClass = DynamicHubCompanion.class, name = "annotationData") // + private static long annotationDataOffset; + + @Substitute + static boolean casReflectionData(DynamicHub clazz, + SoftReference> oldData, + SoftReference> newData) { + return GraalUnsafeAccess.getUnsafe().compareAndSwapObject(clazz.getCompanion(), reflectionDataOffset, oldData, newData); + } + + @Substitute + static boolean casAnnotationType(DynamicHub clazz, + AnnotationType oldType, + AnnotationType newType) { + return GraalUnsafeAccess.getUnsafe().compareAndSwapObject(clazz.getCompanion(), annotationTypeOffset, oldType, newType); + } + + @Substitute + static boolean casAnnotationData(DynamicHub clazz, + Target_java_lang_Class_AnnotationData oldData, + Target_java_lang_Class_AnnotationData newData) { + return GraalUnsafeAccess.getUnsafe().compareAndSwapObject(clazz.getCompanion(), annotationDataOffset, oldData, newData); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java index 42594d0c45a1..ef1544e34b35 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java @@ -24,26 +24,19 @@ */ package com.oracle.svm.core.hub; +import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.graalvm.collections.Pair; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import com.oracle.svm.core.hub.DynamicHub.ReflectionData; import com.oracle.svm.core.jdk.ProtectionDomainSupport; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; -import com.oracle.svm.core.reflect.MethodMetadataDecoder.MethodDescriptor; import com.oracle.svm.core.util.VMError; +import sun.reflect.annotation.AnnotationType; +import sun.reflect.generics.repository.ClassRepository; + /** * The mutable parts of a {@link DynamicHub} instance. */ @@ -59,7 +52,12 @@ public final class DynamicHubCompanion { */ private Object classLoader; private ProtectionDomain protectionDomain; - private ReflectionData completeReflectionData; + private ClassRepository genericInfo; + private SoftReference> reflectionData; + private AnnotationType annotationType; + private Target_java_lang_Class_AnnotationData annotationData; + private Constructor cachedConstructor; + private Class newInstanceCallerCache; @Platforms(Platform.HOSTED_ONLY.class) DynamicHubCompanion(Class hostedJavaClass, ClassLoader classLoader) { @@ -100,83 +98,38 @@ void setProtectionDomain(ProtectionDomain domain) { protectionDomain = domain; } - ReflectionData getCompleteReflectionData(DynamicHub hub) { - if (completeReflectionData == null) { - List newDeclaredMethods = new ArrayList<>(Arrays.asList(hub.rd.declaredMethods)); - List newPublicMethods = new ArrayList<>(Arrays.asList(hub.rd.publicMethods)); - List> newDeclaredConstructors = new ArrayList<>(Arrays.asList(hub.rd.declaredConstructors)); - List> newPublicConstructors = new ArrayList<>(Arrays.asList(hub.rd.publicConstructors)); - List newDeclaredPublicMethods = new ArrayList<>(Arrays.asList(hub.rd.declaredPublicMethods)); - - Pair queriedAndHidingMethods = ImageSingletons.lookup(MethodMetadataDecoder.class).getQueriedAndHidingMethods(hub); - Executable[] queriedMethods = queriedAndHidingMethods.getLeft(); - MethodDescriptor[] hidingMethods = queriedAndHidingMethods.getRight(); - newMethods: for (Executable method : queriedMethods) { - if (method instanceof Constructor) { - Constructor c = (Constructor) method; - for (Constructor c2 : hub.rd.declaredConstructors) { - if (Arrays.equals(c.getParameterTypes(), c2.getParameterTypes())) { - continue newMethods; - } - } - newDeclaredConstructors.add(c); - if (Modifier.isPublic(c.getModifiers())) { - newPublicConstructors.add(c); - } - } else { - Method m = (Method) method; - for (Method m2 : hub.rd.declaredMethods) { - if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) { - continue newMethods; - } - } - newDeclaredMethods.add(m); - if (Modifier.isPublic(m.getModifiers())) { - newPublicMethods.add(m); - newDeclaredPublicMethods.add(m); - } - } - } - - /* Recursively add public superclass methods to the public methods list */ - DynamicHub superHub = hub.getSuperHub(); - if (superHub != null) { - addInheritedPublicMethods(newPublicMethods, superHub.privateGetPublicMethods(), hidingMethods, true); - } - for (DynamicHub interfaceHub : hub.getInterfaces()) { - addInheritedPublicMethods(newPublicMethods, interfaceHub.privateGetPublicMethods(), hidingMethods, hub.isInterface()); - } - - completeReflectionData = new ReflectionData(hub.rd.declaredFields, hub.rd.publicFields, hub.rd.publicUnhiddenFields, newDeclaredMethods.toArray(new Method[0]), - newPublicMethods.toArray(new Method[0]), - newDeclaredConstructors.toArray(new Constructor[0]), newPublicConstructors.toArray(new Constructor[0]), hub.rd.nullaryConstructor, hub.rd.declaredPublicFields, - newDeclaredPublicMethods.toArray(new Method[0]), hub.rd.declaredClasses, hub.rd.permittedSubclasses, hub.rd.publicClasses, hub.rd.enclosingMethodOrConstructor, - hub.rd.recordComponents); + public ClassRepository getGenericInfo(DynamicHub hub) { + if (genericInfo == null) { + genericInfo = hub.computeGenericInfo(); } - return completeReflectionData; + return (genericInfo != ClassRepository.NONE) ? genericInfo : null; } - private static void addInheritedPublicMethods(List newPublicMethods, Method[] parentMethods, MethodDescriptor[] hidingMethods, boolean includeStaticMethods) { - parentMethods: for (Method m : parentMethods) { - if (!includeStaticMethods && Modifier.isStatic(m.getModifiers())) { - continue; - } - for (Method m2 : newPublicMethods) { - if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) { - if (m.getDeclaringClass() != m2.getDeclaringClass() && m2.getDeclaringClass().isAssignableFrom(m.getDeclaringClass())) { - /* Need to store the more specific method */ - newPublicMethods.remove(m2); - newPublicMethods.add(m); - } - continue parentMethods; - } - } - for (MethodDescriptor hidingMethod : hidingMethods) { - if (m.getName().equals(hidingMethod.getName()) && Arrays.equals(m.getParameterTypes(), hidingMethod.getParameterTypes())) { - continue parentMethods; - } - } - newPublicMethods.add(m); - } + SoftReference> getReflectionData() { + return reflectionData; + } + + AnnotationType getAnnotationType() { + return annotationType; + } + + Target_java_lang_Class_AnnotationData getAnnotationData() { + return annotationData; + } + + Constructor getCachedConstructor() { + return cachedConstructor; + } + + void setCachedConstructor(Constructor constructor) { + cachedConstructor = constructor; + } + + Class getNewInstanceCallerCache() { + return newInstanceCallerCache; + } + + void setNewInstanceCallerCache(Class constructor) { + newInstanceCallerCache = constructor; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java index b2718e845248..de5b1007f859 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java @@ -24,277 +24,22 @@ */ package com.oracle.svm.core.hub; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedType; import java.lang.reflect.GenericDeclaration; -import java.lang.reflect.MalformedParameterizedTypeException; -import java.lang.reflect.Type; -import org.graalvm.compiler.core.common.SuppressFBWarnings; -import org.graalvm.util.GuardedAnnotationAccess; - -import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.Inject; -import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; -import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; -import sun.reflect.generics.reflectiveObjects.TypeVariableImpl; -import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl; -import sun.reflect.generics.tree.FieldTypeSignature; - -/** - * The bounds in TypeVariableImpl and WildcardTypeImpl are lazily computed. We capture the value and - * cache it in the native image heap. - */ - @TargetClass(sun.reflect.generics.reflectiveObjects.TypeVariableImpl.class) final class Target_sun_reflect_generics_reflectiveObjects_TypeVariableImpl { - @Alias private String name; - - /* Cache the bounds value. */ - - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = TypeVariableBoundsComputer.class) // - private Object[] bounds; - @Alias GenericDeclaration genericDeclaration; - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = TypeVariableAnnotationsComputer.class) // - Annotation[] annotations; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = TypeVariableAnnotatedBoundsComputer.class) // - AnnotatedType[] annotatedBounds; - - @Substitute - public AnnotatedType[] getAnnotatedBounds() { - return annotatedBounds; - } - /** Reason for substitutions: disable access checks in original method. */ @Substitute public GenericDeclaration getGenericDeclaration() { return genericDeclaration; } - - @Substitute - public Annotation[] getAnnotations() { - return annotations; - } -} - -final class Util_sun_reflect_generics_reflectiveObjects_TypeVariableImpl { - - /** Emulate the Java class hierarchy. */ - static Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator asLazyReflectiveObjectGenerator( - Target_sun_reflect_generics_reflectiveObjects_TypeVariableImpl typeVariableImpl) { - return SubstrateUtil.cast(typeVariableImpl, Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator.class); - } - - /** Emulate virtual dispatch. */ - static Type[] reifyBounds(Target_sun_reflect_generics_reflectiveObjects_TypeVariableImpl typeVariableImpl, FieldTypeSignature[] boundASTs) { - return asLazyReflectiveObjectGenerator(typeVariableImpl).reifyBounds(boundASTs); - } -} - -@TargetClass(className = "sun.reflect.generics.reflectiveObjects.LazyReflectiveObjectGenerator") -final class Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator { - - @Alias - native Type[] reifyBounds(FieldTypeSignature[] boundASTs); -} - -class TypeVariableBoundsComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedBoundsAccess.getBounds((TypeVariableImpl) receiver); - } -} - -class TypeVariableAnnotatedBoundsComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedBoundsAccess.getAnnotatedBounds((TypeVariableImpl) receiver); - } -} - -class TypeVariableAnnotationsComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedAnnotationAccess.getAnnotations((TypeVariableImpl) receiver); - } -} - -@TargetClass(sun.reflect.generics.reflectiveObjects.WildcardTypeImpl.class) -final class Target_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl { - - /* Cache the upperBounds value. */ - @Alias // - @RecomputeFieldValue(kind = Kind.Custom, declClass = WildcardTypeImplUpperBoundsComputer.class) // - private Object[] upperBounds; - - /* Cache the lowerBounds value. */ - @Alias // - @RecomputeFieldValue(kind = Kind.Custom, declClass = WildcardTypeImplLowerBoundsComputer.class) // - private Object[] lowerBounds; - - @Substitute - public Type[] getUpperBounds() { - Object[] value = upperBounds; - if (value instanceof FieldTypeSignature[]) { - value = Util_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl.reifyBounds(this, (FieldTypeSignature[]) value); - upperBounds = value; - } - return (Type[]) value.clone(); - } - - @Substitute - public Type[] getLowerBounds() { - Object[] value = lowerBounds; - if (value instanceof FieldTypeSignature[]) { - value = Util_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl.reifyBounds(this, (FieldTypeSignature[]) value); - lowerBounds = value; - } - return (Type[]) value.clone(); - } -} - -class WildcardTypeImplUpperBoundsComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedBoundsAccess.getUpperBounds((WildcardTypeImpl) receiver); - } -} - -class WildcardTypeImplLowerBoundsComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - return GuardedBoundsAccess.getLowerBounds((WildcardTypeImpl) receiver); - } -} - -final class Util_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl { - - /** Emulate the Java class hierarchy. */ - @SuppressFBWarnings(value = "BC", justification = "Widening cast between @TargetClasses") - static Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator asLazyReflectiveObjectGenerator( - Target_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl wildCardTypeImpl) { - return Target_sun_reflect_generics_reflectiveObjects_LazyReflectiveObjectGenerator.class.cast(wildCardTypeImpl); - } - - /** Emulate virtual dispatch. */ - static Type[] reifyBounds(Target_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl wildCardTypeImpl, FieldTypeSignature[] boundASTs) { - return asLazyReflectiveObjectGenerator(wildCardTypeImpl).reifyBounds(boundASTs); - } -} - -class GuardedBoundsAccess { - - static Type[] getLowerBounds(WildcardTypeImpl receiver) { - try { - return receiver.getLowerBounds(); - } catch (TypeNotPresentException | MalformedParameterizedTypeException e) { - /* - * This computer is used to compute the value of the WildcardTypeImpl.lowerBounds field. - * As per WildcardTypeImpl.getLowerBounds() javadoc: - * - * "Returns an array of Type objects representing the lower bound(s) of this - * type variable. Note that if no lower bound is explicitly declared, the lower bound is - * the type of null. In this case, a zero length array is returned." - * - * Thus, if getLowerBounds() throws a TypeNotPresentException, i.e., any of the bounds - * refers to a non-existent type declaration, or a MalformedParameterizedTypeException, - * i.e., any of the bounds refer to a parameterized type that cannot be instantiated, we - * conservatively return a zero length array. - */ - return new Type[0]; - } - } - - static Type[] getUpperBounds(WildcardTypeImpl receiver) { - try { - return receiver.getUpperBounds(); - } catch (TypeNotPresentException | MalformedParameterizedTypeException e) { - /* - * This computer is used to compute the value of the WildcardTypeImpl.upperBounds field. - * As per WildcardTypeImpl.getUpperBounds() javadoc: - * - * "Returns an array of Type objects representing the upper bound(s) of this - * type variable. Note that if no upper bound is explicitly declared, the upper bound is - * Object." - * - * Thus, if getUpperBounds() throws a TypeNotPresentException, i.e., any of the bounds - * refers to a non-existent type declaration, or a MalformedParameterizedTypeException, - * i.e., any of the bounds refer to a parameterized type that cannot be instantiated, we - * conservatively return the upper bound. - */ - return new Type[0]; - } - } - - static Type[] getBounds(TypeVariableImpl receiver) { - try { - return receiver.getBounds(); - } catch (TypeNotPresentException | MalformedParameterizedTypeException e) { - /* - * This computer is used to compute the value of the TypeVariableImpl.bounds field. As - * per TypeVariableImpl.getBounds() javadoc: - * - * "Returns an array of Type objects representing the upper bound(s) of this - * type variable. Note that if no upper bound is explicitly declared, the upper bound is - * Object." - * - * Thus, if getBounds() throws a TypeNotPresentException, i.e., any of the bounds refers - * to a non-existent type declaration, or a MalformedParameterizedTypeException, i.e., - * any of the bounds refer to a parameterized type that cannot be instantiated, we - * conservatively return the upper bound. - */ - return new Type[]{Object.class}; - } - } - - static AnnotatedType[] getAnnotatedBounds(TypeVariableImpl receiver) { - try { - return receiver.getAnnotatedBounds(); - } catch (TypeNotPresentException | MalformedParameterizedTypeException e) { - /* - * This computer is used to compute the value of the TypeVariableImpl.annotatedBounds - * injected field. The original method calls TypeVariableImpl.getBounds() and if no - * bounds are present it returns an empty array. - */ - return new AnnotatedType[0]; - } - } } public class SunReflectTypeSubstitutions { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java index 45ab0cc89786..16687d21aae1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java @@ -220,10 +220,6 @@ private static Target_jdk_internal_access_JavaAWTAccess getJavaAWTAccess() { final class Target_jdk_internal_access_JavaAWTAccess { } -@TargetClass(classNameProvider = Package_jdk_internal_access.class, className = "JavaLangAccess") -final class Target_jdk_internal_access_JavaLangAccess { -} - @TargetClass(className = "sun.reflect.misc.MethodUtil") final class Target_sun_reflect_misc_MethodUtil { @Substitute diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_module_ModuleDescriptor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_module_ModuleDescriptor.java new file mode 100644 index 000000000000..0f8d6b69fccc --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_module_ModuleDescriptor.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.jdk; + +import java.lang.module.ModuleDescriptor; +import java.util.Locale; +import java.util.Set; + +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; + +@TargetClass(ModuleDescriptor.class) +public final class Target_java_lang_module_ModuleDescriptor { + /* Substituted to prevent including stream classes in basic images */ + @Substitute + private static String toString(Set mods, String what) { + StringBuilder builder = new StringBuilder(); + for (M mod : mods) { + builder.append(mod.toString().toLowerCase(Locale.ROOT)); + builder.append(" "); + } + builder.append(what); + return builder.toString(); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java index 3da4274f6beb..3fc182a66745 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/meta/ReadableJavaField.java @@ -34,7 +34,10 @@ 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()) { + assert !readableField.allowConstantFolding() && readableField.isUnknown(); + return JavaConstant.forIllegal(); + } return readableField.readValue(metaAccess, javaConstant); } else { return originalConstantReflection.readFieldValue(javaField, javaConstant); @@ -62,4 +65,8 @@ static boolean injectFinalForRuntimeCompilation(ResolvedJavaField original) { return false; } } + + default boolean isUnknown() { + return false; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java similarity index 60% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java index 4487a6dab020..8a8b5c1ee75d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionMetadataDecoder.java @@ -24,41 +24,34 @@ */ 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); +public interface ReflectionMetadataDecoder { + int NO_DATA = -1; - MethodDescriptor[] getAllReachableMethods(); + Field[] parseFields(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly); - long getMetadataByteLength(); + Method[] parseMethods(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly); + + Constructor[] parseConstructors(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly); - class MethodDescriptor { - private final Class declaringClass; - private final String name; - private final Class[] parameterTypes; + Class[] parseClasses(int index); - public MethodDescriptor(Class declaringClass, String name, Class[] parameterTypes) { - this.declaringClass = declaringClass; - this.name = name; - this.parameterTypes = parameterTypes; - } + Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, int index); - public Class getDeclaringClass() { - return declaringClass; - } + Parameter[] parseReflectParameters(Executable executable, byte[] encoding); - public String getName() { - return name; - } + Object[] parseEnclosingMethod(int index); - public Class[] getParameterTypes() { - return parameterTypes; - } - } + byte[] parseByteArray(int index); + boolean isHidingMethod(int modifiers); + + long getMetadataByteLength(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_java_lang_reflect_RecordComponent.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_java_lang_reflect_RecordComponent.java new file mode 100644 index 000000000000..2e31c3e0ed62 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_java_lang_reflect_RecordComponent.java @@ -0,0 +1,48 @@ +/* + * 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.core.reflect; + +import java.lang.reflect.Method; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.jdk.JDK17OrLater; + +@TargetClass(className = "java.lang.reflect.RecordComponent", onlyWith = JDK17OrLater.class) +public final class Target_java_lang_reflect_RecordComponent { + @Alias public Class clazz; + + @Alias public String name; + + @Alias public Class type; + + @Alias public Method accessor; + + @Alias public String signature; + + @Alias public byte[] annotations; + + @Alias public byte[] typeAnnotations; +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_jdk_internal_reflect_ConstantPool.java similarity index 99% rename from substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_jdk_internal_reflect_ConstantPool.java index 722282ca1dcb..b2d41f496288 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/Target_jdk_internal_reflect_ConstantPool.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.reflect.target; +package com.oracle.svm.core.reflect; import static com.oracle.svm.core.util.VMError.unimplemented; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassNewInstanceFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassNewInstanceFeature.java index e88ab1edcc94..d392a1cb533d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassNewInstanceFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassNewInstanceFeature.java @@ -27,7 +27,6 @@ import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; @AutomaticFeature @@ -36,14 +35,7 @@ public class ClassNewInstanceFeature implements Feature { @Override public void beforeAnalysis(BeforeAnalysisAccess a) { BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) a; - access.registerAsCompiled(Object.class.getDeclaredConstructors()[0], true); - try { - access.registerAsCompiled(DynamicHub.class.getDeclaredMethod("newInstanceInstantiationError", Object.class), true); - access.registerAsCompiled(DynamicHub.class.getDeclaredMethod("newInstanceReachableError", Object.class), true); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java index d7629e1e6eb0..cabd098f06db 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporter.java @@ -45,7 +45,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import com.oracle.graal.pointsto.util.TimerCollection; import org.graalvm.compiler.debug.DebugOptions; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.serviceprovider.GraalServices; @@ -60,6 +59,7 @@ import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.reports.ReportUtils; import com.oracle.graal.pointsto.util.Timer; +import com.oracle.graal.pointsto.util.TimerCollection; import com.oracle.svm.core.BuildArtifacts; import com.oracle.svm.core.BuildArtifacts.ArtifactType; import com.oracle.svm.core.OS; @@ -69,7 +69,7 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.option.HostedOptionValues; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.c.codegen.CCompilerInvoker; import com.oracle.svm.hosted.code.CompileQueue.CompileTask; @@ -503,9 +503,9 @@ private Map calculateHeapBreakdown(Collection heapObje classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("code metadata", "#glossary-code-metadata"), codeInfoSize); remainingBytes -= codeInfoSize; } - long metadataByteLength = ImageSingletons.lookup(MethodMetadataDecoder.class).getMetadataByteLength(); + long metadataByteLength = ImageSingletons.lookup(ReflectionMetadataDecoder.class).getMetadataByteLength(); if (metadataByteLength > 0) { - classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("method metadata", "#glossary-method-metadata"), metadataByteLength); + classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("reflection metadata", "#glossary-reflection-metadata"), metadataByteLength); remainingBytes -= metadataByteLength; } if (graphEncodingByteLength > 0) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java index 0f35bf6d8726..8f648aab2837 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java @@ -24,17 +24,14 @@ */ package com.oracle.svm.hosted.analysis; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedType; import java.lang.reflect.Field; -import java.lang.reflect.MalformedParameterizedTypeException; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import org.graalvm.compiler.debug.GraalError; import org.graalvm.nativeimage.c.function.CFunctionPointer; import com.oracle.graal.pointsto.BigBang; @@ -48,9 +45,7 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.classinitialization.ClassInitializationInfo; -import com.oracle.svm.core.hub.AnnotatedSuperInfo; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.hub.GenericInfo; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.util.VMError; @@ -61,7 +56,6 @@ import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaType; public class DynamicHubInitializer { @@ -71,18 +65,13 @@ public class DynamicHubInitializer { private final UnsupportedFeatures unsupportedFeatures; private final ConstantReflectionProvider constantReflection; - private final Map genericInterfacesMap; - private final Map annotatedInterfacesMap; private final Map interfacesEncodings; private final Field dynamicHubClassInitializationInfoField; private final Field dynamicHubArrayHubField; private final Field dynamicHubEnclosingClassField; private final Field dynamicHubInterfacesEncodingField; - private final Field dynamicHubAnnotationsEncodingField; private final Field dynamicHubAnnotationsEnumConstantsReferenceField; - private final Field dynamicHubAnnotatedSuperInfoField; - private final Field dynamicHubGenericInfoField; public DynamicHubInitializer(BigBang bb) { this.bb = bb; @@ -91,18 +80,13 @@ public DynamicHubInitializer(BigBang bb) { this.unsupportedFeatures = bb.getUnsupportedFeatures(); this.constantReflection = bb.getConstantReflectionProvider(); - this.genericInterfacesMap = new ConcurrentHashMap<>(); - this.annotatedInterfacesMap = new ConcurrentHashMap<>(); this.interfacesEncodings = new ConcurrentHashMap<>(); dynamicHubClassInitializationInfoField = ReflectionUtil.lookupField(DynamicHub.class, "classInitializationInfo"); dynamicHubArrayHubField = ReflectionUtil.lookupField(DynamicHub.class, "arrayHub"); dynamicHubEnclosingClassField = ReflectionUtil.lookupField(DynamicHub.class, "enclosingClass"); dynamicHubInterfacesEncodingField = ReflectionUtil.lookupField(DynamicHub.class, "interfacesEncoding"); - dynamicHubAnnotationsEncodingField = ReflectionUtil.lookupField(DynamicHub.class, "annotationsEncoding"); dynamicHubAnnotationsEnumConstantsReferenceField = ReflectionUtil.lookupField(DynamicHub.class, "enumConstantsReference"); - dynamicHubAnnotatedSuperInfoField = ReflectionUtil.lookupField(DynamicHub.class, "annotatedSuperInfo"); - dynamicHubGenericInfoField = ReflectionUtil.lookupField(DynamicHub.class, "genericInfo"); } public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) { @@ -118,11 +102,8 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) if (hub.getClassInitializationInfo() == null) { buildClassInitializationInfo(heapScanner, type, hub); } - if (hub.getGenericInfo() == null) { - fillGenericInfo(heapScanner, type, hub); - } - if (hub.getAnnotatedSuperInfo() == null) { - fillAnnotatedSuperInfo(heapScanner, type, hub); + if (hub.getSignature() == null) { + fillSignature(type, hub); } if (type.getJavaKind() == JavaKind.Object) { @@ -146,28 +127,6 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) heapScanner.rescanField(hub, dynamicHubInterfacesEncodingField); } - /* - * Support for Java annotations. - * - * The annotation encodings must be updated after each analysis iteration since only the - * annotation types marked as reachable are included. - */ - try { - /* - * Get the annotations from the wrapped type since AnalysisType.getAnnotations() - * defends against JDK-7183985, and we want to get the original behavior. - */ - Annotation[] annotations = type.getWrappedWithoutResolve().getAnnotations(); - Annotation[] declared = type.getWrappedWithoutResolve().getDeclaredAnnotations(); - Object annotationsEncoding = AnnotationsProcessor.encodeAnnotations(metaAccess, annotations, declared, hub.getAnnotationsEncoding()); - if (hub.setAnnotationsEncoding(annotationsEncoding)) { - heapScanner.rescanField(hub, dynamicHubAnnotationsEncodingField); - } - } catch (ArrayStoreException e) { - /* If we hit JDK-7183985 just encode the exception. */ - hub.setAnnotationsEncoding(e); - } - /* * Support for Java enumerations. */ @@ -274,172 +233,17 @@ private void registerAsCompiled(AnalysisMethod aMethod) { CompilationInfoSupport.singleton().registerForcedCompilation(aMethod); } - static class GenericInterfacesEncodingKey { - final Type[] interfaces; - - GenericInterfacesEncodingKey(Type[] aInterfaces) { - this.interfaces = aInterfaces; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof GenericInterfacesEncodingKey && Arrays.equals(interfaces, ((GenericInterfacesEncodingKey) obj).interfaces); - } - - @Override - public int hashCode() { - return Arrays.hashCode(interfaces); - } - } - - /** Modified copy of {@link Arrays#equals(Object[], Object[])}. */ - private static boolean shallowEquals(Object[] a, Object[] a2) { - if (a == a2) { - return true; - } else if (a == null || a2 == null) { - return false; - } - int length = a.length; - if (a2.length != length) { - return false; - } - for (int i = 0; i < length; i++) { - /* Modification: use reference equality. */ - if (a[i] != a2[i]) { - return false; - } - } - return true; - } - - /** Modified copy of {@link Arrays#hashCode(Object[])}. */ - private static int shallowHashCode(Object[] a) { - if (a == null) { - return 0; - } - int result = 1; - - for (Object element : a) { - /* Modification: use identity hash code. */ - result = 31 * result + System.identityHashCode(element); - } - return result; - } - - static class AnnotatedInterfacesEncodingKey { - final AnnotatedType[] interfaces; - - AnnotatedInterfacesEncodingKey(AnnotatedType[] aInterfaces) { - this.interfaces = aInterfaces; - } - - /* - * After JDK 11, the implementation of hashCode() and equals() for the implementation - * classes of annotated types can lead to the reification of generic bounds, which can lead - * to TypeNotPresentException when the class path is incomplete. Therefore, we use shallow - * implementations that only depend on the identity hash code and reference equality. This - * is the same behavior as on JDK 8 and JDK 11 anyway. - */ - - @Override - public boolean equals(Object obj) { - return obj instanceof AnnotatedInterfacesEncodingKey && shallowEquals(interfaces, ((AnnotatedInterfacesEncodingKey) obj).interfaces); - } + private static final Method getSignature = ReflectionUtil.lookupMethod(Class.class, "getGenericSignature0"); - @Override - public int hashCode() { - return shallowHashCode(interfaces); - } - } - - private void fillGenericInfo(ImageHeapScanner heapScanner, AnalysisType type, DynamicHub hub) { - Class javaClass = type.getJavaClass(); - - TypeVariable[] typeParameters = javaClass.getTypeParameters(); - - Type[] allGenericInterfaces; - try { - allGenericInterfaces = javaClass.getGenericInterfaces(); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Loading generic interfaces can fail due to missing types. Ignore the exception and - * return an empty array. - */ - allGenericInterfaces = new Type[0]; - } - - Type[] genericInterfaces = Arrays.stream(allGenericInterfaces).filter(this::isTypeAllowed).toArray(Type[]::new); - Type[] cachedGenericInterfaces; - try { - cachedGenericInterfaces = genericInterfacesMap.computeIfAbsent(new GenericInterfacesEncodingKey(genericInterfaces), k -> genericInterfaces); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Computing the hash code of generic interfaces can fail due to missing types. Ignore - * the exception and proceed without caching. De-duplication of generic interfaces is an - * optimization and not necessary for correctness. - */ - cachedGenericInterfaces = genericInterfaces; - } - - Type genericSuperClass; - try { - genericSuperClass = javaClass.getGenericSuperclass(); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Loading the generic super class can fail due to missing types. Ignore the exception - * and return null. - */ - genericSuperClass = null; - } - if (!isTypeAllowed(genericSuperClass)) { - genericSuperClass = null; - } - hub.setGenericInfo(GenericInfo.factory(typeParameters, cachedGenericInterfaces, genericSuperClass)); - heapScanner.rescanField(hub, dynamicHubGenericInfoField); - } - - private void fillAnnotatedSuperInfo(ImageHeapScanner heapScanner, AnalysisType type, DynamicHub hub) { + private static void fillSignature(AnalysisType type, DynamicHub hub) { Class javaClass = type.getJavaClass(); - - AnnotatedType annotatedSuperclass; + String signature; try { - annotatedSuperclass = javaClass.getAnnotatedSuperclass(); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Loading the annotated super class can fail due to missing types. Ignore the exception - * and return null. - */ - annotatedSuperclass = null; - } - if (annotatedSuperclass != null && !isTypeAllowed(annotatedSuperclass.getType())) { - annotatedSuperclass = null; - } - - AnnotatedType[] allAnnotatedInterfaces; - try { - allAnnotatedInterfaces = javaClass.getAnnotatedInterfaces(); - } catch (MalformedParameterizedTypeException | TypeNotPresentException | LinkageError t) { - /* - * Loading annotated interfaces can fail due to missing types. Ignore the exception and - * return an empty array. - */ - allAnnotatedInterfaces = new AnnotatedType[0]; - } - - AnnotatedType[] annotatedInterfaces = Arrays.stream(allAnnotatedInterfaces) - .filter(ai -> isTypeAllowed(ai.getType())).toArray(AnnotatedType[]::new); - AnnotatedType[] cachedAnnotatedInterfaces = annotatedInterfacesMap.computeIfAbsent( - new AnnotatedInterfacesEncodingKey(annotatedInterfaces), k -> annotatedInterfaces); - hub.setAnnotatedSuperInfo(AnnotatedSuperInfo.factory(annotatedSuperclass, cachedAnnotatedInterfaces)); - heapScanner.rescanField(hub, dynamicHubAnnotatedSuperInfoField); - } - - private boolean isTypeAllowed(Type t) { - if (t instanceof Class) { - Optional resolved = metaAccess.optionalLookupJavaType((Class) t); - return resolved.isPresent() && hostVM.platformSupported(resolved.get()); + signature = (String) getSignature.invoke(javaClass); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); } - return true; + hub.setSignature(signature); } class InterfacesEncodingKey { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java index 09e3d014b293..d74cda5c45be 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ConfigurableClassInitialization.java @@ -579,20 +579,6 @@ InitKind computeInitKindAndMaybeInitializeClass(Class clazz, boolean memoize, return existing; } - /* Initialize all annotations because we don't support parsing at run-time. */ - if (clazz.isAnnotation()) { - forceInitializeHosted(clazz, "all annotations are initialized", false); - return InitKind.BUILD_TIME; - } - - /* Well, and enums that got initialized while annotations are parsed. */ - if (clazz.isEnum() && !Unsafe.getUnsafe().shouldBeInitialized(clazz)) { - if (memoize) { - forceInitializeHosted(clazz, "enums referred in annotations must be initialized", false); - } - return InitKind.BUILD_TIME; - } - if (clazz.isPrimitive()) { forceInitializeHosted(clazz, "primitive types are initialized at build time", false); return InitKind.BUILD_TIME; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java index 72326a6e641e..2e5273c7502e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java @@ -24,12 +24,15 @@ */ package com.oracle.svm.hosted.heap; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.util.function.Consumer; 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; @@ -40,6 +43,7 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.svm.core.BuildPhaseProvider; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.ReadableJavaField; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.util.VMError; @@ -58,6 +62,7 @@ public class SVMImageHeapScanner extends ImageHeapScanner { private final Class economicMapImpl; private final Field economicMapImplEntriesField; private final Field economicMapImplHashArrayField; + private final RuntimeReflectionSupport reflectionSupport; public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess, SnippetReflectionProvider snippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) { @@ -67,6 +72,7 @@ public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, Analysi economicMapImplEntriesField = ReflectionUtil.lookupField(economicMapImpl, "entries"); economicMapImplHashArrayField = ReflectionUtil.lookupField(economicMapImpl, "hashArray"); ImageSingletons.add(ImageHeapScanner.class, this); + reflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class); } public static ImageHeapScanner instance() { @@ -135,4 +141,16 @@ protected void rescanEconomicMap(EconomicMap map) { } } + + @Override + protected void onObjectReachable(ImageHeapObject imageHeapObject) { + super.onObjectReachable(imageHeapObject); + + Object object = SubstrateObjectConstant.asObject(imageHeapObject.getObject()); + if (object instanceof Field || object instanceof Executable) { + reflectionSupport.registerHeapReflectionObject((AccessibleObject) object); + } else if (object instanceof DynamicHub) { + reflectionSupport.registerHeapDynamicHub(object); + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java index fe31a58fe5a3..9fcc81289c1a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java @@ -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,8 +88,11 @@ 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.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.hosted.meta.HostedUniverse; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.site.Call; @@ -229,28 +239,79 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code codeInfoEncoder.addMethod(method, compilation, method.getCodeAddressOffset()); } - 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); + ReflectionMetadataEncoder reflectionMetadataEncoder = ImageSingletons.lookup(ReflectionMetadataEncoderFactory.class).create(encoders); + RuntimeReflectionSupport reflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class); + HostedUniverse hUniverse = imageHeap.getUniverse(); + HostedMetaAccess hMetaAccess = imageHeap.getMetaAccess(); + + Map, Set>> innerClasses = reflectionSupport.getReflectionInnerClasses(); + Set heapDynamicHubs = reflectionSupport.getHeapDynamicHubs(); + for (HostedType type : hUniverse.getTypes()) { + if (type.getWrapped().isReachable() && heapDynamicHubs.contains(type.getHub())) { + Class[] typeInnerClasses = innerClasses.getOrDefault(type.getJavaClass(), Collections.emptySet()).toArray(new Class[0]); + reflectionMetadataEncoder.addClassMetadata(hMetaAccess, type, typeInnerClasses); } - for (Object method : ImageSingletons.lookup(RuntimeReflectionSupport.class).getHidingMethods()) { - AnalysisMethod hidingMethod = (AnalysisMethod) method; - HostedType declaringType = imageHeap.getUniverse().lookup(hidingMethod.getDeclaringClass()); + } + + Set includedFields = new HashSet<>(); + Set includedMethods = new HashSet<>(); + + for (AccessibleObject object : reflectionSupport.getHeapReflectionObjects()) { + if (object instanceof Field) { + includedFields.add(hMetaAccess.lookupJavaField((Field) object)); + } else if (object instanceof Method || object instanceof Constructor) { + includedMethods.add(hMetaAccess.lookupJavaMethod((Executable) object)); + } + reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, object); + } + + for (Field reflectField : reflectionSupport.getReflectionFields()) { + HostedField field = hMetaAccess.lookupJavaField(reflectField); + if (!includedFields.contains(field)) { + reflectionMetadataEncoder.addReflectionFieldMetadata(hMetaAccess, field, reflectField); + includedFields.add(field); + } + } + + for (Executable reflectMethod : reflectionSupport.getReflectionExecutables()) { + HostedMethod method = hMetaAccess.lookupJavaMethod(reflectMethod); + if (!includedMethods.contains(method)) { + Object accessor = reflectionSupport.getAccessor(reflectMethod); + reflectionMetadataEncoder.addReflectionExecutableMetadata(hMetaAccess, method, reflectMethod, accessor); + includedMethods.add(method); + } + } + + for (Object method : reflectionSupport.getHidingReflectionMethods()) { + AnalysisMethod hidingMethod = (AnalysisMethod) method; + HostedMethod hostedMethod = hUniverse.optionalLookup(hidingMethod); + if (hostedMethod == null || !includedMethods.contains(hostedMethod)) { + HostedType declaringType = hUniverse.lookup(hidingMethod.getDeclaringClass()); String name = hidingMethod.getName(); JavaType[] analysisParameterTypes = hidingMethod.getSignature().toParameterTypes(null); HostedType[] parameterTypes = new HostedType[analysisParameterTypes.length]; for (int i = 0; i < analysisParameterTypes.length; ++i) { - parameterTypes[i] = imageHeap.getUniverse().lookup(analysisParameterTypes[i]); + parameterTypes[i] = hUniverse.lookup(analysisParameterTypes[i]); + } + int modifiers = hidingMethod.getModifiers(); + HostedType returnType = hUniverse.lookup(hidingMethod.getSignature().getReturnType(null)); + reflectionMetadataEncoder.addHidingMethodMetadata(hidingMethod, declaringType, name, parameterTypes, modifiers, returnType); + if (hostedMethod != null) { + includedMethods.add(hostedMethod); } - methodMetadataEncoder.addHidingMethodMetadata(declaringType, name, parameterTypes); } } + if (SubstrateOptions.IncludeMethodData.getValue()) { - for (HostedMethod method : imageHeap.getUniverse().getMethods()) { - if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod()) { - methodMetadataEncoder.addReachableMethodMetadata(method); + for (HostedField field : hUniverse.getFields()) { + if (field.isAccessed() && !includedFields.contains(field)) { + reflectionMetadataEncoder.addReachableFieldMetadata(field); + } + } + + for (HostedMethod method : hUniverse.getMethods()) { + if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod() && !includedMethods.contains(method)) { + reflectionMetadataEncoder.addReachableExecutableMetadata(method); } } } @@ -262,7 +323,7 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code HostedImageCodeInfo imageCodeInfo = CodeInfoTable.getImageCodeCache().getHostedImageCodeInfo(); codeInfoEncoder.encodeAllAndInstall(imageCodeInfo, new InstantReferenceAdjuster()); - methodMetadataEncoder.encodeAllAndInstall(); + reflectionMetadataEncoder.encodeAllAndInstall(); imageCodeInfo.setCodeStart(firstMethod); imageCodeInfo.setCodeSize(codeSize); imageCodeInfo.setDataOffset(codeSize); @@ -554,17 +615,35 @@ protected boolean isDeoptEntry(ResolvedJavaMethod method, Infopoint infopoint) { } } - public interface MethodMetadataEncoder { - void addReflectionMethodMetadata(MetaAccessProvider metaAccess, HostedMethod sharedMethod, Executable reflectMethod); + public interface ReflectionMetadataEncoder { + 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 addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object); - void addHidingMethodMetadata(HostedType declType, String name, HostedType[] paramTypes); + void addHidingMethodMetadata(AnalysisMethod analysisMethod, HostedType declType, String name, HostedType[] paramTypes, int modifiers, HostedType returnType); - void addReachableMethodMetadata(HostedMethod method); + void addReachableFieldMetadata(HostedField field); + + void addReachableExecutableMetadata(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 { - MethodMetadataEncoder create(CodeInfoEncoder.Encoders encoders); + public interface ReflectionMetadataEncoderFactory { + ReflectionMetadataEncoder create(CodeInfoEncoder.Encoders encoders); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java index 2e1aed9d04ad..391c42457af9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/ComputedValueField.java @@ -53,6 +53,8 @@ import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueProvider; import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueTransformer; +import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.annotate.UnknownPrimitiveField; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.meta.ReadableJavaField; import com.oracle.svm.core.util.UserError; @@ -499,4 +501,9 @@ public String toString() { public Field getJavaField() { return OriginalFieldProvider.getJavaField(GraalAccess.getOriginalSnippetReflection(), original); } + + @Override + public boolean isUnknown() { + return annotated != null && (annotated.isAnnotationPresent(UnknownObjectField.class) || annotated.isAnnotationPresent(UnknownPrimitiveField.class)); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java index 3cada6d5aee3..6f1cd9289eb1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java @@ -900,7 +900,9 @@ private boolean tryAutomaticRecomputation(ResolvedJavaField field, Kind kind, Su if (substitutionField instanceof ComputedValueField) { ComputedValueField computedSubstitutionField = (ComputedValueField) substitutionField; if (computedSubstitutionField.getRecomputeValueKind().equals(kind)) { - reportUnnecessarySubstitution(substitutionField, computedSubstitutionField); + if (computedSubstitutionField.getTargetField().equals(computedSubstitutionField.getJavaField())) { + reportUnnecessarySubstitution(substitutionField, computedSubstitutionField); + } return false; } else if (computedSubstitutionField.getRecomputeValueKind().equals(Kind.None)) { /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java deleted file mode 100644 index da61ae7c6f93..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.reflect.hosted; - -import java.lang.annotation.Annotation; - -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.hosted.Feature; - -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; -import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; -import com.oracle.svm.hosted.meta.HostedType; -import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl; -import com.oracle.svm.reflect.target.MethodMetadataEncoding; - -import sun.reflect.annotation.TypeAnnotation; - -@AutomaticFeature -class MethodMetadataFeature implements Feature { - @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(MethodMetadataEncoderFactory.class, new MethodMetadataEncoderImpl.Factory()); - ImageSingletons.add(MethodMetadataDecoder.class, new MethodMetadataDecoderImpl()); - ImageSingletons.add(MethodMetadataEncoding.class, new MethodMetadataEncoding()); - } -} - -public class MethodMetadata { - final HostedType declaringType; - final String name; - final HostedType[] parameterTypes; - - MethodMetadata(HostedType declaringType, String name, HostedType[] parameterTypes) { - this.declaringType = declaringType; - this.name = name; - this.parameterTypes = parameterTypes; - } - - static class ReflectionMethodMetadata extends MethodMetadata { - final int modifiers; - final HostedType returnType; - final HostedType[] exceptionTypes; - final String signature; - final Annotation[] annotations; - final Annotation[][] parameterAnnotations; - final TypeAnnotation[] typeAnnotations; - final boolean hasRealParameterData; - final MethodMetadataDecoderImpl.ReflectParameterDescriptor[] reflectParameters; - - ReflectionMethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, HostedType[] exceptionTypes, String signature, - Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, boolean hasRealParameterData, - MethodMetadataDecoderImpl.ReflectParameterDescriptor[] reflectParameters) { - super(declaringClass, name, parameterTypes); - this.modifiers = modifiers; - this.returnType = returnType; - this.exceptionTypes = exceptionTypes; - this.signature = signature; - this.annotations = annotations; - this.parameterAnnotations = parameterAnnotations; - this.typeAnnotations = typeAnnotations; - this.hasRealParameterData = hasRealParameterData; - this.reflectParameters = reflectParameters; - } - } - -} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java deleted file mode 100644 index 276a118393d9..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java +++ /dev/null @@ -1,662 +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 java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.function.Consumer; - -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.nativeimage.ImageSingletons; -import org.graalvm.util.GuardedAnnotationAccess; - -import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.code.CodeInfoEncoder; -import com.oracle.svm.core.hub.DynamicHubSupport; -import com.oracle.svm.core.util.ByteArrayReader; -import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoder; -import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; -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.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.MetaAccessProvider; -import sun.invoke.util.Wrapper; -import sun.reflect.annotation.AnnotationType; -import sun.reflect.annotation.TypeAnnotation; -import sun.reflect.annotation.TypeAnnotationParser; - -/** - * The method metadata encoding puts data in the image for three distinct types of methods. - *
    - *
  1. Methods that are queried for reflection, but never accessed: in that case, the encoding - * includes everything required to recreate an {@link Executable} object at runtime.
  2. - *
  3. Methods that hide a method registered for reflection, but are not registered themselves: only - * basic method information is stored for those methods (declaring class, name and parameter types). - * They are used to ensure that the hidden superclass method is not incorrectly returned by a - * reflection query on the subclass where it is hidden.
  4. - *
  5. Methods that are included in the image: all reachable methods have their basic information - * included to enable introspecting the produced executable.
  6. - *
- * - * 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 - * {@link #encodeAllAndInstall()}). - */ -public class MethodMetadataEncoderImpl implements MethodMetadataEncoder { - - static class Factory implements MethodMetadataEncoderFactory { - @Override - public MethodMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { - return new MethodMetadataEncoderImpl(encoders); - } - } - - private final CodeInfoEncoder.Encoders encoders; - private final TreeSet sortedTypes; - private Map> queriedMethodData; - private Map> reachableMethodData; - private Map> hidingMethodData; - - private byte[] methodDataEncoding; - private byte[] methodDataIndexEncoding; - - 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<>(); - } - if (SubstrateOptions.IncludeMethodData.getValue()) { - this.reachableMethodData = new HashMap<>(); - } - } - - 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 addReflectionMethodMetadata(MetaAccessProvider metaAccess, HostedMethod hostedMethod, Executable reflectMethod) { - HostedType declaringType = hostedMethod.getDeclaringClass(); - String name = hostedMethod.isConstructor() ? "" : hostedMethod.getName(); - HostedType[] parameterTypes = getParameterTypes(hostedMethod); - /* Reflect method because substitution of Object.hashCode() is private */ - int modifiers = reflectMethod.getModifiers(); - HostedType returnType = (HostedType) hostedMethod.getSignature().getReturnType(null); - HostedType[] exceptionTypes = getExceptionTypes(metaAccess, reflectMethod); - 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]; - - /* Fill encoders with the necessary values. */ - encoders.sourceMethodNames.addObject(name); - 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); - } - for (TypeAnnotation typeAnnotation : typeAnnotations) { - // Checkstyle: allow direct annotation access - registerAnnotationValues(typeAnnotation.getAnnotation()); - // Checkstyle: disallow direct annotation access - } - for (ReflectParameterDescriptor parameter : reflectParameterDescriptors) { - encoders.sourceMethodNames.addObject(parameter.getName()); - } - - sortedTypes.add(declaringType); - queriedMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(new ReflectionMethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, - annotations, parameterAnnotations, typeAnnotations, reflectParameterDataPresent, reflectParameterDescriptors)); - } - - private void registerAnnotationValues(Annotation... annotations) { - for (Annotation annotation : annotations) { - encoders.sourceClasses.addObject(annotation.annotationType()); - registerAnnotationValue(annotation.annotationType(), annotation); - } - } - - @SuppressWarnings("unchecked") - private void registerAnnotationValue(Class type, Object value) { - if (type.isAnnotation()) { - Annotation annotation = (Annotation) value; - AnnotationType annotationType = AnnotationType.getInstance((Class) type); - encoders.sourceClasses.addObject(type); - for (Map.Entry> entry : annotationType.memberTypes().entrySet()) { - String valueName = entry.getKey(); - Class valueType = entry.getValue(); - encoders.sourceMethodNames.addObject(valueName); - Method getAnnotationValue = annotationType.members().get(valueName); - getAnnotationValue.setAccessible(true); - Object annotationValue; - try { - annotationValue = getAnnotationValue.invoke(annotation); - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); - } - registerAnnotationValue(valueType, annotationValue); - } - } else if (type.isArray()) { - Class componentType = type.getComponentType(); - if (!componentType.isPrimitive()) { - for (Object val : (Object[]) value) { - registerAnnotationValue(componentType, val); - } - } - } else if (type == Class.class) { - encoders.sourceClasses.addObject((Class) value); - } else if (type == String.class) { - encoders.sourceMethodNames.addObject((String) value); - } else if (type.isEnum()) { - encoders.sourceClasses.addObject(type); - encoders.sourceMethodNames.addObject(((Enum) value).name()); - } - } - - @Override - public void addHidingMethodMetadata(HostedType declaringType, String name, HostedType[] parameterTypes) { - /* Fill encoders with the necessary values. */ - encoders.sourceMethodNames.addObject(name); - for (HostedType parameterType : parameterTypes) { - encoders.sourceClasses.addObject(parameterType.getJavaClass()); - } - - sortedTypes.add(declaringType); - hidingMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(new MethodMetadata(declaringType, name, parameterTypes)); - } - - @Override - public void addReachableMethodMetadata(HostedMethod method) { - HostedType declaringType = method.getDeclaringClass(); - String name = method.getName(); - HostedType[] parameterTypes = getParameterTypes(method); - - /* Fill encoders with the necessary values. */ - encoders.sourceMethodNames.addObject(method.getName()); - 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)); - } - - private static HostedType[] getParameterTypes(HostedMethod method) { - HostedType[] parameterTypes = new HostedType[method.getSignature().getParameterCount(false)]; - for (int i = 0; i < parameterTypes.length; ++i) { - parameterTypes[i] = (HostedType) method.getSignature().getParameterType(i, null); - } - return parameterTypes; - } - - private static HostedType[] getExceptionTypes(MetaAccessProvider metaAccess, Executable reflectMethod) { - Class[] exceptionClasses = reflectMethod.getExceptionTypes(); - HostedType[] exceptionTypes = new HostedType[exceptionClasses.length]; - for (int i = 0; i < exceptionClasses.length; ++i) { - exceptionTypes[i] = (HostedType) metaAccess.lookupJavaType(exceptionClasses[i]); - } - 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()); - } - return reflectParameterDescriptors; - } - - @Override - public void encodeAllAndInstall() { - encodeMethodMetadata(); - ImageSingletons.lookup(MethodMetadataEncoding.class).setMethodsEncoding(methodDataEncoding); - ImageSingletons.lookup(MethodMetadataEncoding.class).setIndexEncoding(methodDataIndexEncoding); - } - - /** - * 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; - for (HostedType declaringType : sortedTypes) { - long typeID = declaringType.getHub().getTypeID(); - assert typeID >= nextTypeId; - for (; nextTypeId < typeID; nextTypeId++) { - indexEncodingBuffer.putS4(MethodMetadataDecoderImpl.NO_METHOD_METADATA); - } - assert nextTypeId == typeID; - - long index = dataEncodingBuffer.getBytesWritten(); - indexEncodingBuffer.putS4(index); - nextTypeId++; - - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - Set queriedMethods = queriedMethodData.getOrDefault(declaringType, Collections.emptySet()); - encodeArray(dataEncodingBuffer, queriedMethods.toArray(new ReflectionMethodMetadata[0]), method -> { - assert method.declaringType.equals(declaringType); - encodeReflectionMethod(dataEncodingBuffer, method); - }); - - Set hidingMethods = hidingMethodData.getOrDefault(declaringType, Collections.emptySet()); - encodeArray(dataEncodingBuffer, hidingMethods.toArray(new MethodMetadata[0]), hidingMethod -> encodeSimpleMethod(dataEncodingBuffer, hidingMethod)); - } - - if (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); - } - } - } - 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 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())); - } - - private void encodeName(UnsafeArrayTypeWriter buf, String name) { - buf.putSV(encoders.sourceMethodNames.getIndex(name)); - } - - private static void encodeArray(UnsafeArrayTypeWriter buf, T[] array, Consumer elementEncoder) { - buf.putUV(array.length); - for (T elem : array) { - elementEncoder.accept(elem); - } - } - - private static void encodeByteArray(UnsafeArrayTypeWriter buf, byte[] array) { - buf.putUV(array.length); - for (byte b : array) { - buf.putS1(b); - } - } - - private static final Method getMethodSignature = ReflectionUtil.lookupMethod(Method.class, "getGenericSignature"); - private static final Method getConstructorSignature = ReflectionUtil.lookupMethod(Constructor.class, "getSignature"); - - private static String getSignature(Executable method) { - try { - return (String) (method instanceof Method ? getMethodSignature.invoke(method) : getConstructorSignature.invoke(method)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); - } - } - - /** - * 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 - * format we use differs from that one on a few points, based on the fact that the JDK encoding - * is based on constant pool indices, which are not available in that form at runtime. - * - * Class and String values are represented by their index in the source metadata encoders - * instead of their constant pool indices. Additionally, Class objects are encoded directly - * instead of through their type signature. Primitive values are written directly into the - * encoding. This means that our encoding can be of a different length from the JDK one. - * - * We use a modified version of the ConstantPool and AnnotationParser classes to decode the - * data, since those are not used in their original functions at runtime. (see - * {@link Target_jdk_internal_reflect_ConstantPool} and - * {@link Target_sun_reflect_annotation_AnnotationParser}) - */ - public byte[] encodeAnnotations(Annotation[] annotations) { - UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - buf.putU2(annotations.length); - for (Annotation annotation : annotations) { - encodeAnnotation(buf, annotation); - } - return buf.toArray(); - } - - private byte[] encodeParameterAnnotations(Annotation[][] annotations) { - UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - buf.putU1(annotations.length); - for (Annotation[] parameterAnnotations : annotations) { - buf.putU2(parameterAnnotations.length); - for (Annotation parameterAnnotation : parameterAnnotations) { - encodeAnnotation(buf, parameterAnnotation); - } - } - return buf.toArray(); - } - - private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) { - buf.putS4(encoders.sourceClasses.getIndex(annotation.annotationType())); - AnnotationType type = AnnotationType.getInstance(annotation.annotationType()); - buf.putU2(type.members().size()); - for (Map.Entry entry : type.members().entrySet()) { - String memberName = entry.getKey(); - Method valueAccessor = entry.getValue(); - buf.putS4(encoders.sourceMethodNames.getIndex(memberName)); - try { - encodeValue(buf, valueAccessor.invoke(annotation), type.memberTypes().get(memberName)); - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); - } - } - } - - private void encodeValue(UnsafeArrayTypeWriter buf, Object value, Class type) { - buf.putU1(tag(type)); - if (type.isAnnotation()) { - encodeAnnotation(buf, (Annotation) value); - } else if (type.isEnum()) { - buf.putS4(encoders.sourceClasses.getIndex(type)); - buf.putS4(encoders.sourceMethodNames.getIndex(((Enum) value).name())); - } else if (type.isArray()) { - encodeArray(buf, value, type.getComponentType()); - } else if (type == Class.class) { - buf.putS4(encoders.sourceClasses.getIndex((Class) value)); - } else if (type == String.class) { - buf.putS4(encoders.sourceMethodNames.getIndex((String) value)); - } else if (type.isPrimitive() || Wrapper.isWrapperType(type)) { - Wrapper wrapper = type.isPrimitive() ? Wrapper.forPrimitiveType(type) : Wrapper.forWrapperType(type); - switch (wrapper) { - case BOOLEAN: - buf.putU1((boolean) value ? 1 : 0); - break; - case BYTE: - buf.putS1((byte) value); - break; - case SHORT: - buf.putS2((short) value); - break; - case CHAR: - buf.putU2((char) value); - break; - case INT: - buf.putS4((int) value); - break; - case LONG: - buf.putS8((long) value); - break; - case FLOAT: - buf.putS4(Float.floatToRawIntBits((float) value)); - break; - case DOUBLE: - buf.putS8(Double.doubleToRawLongBits((double) value)); - break; - default: - throw GraalError.shouldNotReachHere(); - } - } else { - throw GraalError.shouldNotReachHere(); - } - } - - private void encodeArray(UnsafeArrayTypeWriter buf, Object value, Class componentType) { - if (!componentType.isPrimitive()) { - Object[] array = (Object[]) value; - buf.putU2(array.length); - for (Object val : array) { - encodeValue(buf, val, componentType); - } - } else if (componentType == boolean.class) { - boolean[] array = (boolean[]) value; - buf.putU2(array.length); - for (boolean val : array) { - encodeValue(buf, val, componentType); - } - } else if (componentType == byte.class) { - byte[] array = (byte[]) value; - buf.putU2(array.length); - for (byte val : array) { - encodeValue(buf, val, componentType); - } - } else if (componentType == short.class) { - short[] array = (short[]) value; - buf.putU2(array.length); - for (short val : array) { - encodeValue(buf, val, componentType); - } - } else if (componentType == char.class) { - char[] array = (char[]) value; - buf.putU2(array.length); - for (char val : array) { - encodeValue(buf, val, componentType); - } - } else if (componentType == int.class) { - int[] array = (int[]) value; - buf.putU2(array.length); - for (int val : array) { - encodeValue(buf, val, componentType); - } - } else if (componentType == long.class) { - long[] array = (long[]) value; - buf.putU2(array.length); - for (long val : array) { - encodeValue(buf, val, componentType); - } - } else if (componentType == float.class) { - float[] array = (float[]) value; - buf.putU2(array.length); - for (float val : array) { - encodeValue(buf, val, componentType); - } - } else if (componentType == double.class) { - double[] array = (double[]) value; - buf.putU2(array.length); - for (double val : array) { - encodeValue(buf, val, componentType); - } - } - } - - private static byte tag(Class type) { - if (type.isAnnotation()) { - return '@'; - } else if (type.isEnum()) { - return 'e'; - } else if (type.isArray()) { - return '['; - } else if (type == Class.class) { - return 'c'; - } else if (type == String.class) { - return 's'; - } else if (type.isPrimitive()) { - return (byte) Wrapper.forPrimitiveType(type).basicTypeChar(); - } else if (Wrapper.isWrapperType(type)) { - return (byte) Wrapper.forWrapperType(type).basicTypeChar(); - } else { - throw GraalError.shouldNotReachHere(); - } - } - - private byte[] encodeTypeAnnotations(TypeAnnotation[] annotations) { - UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - buf.putU2(annotations.length); - for (TypeAnnotation typeAnnotation : annotations) { - encodeTypeAnnotation(buf, typeAnnotation); - } - return buf.toArray(); - } - - private void encodeTypeAnnotation(UnsafeArrayTypeWriter buf, TypeAnnotation typeAnnotation) { - encodeTargetInfo(buf, typeAnnotation.getTargetInfo()); - encodeLocationInfo(buf, typeAnnotation.getLocationInfo()); - // Checkstyle: allow direct annotation access - encodeAnnotation(buf, typeAnnotation.getAnnotation()); - // Checkstyle: disallow direct annotation access - } - - private static final byte CLASS_TYPE_PARAMETER = 0x00; - private static final byte METHOD_TYPE_PARAMETER = 0x01; - private static final byte CLASS_EXTENDS = 0x10; - private static final byte CLASS_TYPE_PARAMETER_BOUND = 0x11; - private static final byte METHOD_TYPE_PARAMETER_BOUND = 0x12; - private static final byte FIELD = 0x13; - private static final byte METHOD_RETURN = 0x14; - private static final byte METHOD_RECEIVER = 0x15; - private static final byte METHOD_FORMAL_PARAMETER = 0x16; - private static final byte THROWS = 0x17; - - private static void encodeTargetInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.TypeAnnotationTargetInfo targetInfo) { - switch (targetInfo.getTarget()) { - case CLASS_TYPE_PARAMETER: - buf.putU1(CLASS_TYPE_PARAMETER); - buf.putU1(targetInfo.getCount()); - break; - case METHOD_TYPE_PARAMETER: - buf.putU1(METHOD_TYPE_PARAMETER); - buf.putU1(targetInfo.getCount()); - break; - case CLASS_EXTENDS: - buf.putU1(CLASS_EXTENDS); - buf.putS2(-1); - break; - case CLASS_IMPLEMENTS: - buf.putU1(CLASS_EXTENDS); - buf.putS2(targetInfo.getCount()); - break; - case CLASS_TYPE_PARAMETER_BOUND: - buf.putU1(CLASS_TYPE_PARAMETER_BOUND); - buf.putU1(targetInfo.getCount()); - buf.putU1(targetInfo.getSecondaryIndex()); - break; - case METHOD_TYPE_PARAMETER_BOUND: - buf.putU1(METHOD_TYPE_PARAMETER_BOUND); - buf.putU1(targetInfo.getCount()); - buf.putU1(targetInfo.getSecondaryIndex()); - break; - case FIELD: - buf.putU1(FIELD); - break; - case METHOD_RETURN: - buf.putU1(METHOD_RETURN); - break; - case METHOD_RECEIVER: - buf.putU1(METHOD_RECEIVER); - break; - case METHOD_FORMAL_PARAMETER: - buf.putU1(METHOD_FORMAL_PARAMETER); - buf.putU1(targetInfo.getCount()); - break; - case THROWS: - buf.putU1(THROWS); - buf.putU2(targetInfo.getCount()); - break; - } - } - - private static final Field locationInfoDepth = ReflectionUtil.lookupField(TypeAnnotation.LocationInfo.class, "depth"); - private static final Field locationInfoLocations = ReflectionUtil.lookupField(TypeAnnotation.LocationInfo.class, "locations"); - - private static void encodeLocationInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.LocationInfo locationInfo) { - try { - int depth = (int) locationInfoDepth.get(locationInfo); - buf.putU1(depth); - TypeAnnotation.LocationInfo.Location[] locations; - locations = (TypeAnnotation.LocationInfo.Location[]) locationInfoLocations.get(locationInfo); - for (TypeAnnotation.LocationInfo.Location location : locations) { - buf.putS1(location.tag); - buf.putU1(location.index); - } - } catch (IllegalAccessException e) { - throw GraalError.shouldNotReachHere(); - } - } -} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java index 76a494f4596a..29149fd945aa 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java @@ -24,12 +24,16 @@ */ package com.oracle.svm.reflect.hosted; +import static com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl.getTypeAnnotations; + import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InaccessibleObjectException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; @@ -46,104 +50,69 @@ import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; import java.util.stream.Collectors; -import org.graalvm.compiler.debug.GraalError; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; +import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.InjectAccessors; import com.oracle.svm.core.hub.AnnotationTypeSupport; import com.oracle.svm.core.hub.ClassForNameSupport; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.RecordSupport; -import com.oracle.svm.core.jdk.SealedClassSupport; import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ConditionalConfigurationRegistry; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; -import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; -import com.oracle.svm.hosted.FeatureImpl.FeatureAccessImpl; import com.oracle.svm.hosted.annotation.AnnotationSubstitutionType; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; -import com.oracle.svm.util.ReflectionUtil; +import com.oracle.svm.util.ModuleSupport; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import sun.reflect.annotation.AnnotationType; +import sun.reflect.annotation.EnumConstantNotPresentExceptionProxy; import sun.reflect.annotation.TypeAnnotation; -import sun.reflect.annotation.TypeAnnotationParser; +import sun.reflect.annotation.TypeNotPresentExceptionProxy; public class ReflectionDataBuilder extends ConditionalConfigurationRegistry implements RuntimeReflectionSupport { - public static final Field[] EMPTY_FIELDS = new Field[0]; - public static final Method[] EMPTY_METHODS = new Method[0]; - public static final Constructor[] EMPTY_CONSTRUCTORS = new Constructor[0]; - public static final Class[] EMPTY_CLASSES = new Class[0]; - private final Set> modifiedClasses = ConcurrentHashMap.newKeySet(); private boolean sealed; - private final DynamicHub.ReflectionData arrayReflectionData; private final Set> reflectionClasses = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private final Set reflectionMethods = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Map reflectionMethods = new ConcurrentHashMap<>(); + private final Map methodAccessors = new ConcurrentHashMap<>(); private final Set reflectionFields = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private Set queriedMethods; - private Set hidingMethods; + private final Set hidingMethods = ConcurrentHashMap.newKeySet(); + private final Set registeredMethods = ConcurrentHashMap.newKeySet(); + private final Set registeredFields = ConcurrentHashMap.newKeySet(); + private final Map, Object[]> registeredRecordComponents = new ConcurrentHashMap<>(); + private final Set heapDynamicHubs = ConcurrentHashMap.newKeySet(); + private final Set heapReflectionObjects = ConcurrentHashMap.newKeySet(); + private final Map, Set>> innerClasses = new ConcurrentHashMap<>(); private final Set> processedClasses = new HashSet<>(); + private final Set processedTypes = new HashSet<>(); + private final Set processedDynamicHubs = 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 +132,21 @@ 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) { + ExecutableAccessibility oldValue; + ExecutableAccessibility newValue; + do { + newValue = queriedOnly ? ExecutableAccessibility.QueriedOnly : ExecutableAccessibility.Accessed; + oldValue = reflectionMethods.get(method); + if (oldValue != null) { + newValue = ExecutableAccessibility.max(oldValue, newValue); + } + } while (oldValue == null ? reflectionMethods.putIfAbsent(method, newValue) != null : !reflectionMethods.replace(method, oldValue, newValue)); + if (oldValue != newValue) { modifiedClasses.add(method.getDeclaringClass()); } } @@ -199,10 +173,6 @@ private void checkNotSealed() { } } - protected void duringSetup(DuringSetupAccessImpl access) { - dynamicHubReflectionDataField = access.findField(DynamicHub.class, "rd"); - } - protected void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; processReachableTypes(access); @@ -238,7 +208,7 @@ private void processReachableTypes(DuringAnalysisAccessImpl access) { */ continue; } - if (type.isArray() || enclosingMethodOrConstructor(originalClass) != null) { + if (type.isArray() || enclosingMethodOrConstructor(originalClass, null) != null) { /* * This type is either an array or it has an enclosing method or constructor. In * either case we process the class, i.e., initialize its reflection data, mark @@ -280,28 +250,64 @@ private void processReachableTypes(DuringAnalysisAccessImpl access) { } /** - * See {@link MethodMetadataEncoderImpl} for details. + * See {@link ReflectionMetadataEncoderImpl} 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 (DynamicHub hub : heapDynamicHubs) { + if (!processedDynamicHubs.contains(hub)) { + AnalysisType type = access.getHostVM().lookupType(hub); + if (!SubstitutionReflectivityFilter.shouldExclude(type.getJavaClass(), access.getMetaAccess(), access.getUniverse())) { + registerTypesForClass(access, type, type.getJavaClass()); + processedDynamicHubs.add(hub); } } - queriedMethods = newQueriedMethods; - for (Executable method : reflectionMethods) { - if (!SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { - AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(method); + } + for (Field reflectField : reflectionFields) { + if (!registeredFields.contains(reflectField) && !SubstitutionReflectivityFilter.shouldExclude(reflectField, access.getMetaAccess(), access.getUniverse())) { + AnalysisField analysisField = access.getMetaAccess().lookupJavaField(reflectField); + registerTypesForField(access, analysisField, reflectField); + registeredFields.add(reflectField); + } + } + for (Executable method : reflectionMethods.keySet()) { + if (SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { + continue; + } + if (!registeredMethods.contains(method)) { + AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(method); + registerTypesForMethod(access, analysisMethod, method); + registerHidingSubTypeMethods(access, analysisMethod, analysisMethod.getDeclaringClass()); + registeredMethods.add(method); + } + if (reflectionMethods.get(method) == ExecutableAccessibility.Accessed) { + /* + * We must also generate the accessor for a method that was registered as queried + * and then registered again as accessed + */ + methodAccessors.putIfAbsent(method, ImageSingletons.lookup(ReflectionFeature.class).getOrCreateAccessor(method)); + } + } + for (AccessibleObject object : heapReflectionObjects) { + if (!processedHeapReflectionObjects.contains(object)) { + if (object instanceof Field) { + Field field = (Field) object; + AnalysisField analysisField = access.getMetaAccess().lookupJavaField(field); + registerTypesForField(access, analysisField, field); + } else if (object instanceof Executable) { + Executable executable = (Executable) object; + AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(executable); + registerTypesForMethod(access, analysisMethod, executable); registerHidingSubTypeMethods(access, analysisMethod, analysisMethod.getDeclaringClass()); } + processedHeapReflectionObjects.add(object); } } if (SubstrateOptions.IncludeMethodData.getValue()) { + for (AnalysisField field : access.getUniverse().getFields()) { + if (field.isAccessed()) { + registerTypesForReachableField(access, field); + } + } for (AnalysisMethod method : access.getUniverse().getMethods()) { if (method.isReachable() && !method.isIntrinsicMethod()) { registerTypesForReachableMethod(access, method); @@ -310,12 +316,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 +360,74 @@ private void registerHidingSubTypeMethods(DuringAnalysisAccessImpl access, Analy } } - private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); + private void registerTypesForClass(DuringAnalysisAccessImpl access, AnalysisType analysisType, Class clazz) { + List errors = new ArrayList<>(); + makeTypeReachable(access, query(clazz::getGenericSuperclass, errors)); + Type[] genericInterfaces = query(clazz::getGenericInterfaces, errors); + if (genericInterfaces != null) { + for (Type genericInterface : genericInterfaces) { + try { + makeTypeReachable(access, genericInterface); + } catch (TypeNotPresentException | LinkageError e) { + errors.add(e); + } + } + } + Executable enclosingMethod = enclosingMethodOrConstructor(clazz, errors); + if (enclosingMethod != null) { + makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(enclosingMethod.getDeclaringClass())); + RuntimeReflection.register(enclosingMethod); + } + reportLinkingErrors(clazz, errors); + + Object[] recordComponents = buildRecordComponents(clazz, access); + if (recordComponents != null) { + for (Object recordComponent : recordComponents) { + registerTypesForRecordComponent(access, recordComponent); + } + registeredRecordComponents.put(clazz, recordComponents); + } + for (Annotation annotation : GuardedAnnotationAccess.getDeclaredAnnotations(analysisType)) { + registerTypesForAnnotation(access, annotation); + } + for (TypeAnnotation typeAnnotation : getTypeAnnotations(clazz)) { + // Checkstyle: allow direct annotation access + registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access + } + } + + private void registerTypesForField(DuringAnalysisAccessImpl access, AnalysisField analysisField, Field reflectField) { + /* + * Reflection accessors use Unsafe, so ensure that all reflectively accessible fields are + * registered as unsafe-accessible, whether they have been explicitly registered or their + * Field object is reachable in the image heap. + */ + ImageSingletons.lookup(ReflectionFeature.class).inspectAccessibleField(reflectField); + + if (!analysisField.isUnsafeAccessed() && !GuardedAnnotationAccess.isAnnotationPresent(analysisField, InjectAccessors.class)) { + 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,39 +447,42 @@ private static void registerTypesForQueriedMethod(DuringAnalysisAccessImpl acces * Enable runtime instantiation of annotations */ for (Annotation annotation : GuardedAnnotationAccess.getDeclaredAnnotations(analysisMethod)) { - registerTypesForAnnotationValue(access, annotation.annotationType(), annotation); + registerTypesForAnnotation(access, annotation); } for (Annotation[] parameterAnnotations : reflectMethod.getParameterAnnotations()) { for (Annotation parameterAnnotation : parameterAnnotations) { - registerTypesForAnnotationValue(access, parameterAnnotation.annotationType(), parameterAnnotation); + registerTypesForAnnotation(access, parameterAnnotation); } } - try { - for (TypeAnnotation typeAnnotation : (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, reflectMethod)) { - // Checkstyle: allow direct annotation access - registerTypesForAnnotationValue(access, typeAnnotation.getAnnotation().annotationType(), typeAnnotation.getAnnotation()); - // Checkstyle: disallow direct annotation access - } - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); + for (TypeAnnotation typeAnnotation : getTypeAnnotations(reflectMethod)) { + // Checkstyle: allow direct annotation access + registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access } } + private static void registerTypesForReachableField(DuringAnalysisAccessImpl access, AnalysisField analysisField) { + makeAnalysisTypeReachable(access, analysisField.getDeclaringClass()); + } + private static void registerTypesForReachableMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod) { makeAnalysisTypeReachable(access, analysisMethod.getDeclaringClass()); for (JavaType paramType : analysisMethod.toParameterTypes()) { makeAnalysisTypeReachable(access, (AnalysisType) paramType); } + makeAnalysisTypeReachable(access, (AnalysisType) analysisMethod.getSignature().getReturnType(null)); } - 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) { + try { + if (type == null || processedTypes.contains(type)) { + return; + } + } catch (TypeNotPresentException e) { + /* Hash code computation can trigger an exception if the type is missing */ 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,15 +518,31 @@ 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 access, Annotation annotation) { + registerTypesForAnnotationValue(access, annotation.annotationType(), annotation); + } + @SuppressWarnings("unchecked") private static void registerTypesForAnnotationValue(DuringAnalysisAccessImpl access, Class type, Object value) { - if (type.isAnnotation()) { + if (type.isAnnotation() && !SubstitutionReflectivityFilter.shouldExclude(type, access.getMetaAccess(), access.getUniverse())) { makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(type)); /* * Parsing annotation data in reflection classes requires being able to instantiate all * annotation types at runtime. */ ImageSingletons.lookup(AnnotationTypeSupport.class).createInstance((Class) type); + ModuleSupport.openModuleByClass(type, ReflectionDataBuilder.class); ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(type); Annotation annotation = (Annotation) value; @@ -467,8 +555,21 @@ private static void registerTypesForAnnotationValue(DuringAnalysisAccessImpl acc getAnnotationValue.setAccessible(true); Object annotationValue = getAnnotationValue.invoke(annotation); registerTypesForAnnotationValue(access, valueType, annotationValue); - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); + } catch (IllegalAccessException | InvocationTargetException | InaccessibleObjectException e) { + // Ignore the value + Throwable exception = e instanceof InvocationTargetException ? ((InvocationTargetException) e).getTargetException() : e; + System.out.println("Warning: unable to register annotation value \"" + valueName + "\" for annotation type " + type + ". Reason: " + exception); + if (e instanceof InvocationTargetException) { + if (exception instanceof TypeNotPresentException) { + AnalysisType proxyType = access.getMetaAccess().lookupJavaType(TypeNotPresentExceptionProxy.class); + makeAnalysisTypeReachable(access, proxyType); + proxyType.registerAsInHeap(); + } else if (exception instanceof EnumConstantNotPresentException) { + AnalysisType proxyType = access.getMetaAccess().lookupJavaType(EnumConstantNotPresentExceptionProxy.class); + makeAnalysisTypeReachable(access, proxyType); + proxyType.registerAsInHeap(); + } + } } } } else if (type.isArray()) { @@ -514,64 +615,16 @@ private void processClass(DuringAnalysisAccessImpl access, Class clazz) { * build the reflection metadata. */ type.registerAsReachable(); - DynamicHub hub = access.getHostVM().dynamicHub(type); if (reflectionClasses.contains(clazz)) { ClassForNameSupport.registerClass(clazz); - } - - /* - * Trigger initialization of the fields of the original Class.ReflectionData object by - * calling the public methods. - * - * If one of the called methods throws a LinkageError because of a missing type or types - * that have incompatible changes, we skip registering that part of the reflection metadata - * for this class, but continue to try to register other parts of the reflection metadata. - * - * If the class fails verification then no reflection metadata can be registered. However, - * the class is still registered for run time loading with Class.forName() and its class - * initializer is replaced with a synthesized 'throw new VerifyError()' (see - * ClassInitializationFeature.buildRuntimeInitializationInfo()). - */ - List errors = new ArrayList<>(); - query(clazz::getDeclaredFields, errors); - query(clazz::getFields, errors); - query(clazz::getDeclaredMethods, errors); - query(clazz::getMethods, errors); - query(clazz::getDeclaredConstructors, errors); - query(clazz::getConstructors, errors); - Class[] declaredClasses = query(clazz::getDeclaredClasses, errors); - Class[] permittedClasses = SealedClassSupport.singleton().getPermittedSubclasses(clazz); - Class[] classes = query(clazz::getClasses, errors); - reportLinkingErrors(clazz, errors); - - Object originalReflectionData = accessors.getReflectionData(clazz); - DynamicHub.ReflectionData reflectionData; - if (type.isArray()) { - // Always register reflection data for array classes - reflectionData = arrayReflectionData; - } else { - reflectionData = DynamicHub.ReflectionData.get( - filterFields(accessors.getDeclaredFields(originalReflectionData), reflectionFields, access), - filterFields(accessors.getPublicFields(originalReflectionData), reflectionFields, access), - filterFields(accessors.getPublicFields(originalReflectionData), f -> reflectionFields.contains(f) && !isHiddenIn(f, clazz), access), - filterMethods(accessors.getDeclaredMethods(originalReflectionData), reflectionMethods, access), - filterMethods(accessors.getPublicMethods(originalReflectionData), reflectionMethods, access), - filterConstructors(accessors.getDeclaredConstructors(originalReflectionData), reflectionMethods, access), - filterConstructors(accessors.getPublicConstructors(originalReflectionData), reflectionMethods, access), - nullaryConstructor(accessors.getDeclaredConstructors(originalReflectionData), reflectionMethods, access), - filterFields(accessors.getDeclaredPublicFields(originalReflectionData), reflectionFields, access), - filterMethods(accessors.getDeclaredPublicMethods(originalReflectionData), reflectionMethods, access), - filterClasses(declaredClasses, reflectionClasses, access), - filterClasses(permittedClasses, reflectionClasses, access, true), - /* null is different from Class[0] here. */ - filterClasses(classes, reflectionClasses, access), - enclosingMethodOrConstructor(clazz), - buildRecordComponents(clazz, access)); - } - hub.setReflectionData(reflectionData); - access.rescanField(hub, dynamicHubReflectionDataField); + List errors = new ArrayList<>(); + if (query(clazz::getEnclosingClass, errors) != null) { + innerClasses.computeIfAbsent(access.getMetaAccess().lookupJavaType(clazz.getEnclosingClass()).getJavaClass(), (enclosingType) -> ConcurrentHashMap.newKeySet()).add(clazz); + } + reportLinkingErrors(clazz, errors); + } if (type.isAnnotation()) { /* @@ -579,8 +632,16 @@ private void processClass(DuringAnalysisAccessImpl access, Class clazz) { * their own reflection data */ Set members = new HashSet<>(); - Collections.addAll(members, filterFields(accessors.getDeclaredFields(originalReflectionData), reflectionFields, access)); - Collections.addAll(members, filterMethods(accessors.getDeclaredMethods(originalReflectionData), reflectionMethods, access)); + for (Field field : reflectionFields) { + if (field.getDeclaringClass().equals(clazz) && !SubstitutionReflectivityFilter.shouldExclude(field, access.getMetaAccess(), access.getUniverse())) { + members.add(field); + } + } + for (Executable executable : reflectionMethods.keySet()) { + if (executable.getDeclaringClass().equals(clazz) && !SubstitutionReflectivityFilter.shouldExclude(executable, access.getMetaAccess(), access.getUniverse())) { + members.add(executable); + } + } annotationMembers.put(clazz, members); access.requireAnalysisIteration(); /* Need the proxy class to see the added members */ } @@ -615,13 +676,12 @@ private Object[] buildRecordComponents(Class clazz, DuringAnalysisAccessImpl * DynamicHub.getRecordComponents0(). */ Method[] allMethods = support.getRecordComponentAccessorMethods(clazz); - Method[] filteredMethods = filterMethods(allMethods, reflectionMethods, access); - - if (allMethods.length == filteredMethods.length) { - return support.getRecordComponents(clazz); - } else { - return null; + for (Method method : allMethods) { + if (!reflectionMethods.containsKey(method) || SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { + return null; + } } + return support.getRecordComponents(clazz); } private static void reportLinkingErrors(Class clazz, List errors) { @@ -645,19 +705,7 @@ public boolean requiresProcessing() { return !modifiedClasses.isEmpty(); } - private static Constructor nullaryConstructor(Object constructors, Set reflectionMethods, DuringAnalysisAccessImpl access) { - if (constructors != null) { - for (Constructor constructor : (Constructor[]) constructors) { - if (constructor.getParameterCount() == 0 && reflectionMethods.contains(constructor) && - !SubstitutionReflectivityFilter.shouldExclude(constructor, access.getMetaAccess(), access.getUniverse())) { - return constructor; - } - } - } - return null; - } - - private Executable enclosingMethodOrConstructor(Class clazz) { + private Executable enclosingMethodOrConstructor(Class clazz, List errors) { Method enclosingMethod; Constructor enclosingConstructor; try { @@ -669,6 +717,9 @@ private Executable enclosingMethodOrConstructor(Class clazz) { * missing types or types that have incompatible changes a LinkageError is thrown. Skip * the class. */ + if (errors != null) { + errors.add(e); + } return null; } catch (InternalError ex) { /* @@ -677,6 +728,9 @@ private Executable enclosingMethodOrConstructor(Class clazz) { * synthetic method for a anonymous class declared inside a lambda. We skip registering * the enclosing method for such classes. */ + if (errors != null) { + errors.add(ex); + } return null; } @@ -689,89 +743,71 @@ 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); + @Override + public Map, Set>> getReflectionInnerClasses() { + assert sealed; + return Collections.unmodifiableMap(innerClasses); } - private static boolean isHiddenIn(Field field, Class clazz) { - try { - return !clazz.getField(field.getName()).equals(field); - } catch (NoSuchFieldException e) { - throw VMError.shouldNotReachHere(e); - } + @Override + public Set getReflectionFields() { + assert sealed; + return Collections.unmodifiableSet(registeredFields); } - 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 Set getReflectionExecutables() { + assert sealed; + return Collections.unmodifiableSet(registeredMethods); } - private static Constructor[] filterConstructors(Object methods, Set filter, DuringAnalysisAccessImpl access) { - return filterMethods(methods, filter, access, EMPTY_CONSTRUCTORS); + @Override + public Object getAccessor(Executable method) { + assert sealed; + return methodAccessors.get(method); } - private static Method[] filterMethods(Object methods, Set filter, DuringAnalysisAccessImpl access) { - return filterMethods(methods, filter, access, EMPTY_METHODS); + @Override + public Set getHidingReflectionMethods() { + assert sealed; + return Collections.unmodifiableSet(hidingMethods); } - @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[] getRecordComponents(Class type) { + assert sealed; + return registeredRecordComponents.get(type); } - private static Class[] filterClasses(Object classes, Set> filter, DuringAnalysisAccessImpl access) { - return filterClasses(classes, filter, access, false); + @Override + public void registerHeapDynamicHub(Object hub) { + assert !sealed; + heapDynamicHubs.add((DynamicHub) hub); } - 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 Set getHeapDynamicHubs() { + assert sealed; + return Collections.unmodifiableSet(heapDynamicHubs); } @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 +817,20 @@ 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); - } - } + return registeredFields.size(); + } - public Object getDeclaredPublicFields(Object obj) { - try { - return declaredPublicFieldsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } - } + private enum ExecutableAccessibility { + QueriedOnly, + Accessed; - public Object getDeclaredPublicMethods(Object obj) { - try { - return declaredPublicMethodsField.get(obj); - } catch (IllegalAccessException e) { - throw VMError.shouldNotReachHere(e); - } + static ExecutableAccessibility max(ExecutableAccessibility a, ExecutableAccessibility b) { + return a == Accessed || b == Accessed ? Accessed : QueriedOnly; } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java index fb5f0469ba4d..6f1d6303d536 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java @@ -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,8 +164,6 @@ public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; aUniverse = access.getUniverse(); - access.registerObjectReplacer(new ReflectionObjectReplacer(access.getMetaAccess())); - ReflectionConfigurationParser>> parser = ConfigurationParserUtils.create(reflectionData, access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, access.getImageClassLoader(), "reflection", ConfigurationFiles.Options.ReflectionConfigurationFiles, ConfigurationFiles.Options.ReflectionConfigurationResources, @@ -174,7 +171,6 @@ public void duringSetup(DuringSetupAccess a) { loader = access.getImageClassLoader(); annotationSubstitutions = ((Inflation) access.getBigBang()).getAnnotationSubstitutionProcessor(); - reflectionData.duringSetup(access); } @Override diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java new file mode 100644 index 000000000000..5c72a4163b17 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java @@ -0,0 +1,229 @@ +/* + * 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.ReflectionMetadataDecoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache.ReflectionMetadataEncoderFactory; +import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl; +import com.oracle.svm.reflect.target.ReflectionMetadataEncoding; + +import jdk.vm.ci.meta.JavaConstant; +import sun.reflect.annotation.TypeAnnotation; + +@AutomaticFeature +class ReflectionMetadataFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(ReflectionMetadataEncoderFactory.class, new ReflectionMetadataEncoderImpl.Factory()); + ImageSingletons.add(ReflectionMetadataDecoder.class, new ReflectionMetadataDecoderImpl()); + ImageSingletons.add(ReflectionMetadataEncoding.class, new ReflectionMetadataEncoding()); + } +} + +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 Object[] enclosingMethodInfo; + final RecordComponentMetadata[] recordComponents; + final HostedType[] permittedSubclasses; + + ClassMetadata(HostedType[] classes, Object[] enclosingMethodInfo, RecordComponentMetadata[] recordComponents, HostedType[] permittedSubclasses, Annotation[] annotations, + TypeAnnotation[] typeAnnotations) { + super(annotations, typeAnnotations); + this.classes = classes; + this.enclosingMethodInfo = enclosingMethodInfo; + this.recordComponents = recordComponents; + this.permittedSubclasses = permittedSubclasses; + } + } + + static class AccessibleObjectMetadata extends AnnotatedElementMetadata { + final boolean complete; + final JavaConstant heapObject; + final HostedType declaringType; + final int modifiers; + final String signature; + + AccessibleObjectMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, int modifiers, String signature, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + super(annotations, typeAnnotations); + this.complete = complete; + this.heapObject = heapObject; + this.declaringType = declaringType; + this.modifiers = modifiers; + this.signature = signature; + } + } + + static class FieldMetadata extends AccessibleObjectMetadata { + final String name; + final HostedType type; + final boolean trustedFinal; + final int offset; + final String deletedReason; + + FieldMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, + Annotation[] annotations, TypeAnnotation[] typeAnnotations, int offset, String deletedReason) { + super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations); + this.name = name; + this.type = type; + this.trustedFinal = trustedFinal; + this.offset = offset; + this.deletedReason = deletedReason; + } + + FieldMetadata(HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, Annotation[] annotations, TypeAnnotation[] typeAnnotations, + int offset, String deletedReason) { + this(true, null, declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason); + } + + FieldMetadata(JavaConstant heapObject, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + this(true, heapObject, null, null, null, 0, false, null, annotations, typeAnnotations, LOC_UNINITIALIZED, null); + } + + FieldMetadata(HostedType declaringType, String name) { + this(false, null, declaringType, name, null, 0, false, null, null, null, LOC_UNINITIALIZED, null); + } + } + + static class ExecutableMetadata extends AccessibleObjectMetadata { + final HostedType[] parameterTypes; + final HostedType[] exceptionTypes; + final Annotation[][] parameterAnnotations; + final ReflectParameterMetadata[] reflectParameters; + final JavaConstant accessor; + + ExecutableMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, + Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { + super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations); + this.parameterTypes = parameterTypes; + this.exceptionTypes = exceptionTypes; + this.parameterAnnotations = parameterAnnotations; + this.reflectParameters = reflectParameters; + this.accessor = accessor; + } + } + + static class MethodMetadata extends ExecutableMetadata { + final boolean hiding; + final String name; + final HostedType returnType; + final Object annotationDefault; + + MethodMetadata(boolean complete, boolean hiding, JavaConstant heapObject, HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, + HostedType[] exceptionTypes, String signature, Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, + ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { + super(complete, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor); + this.hiding = hiding; + this.name = name; + this.returnType = returnType; + this.annotationDefault = annotationDefault; + } + + MethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, HostedType[] exceptionTypes, String signature, + Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, + JavaConstant accessor) { + this(true, false, null, declaringClass, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, parameterAnnotations, annotationDefault, typeAnnotations, + reflectParameters, + accessor); + } + + MethodMetadata(JavaConstant heapObject, Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, + ReflectParameterMetadata[] reflectParameters) { + this(true, false, heapObject, null, null, null, 0, null, null, null, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, null); + } + + MethodMetadata(boolean hiding, HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType) { + this(false, hiding, null, declaringClass, name, parameterTypes, modifiers, returnType, null, null, null, null, null, null, null, null); + } + } + + static class ConstructorMetadata extends ExecutableMetadata { + + ConstructorMetadata(boolean complete, JavaConstant heapObject, HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, + Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { + super(complete, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor); + } + + ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, Annotation[] annotations, + Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { + this(true, null, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor); + } + + ConstructorMetadata(JavaConstant heapObject, Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters) { + this(true, heapObject, null, null, 0, null, null, annotations, parameterAnnotations, typeAnnotations, reflectParameters, null); + } + + ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes, int modifiers) { + this(false, null, declaringClass, parameterTypes, modifiers, null, null, null, null, null, null, null); + } + } + + static class RecordComponentMetadata extends AnnotatedElementMetadata { + final HostedType declaringType; + final String name; + final HostedType type; + final String signature; + final JavaConstant accessor; + + RecordComponentMetadata(HostedType declaringType, String name, HostedType type, String signature, JavaConstant accessor, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + super(annotations, typeAnnotations); + this.declaringType = declaringType; + this.name = name; + this.type = type; + this.signature = signature; + this.accessor = accessor; + } + } + + static class ReflectParameterMetadata { + final String name; + final int modifiers; + + ReflectParameterMetadata(String name, int modifiers) { + this.name = name; + this.modifiers = modifiers; + } + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java new file mode 100644 index 000000000000..1591d0bcf51c --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java @@ -0,0 +1,1338 @@ +/* + * 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.reflect.ReflectionMetadataDecoder.NO_DATA; +import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.COMPLETE_FLAG_MASK; +import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.HIDING_FLAG_MASK; +import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.IN_HEAP_FLAG_MASK; +import static com.oracle.svm.reflect.target.ReflectionMetadataDecoderImpl.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; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.graalvm.collections.Pair; +import org.graalvm.compiler.core.common.util.TypeConversion; +import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter; +import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; +import org.graalvm.util.GuardedAnnotationAccess; + +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.svm.core.annotate.Delete; +import com.oracle.svm.core.code.CodeInfoEncoder; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.meta.SharedField; +import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; +import com.oracle.svm.core.util.ByteArrayReader; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.image.NativeImageCodeCache.ReflectionMetadataEncoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache.ReflectionMetadataEncoderFactory; +import com.oracle.svm.hosted.meta.HostedField; +import com.oracle.svm.hosted.meta.HostedMetaAccess; +import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.meta.HostedType; +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.ReflectionMetadataDecoderImpl; +import com.oracle.svm.reflect.target.ReflectionMetadataEncoding; +import com.oracle.svm.reflect.target.Target_sun_reflect_annotation_AnnotationParser; +import com.oracle.svm.util.ReflectionUtil; + +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.MetaAccessProvider; +import sun.invoke.util.Wrapper; +import sun.reflect.annotation.AnnotationType; +import sun.reflect.annotation.EnumConstantNotPresentExceptionProxy; +import sun.reflect.annotation.ExceptionProxy; +import sun.reflect.annotation.TypeAnnotation; +import sun.reflect.annotation.TypeAnnotationParser; +import sun.reflect.annotation.TypeNotPresentExceptionProxy; + +/** + * The reflection metadata encoder creates metadata for reflection objects (classes, fields, methods + * and constructors), as well as for annotations and other objects queried from the VM to enable + * their creation at runtime. The metadata related to classes is saved to a single byte array (see + * {@link ReflectionMetadataEncoding}), with the index into this array saved in the corresponding + * {@link DynamicHub}. The metadata for reflection objects already present in the image heap is + * encoded directly as byte arrays into the corresponding object through field recomputations. + * + * Method, field and constructor metadata can be complete, meaning it can be used to recreate a + * working object of the corresponding type, or not, meaning it only holds the element's signature. + * The incomplete form is used to implement hiding methods, which are methods that are not + * registered for reflection but override a registered method, and as such should be seen by the JDK + * code determining which superclass methods to include in a certain class (see + * {@link Class#getMethods()}). + * + * Emitting the metadata happens in two phases. In the first phase, the string and class encoders + * are filled with the necessary values (in the {@code #add*Metadata} functions). In a second phase, + * the values are encoded into their intended byte arrays (see {@link #encodeAllAndInstall()}). + * + * The metadata encoding format is detailed in {@link ReflectionMetadataDecoderImpl}. + */ +public class ReflectionMetadataEncoderImpl implements ReflectionMetadataEncoder { + + static class Factory implements ReflectionMetadataEncoderFactory { + @Override + public ReflectionMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { + return new ReflectionMetadataEncoderImpl(encoders); + } + } + + private final CodeInfoEncoder.Encoders encoders; + private final ReflectionDataAccessors accessors; + private final Map, JavaConstant> annotationExceptionProxies = new HashMap<>(); + private final TreeSet sortedTypes = new TreeSet<>(Comparator.comparingLong(t -> t.getHub().getTypeID())); + private final Map classData = new HashMap<>(); + private final Map> fieldData = new HashMap<>(); + private final Map> methodData = new HashMap<>(); + private final Map> constructorData = new HashMap<>(); + + private final Set heapData = new HashSet<>(); + + 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 ReflectionMetadataEncoderImpl(CodeInfoEncoder.Encoders encoders) { + this.encoders = encoders; + this.accessors = new ReflectionDataAccessors(); + } + + private void registerClass(HostedType type, ClassMetadata metadata) { + sortedTypes.add(type); + classData.put(type, metadata); + } + + private void registerField(HostedType declaringType, Object field, FieldMetadata metadata) { + sortedTypes.add(declaringType); + FieldMetadata oldData = fieldData.computeIfAbsent(declaringType, t -> new HashMap<>()).put(field, metadata); + assert oldData == null; + } + + private FieldMetadata[] getFields(HostedType declaringType) { + Field[] jdkFields = accessors.getDeclaredFields(declaringType.getJavaClass()); + Map fieldMetadata = fieldData.getOrDefault(declaringType, Collections.emptyMap()); + return sortElements(jdkFields, fieldMetadata).toArray(new FieldMetadata[0]); + } + + private void registerMethod(HostedType declaringType, Object method, MethodMetadata metadata) { + sortedTypes.add(declaringType); + MethodMetadata oldData = methodData.computeIfAbsent(declaringType, t -> new HashMap<>()).put(method, metadata); + assert oldData == null; + } + + private MethodMetadata[] getMethods(HostedType declaringType) { + Method[] jdkMethods = accessors.getDeclaredMethods(declaringType.getJavaClass()); + Map methodMetadata = methodData.getOrDefault(declaringType, Collections.emptyMap()); + return sortElements(jdkMethods, methodMetadata).toArray(new MethodMetadata[0]); + } + + private void registerConstructor(HostedType declaringType, Object constructor, ConstructorMetadata metadata) { + sortedTypes.add(declaringType); + ConstructorMetadata oldData = constructorData.computeIfAbsent(declaringType, t -> new HashMap<>()).put(constructor, metadata); + assert oldData == null; + } + + private ConstructorMetadata[] getConstructors(HostedType declaringType) { + Constructor[] jdkConstructors = accessors.getDeclaredConstructors(declaringType.getJavaClass()); + Map constructorMetadata = constructorData.getOrDefault(declaringType, Collections.emptyMap()); + return sortElements(jdkConstructors, constructorMetadata).toArray(new ConstructorMetadata[0]); + } + + /* Sort elements in the same order as the JCK */ + private static List sortElements(T[] jdkElements, Map metadata) { + List orderedElements = new ArrayList<>(); + List trailingElements = new ArrayList<>(); + for (T element : jdkElements) { + if (metadata.containsKey(element)) { + M elementMetadata = metadata.remove(element); + if (element instanceof Method && ((Method) element).isBridge()) { + trailingElements.add(elementMetadata); + } else { + orderedElements.add(elementMetadata); + } + } + } + /* Add non-reflection metadata to the end of the list */ + orderedElements.addAll(metadata.values()); + orderedElements.addAll(trailingElements); + return orderedElements; + } + + @Override + public byte[] getAnnotationsEncoding(AccessibleObject object) { + return annotationsEncodings.get(object); + } + + @Override + public byte[] getParameterAnnotationsEncoding(Executable object) { + return parameterAnnotationsEncodings.get(object); + } + + @Override + public byte[] getAnnotationDefaultEncoding(Method object) { + return annotationDefaultEncodings.get(object); + } + + @Override + public byte[] getTypeAnnotationsEncoding(AccessibleObject object) { + return typeAnnotationsEncodings.get(object); + } + + @Override + public byte[] getReflectParametersEncoding(Executable object) { + return reflectParametersEncodings.get(object); + } + + @Override + public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Class[] innerClasses) { + Class javaClass = type.getHub().getHostedJavaClass(); + Object[] enclosingMethodInfo = getEnclosingMethodInfo(javaClass); + if (enclosingMethodInfo != null) { + enclosingMethodInfo[0] = ((HostedMetaAccess) metaAccess).lookupJavaType((Class) enclosingMethodInfo[0]); + } + RecordComponentMetadata[] recordComponents = getRecordComponents(metaAccess, type, javaClass); + Class[] permittedSubclasses = getPermittedSubclasses(metaAccess, javaClass); + Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(type); + TypeAnnotation[] typeAnnotations = getTypeAnnotations(javaClass); + + /* Register string and class values in annotations */ + encoders.sourceClasses.addObject(javaClass); + if (enclosingMethodInfo != null) { + encoders.sourceClasses.addObject(((HostedType) enclosingMethodInfo[0]).getJavaClass()); + encoders.sourceMethodNames.addObject((String) enclosingMethodInfo[1]); + encoders.sourceMethodNames.addObject((String) enclosingMethodInfo[2]); + } + HostedType[] innerTypes = registerClassValues(metaAccess, innerClasses); + HostedType[] permittedSubtypes = (permittedSubclasses != null) ? registerClassValues(metaAccess, permittedSubclasses) : null; + annotations = registerAnnotationValues(metaAccess, annotations); + typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + + registerClass(type, new ClassMetadata(innerTypes, enclosingMethodInfo, recordComponents, permittedSubtypes, annotations, + typeAnnotations)); + } + + private static final Method getEnclosingMethodInfo = ReflectionUtil.lookupMethod(Class.class, "getEnclosingMethod0"); + + private static Object[] getEnclosingMethodInfo(Class clazz) { + try { + return (Object[]) getEnclosingMethodInfo.invoke(clazz); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static final Method getPermittedSubclasses = ReflectionUtil.lookupMethod(true, Class.class, "getPermittedSubclasses"); + + private static Class[] getPermittedSubclasses(MetaAccessProvider metaAccess, Class clazz) { + if (JavaVersionUtil.JAVA_SPEC < 17) { + return null; + } + try { + Class[] permittedSubclasses = (Class[]) getPermittedSubclasses.invoke(clazz); + if (permittedSubclasses == null) { + return null; + } + Set> reachablePermittedSubclasses = new HashSet<>(); + for (Class permittedSubclass : permittedSubclasses) { + HostedType hostedType = ((HostedMetaAccess) metaAccess).optionalLookupJavaType(permittedSubclass).orElse(null); + if (hostedType != null && hostedType.getWrapped().isReachable()) { + reachablePermittedSubclasses.add(permittedSubclass); + } + } + return reachablePermittedSubclasses.toArray(new Class[0]); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + @Override + public void 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, reflectField, new FieldMetadata(declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason)); + } + + @Override + public void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, HostedMethod hostedMethod, Executable reflectMethod, Object accessor) { + boolean isMethod = !hostedMethod.isConstructor(); + HostedType declaringType = hostedMethod.getDeclaringClass(); + String name = isMethod ? hostedMethod.getName() : null; + HostedType[] parameterTypes = getParameterTypes(hostedMethod); + /* Reflect method because substitution of Object.hashCode() is private */ + int modifiers = reflectMethod.getModifiers(); + HostedType returnType = (HostedType) hostedMethod.getSignature().getReturnType(null); + HostedType[] exceptionTypes = getExceptionTypes(metaAccess, reflectMethod); + String signature = getSignature(reflectMethod); + Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(hostedMethod); + Annotation[][] parameterAnnotations = reflectMethod.getParameterAnnotations(); + Object annotationDefault = isMethod ? ((Method) reflectMethod).getDefaultValue() : null; + TypeAnnotation[] typeAnnotations = getTypeAnnotations(reflectMethod); + ReflectParameterMetadata[] reflectParameters = getReflectParameters(reflectMethod); + + /* Fill encoders with the necessary values. */ + if (isMethod) { + encoders.sourceMethodNames.addObject(name); + encoders.sourceClasses.addObject(returnType.getJavaClass()); + } + for (HostedType parameterType : parameterTypes) { + encoders.sourceClasses.addObject(parameterType.getJavaClass()); + } + for (HostedType exceptionType : exceptionTypes) { + encoders.sourceClasses.addObject(exceptionType.getJavaClass()); + } + encoders.sourceMethodNames.addObject(signature); + /* Register string and class values in annotations */ + annotations = registerAnnotationValues(metaAccess, annotations); + for (int i = 0; i < parameterAnnotations.length; ++i) { + parameterAnnotations[i] = registerAnnotationValues(metaAccess, parameterAnnotations[i]); + } + if (isMethod && annotationDefault != null) { + registerAnnotationValue(annotationDefault.getClass(), annotationDefault); + } + typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + if (reflectParameters != null) { + for (ReflectParameterMetadata parameter : reflectParameters) { + encoders.sourceMethodNames.addObject(parameter.name); + } + } + JavaConstant accessorConstant = null; + if (accessor != null) { + accessorConstant = SubstrateObjectConstant.forObject(accessor); + encoders.objectConstants.addObject(accessorConstant); + } + + if (isMethod) { + registerMethod(declaringType, reflectMethod, new MethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, + parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, accessorConstant)); + } else { + registerConstructor(declaringType, reflectMethod, new ConstructorMetadata(declaringType, parameterTypes, modifiers, exceptionTypes, signature, annotations, + parameterAnnotations, typeAnnotations, reflectParameters, accessorConstant)); + } + } + + private static final Method isFieldTrustedFinal = ReflectionUtil.lookupMethod(true, Field.class, "isTrustedFinal"); + + private static boolean isTrustedFinal(Field field) { + if (JavaVersionUtil.JAVA_SPEC < 17) { + return false; + } + try { + return (boolean) isFieldTrustedFinal.invoke(field); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + @Override + public void addHeapAccessibleObjectMetadata(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); + } + } + } + AccessibleObject holder = getHolder(object); + JavaConstant heapObjectConstant = SubstrateObjectConstant.forObject(holder); + encoders.objectConstants.addObject(heapObjectConstant); + + AccessibleObjectMetadata metadata; + if (isMethod) { + metadata = new MethodMetadata(heapObjectConstant, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters); + registerMethod((HostedType) metaAccess.lookupJavaType(((Method) object).getDeclaringClass()), holder, (MethodMetadata) metadata); + } else if (isExecutable) { + metadata = new ConstructorMetadata(heapObjectConstant, annotations, parameterAnnotations, typeAnnotations, reflectParameters); + registerConstructor((HostedType) metaAccess.lookupJavaType(((Constructor) object).getDeclaringClass()), holder, (ConstructorMetadata) metadata); + } else { + metadata = new FieldMetadata(heapObjectConstant, annotations, typeAnnotations); + registerField((HostedType) metaAccess.lookupJavaType(((Field) object).getDeclaringClass()), holder, (FieldMetadata) metadata); + } + heapData.add(metadata); + } + + private static final Method getRoot = ReflectionUtil.lookupMethod(AccessibleObject.class, "getRoot"); + + private static AccessibleObject getHolder(AccessibleObject accessibleObject) { + try { + AccessibleObject root = (AccessibleObject) getRoot.invoke(accessibleObject); + return root == null ? accessibleObject : root; + } catch (InvocationTargetException | IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); + + static TypeAnnotation[] getTypeAnnotations(AnnotatedElement annotatedElement) { + try { + return (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, annotatedElement); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private HostedType[] registerClassValues(MetaAccessProvider metaAccess, Class[] classes) { + Set includedClasses = new HashSet<>(); + for (Class clazz : classes) { + HostedType type; + try { + type = ((HostedMetaAccess) metaAccess).optionalLookupJavaType(clazz).orElse(null); + } catch (DeletedElementException e) { + type = null; + } + if (type != null && type.getWrapped().isReachable()) { + encoders.sourceClasses.addObject(type.getJavaClass()); + includedClasses.add(type); + } + } + return includedClasses.toArray(new HostedType[0]); + } + + private 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") + private void registerAnnotationValue(Class type, Object value) { + if (type.isAnnotation()) { + Annotation annotation = (Annotation) value; + AnnotationType annotationType = AnnotationType.getInstance((Class) type); + encoders.sourceClasses.addObject(type); + for (Map.Entry> entry : annotationType.memberTypes().entrySet()) { + String valueName = entry.getKey(); + Class valueType = entry.getValue(); + encoders.sourceMethodNames.addObject(valueName); + Method getAnnotationValue = annotationType.members().get(valueName); + getAnnotationValue.setAccessible(true); + Object annotationValue; + try { + annotationValue = getAnnotationValue.invoke(annotation); + registerAnnotationValue(valueType, annotationValue); + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + ExceptionProxy exceptionProxy; + if (targetException instanceof TypeNotPresentException) { + exceptionProxy = new TypeNotPresentExceptionProxy(((TypeNotPresentException) targetException).typeName(), targetException.getCause()); + } else if (targetException instanceof EnumConstantNotPresentException) { + EnumConstantNotPresentException enumException = (EnumConstantNotPresentException) targetException; + exceptionProxy = new EnumConstantNotPresentExceptionProxy((Class>) enumException.enumType(), enumException.constantName()); + } else { + throw GraalError.shouldNotReachHere(e); + } + JavaConstant javaConstant = annotationExceptionProxies.computeIfAbsent(Pair.create(annotation, valueName), (ignored) -> SubstrateObjectConstant.forObject(exceptionProxy)); + encoders.objectConstants.addObject(javaConstant); + } + } + } else if (type.isArray()) { + Class componentType = type.getComponentType(); + if (!componentType.isPrimitive()) { + for (Object val : (Object[]) value) { + registerAnnotationValue(componentType, val); + } + } + } else if (type == Class.class) { + encoders.sourceClasses.addObject((Class) value); + } else if (type == String.class) { + encoders.sourceMethodNames.addObject((String) value); + } else if (type.isEnum()) { + encoders.sourceClasses.addObject(type); + encoders.sourceMethodNames.addObject(((Enum) value).name()); + } + } + + @Override + public void addHidingMethodMetadata(AnalysisMethod analysisMethod, HostedType declaringType, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType) { + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + for (HostedType parameterType : parameterTypes) { + encoders.sourceClasses.addObject(parameterType.getJavaClass()); + } + encoders.sourceClasses.addObject(returnType.getJavaClass()); + + sortedTypes.add(declaringType); + registerMethod(declaringType, analysisMethod, new MethodMetadata(true, declaringType, name, parameterTypes, modifiers, returnType)); + } + + @Override + public void addReachableFieldMetadata(HostedField field) { + HostedType declaringType = field.getDeclaringClass(); + String name = field.getName(); + + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + + registerField(declaringType, field, new FieldMetadata(declaringType, name)); + } + + @Override + public void addReachableExecutableMetadata(HostedMethod executable) { + boolean isMethod = !executable.isConstructor(); + HostedType declaringType = executable.getDeclaringClass(); + String name = isMethod ? executable.getName() : null; + HostedType[] parameterTypes = getParameterTypes(executable); + HostedType returnType = (HostedType) executable.getSignature().getReturnType(null); + int modifiers = executable.getModifiers(); + + /* Fill encoders with the necessary values. */ + if (isMethod) { + encoders.sourceMethodNames.addObject(name); + } + for (HostedType parameterType : parameterTypes) { + encoders.sourceClasses.addObject(parameterType.getJavaClass()); + } + encoders.sourceClasses.addObject(returnType.getJavaClass()); + + if (isMethod) { + registerMethod(declaringType, executable, new MethodMetadata(false, declaringType, name, parameterTypes, modifiers, returnType)); + } else { + registerConstructor(declaringType, executable, new ConstructorMetadata(declaringType, parameterTypes, modifiers)); + } + } + + private static HostedType[] getParameterTypes(HostedMethod method) { + HostedType[] parameterTypes = new HostedType[method.getSignature().getParameterCount(false)]; + for (int i = 0; i < parameterTypes.length; ++i) { + parameterTypes[i] = (HostedType) method.getSignature().getParameterType(i, null); + } + return parameterTypes; + } + + private static HostedType[] getExceptionTypes(MetaAccessProvider metaAccess, Executable reflectMethod) { + Class[] exceptionClasses = reflectMethod.getExceptionTypes(); + HostedType[] exceptionTypes = new HostedType[exceptionClasses.length]; + for (int i = 0; i < exceptionClasses.length; ++i) { + exceptionTypes[i] = (HostedType) metaAccess.lookupJavaType(exceptionClasses[i]); + } + return exceptionTypes; + } + + 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 reflectParameters; + } + + private RecordComponentMetadata[] getRecordComponents(MetaAccessProvider metaAccess, HostedType declaringType, Class clazz) { + Object[] recordComponents = ImageSingletons.lookup(RuntimeReflectionSupport.class).getRecordComponents(clazz); + if (recordComponents == null) { + return null; + } + Set metadata = new HashSet<>(); + for (Object recordComponent : recordComponents) { + String name = getRecordComponentName(recordComponent); + HostedType type = (HostedType) metaAccess.lookupJavaType(getRecordComponentType(recordComponent)); + String signature = getRecordComponentSignature(recordComponent); + Method accessor = getRecordComponentAccessor(recordComponent); + Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations((AnnotatedElement) recordComponent); + TypeAnnotation[] typeAnnotations = getTypeAnnotations((AnnotatedElement) recordComponent); + + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + encoders.sourceClasses.addObject(type.getJavaClass()); + encoders.sourceMethodNames.addObject(signature); + /* Register string and class values in annotations */ + annotations = registerAnnotationValues(metaAccess, annotations); + typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + JavaConstant accessorConstant = null; + if (accessor != null) { + accessorConstant = SubstrateObjectConstant.forObject(accessor); + encoders.objectConstants.addObject(accessorConstant); + } + + metadata.add(new RecordComponentMetadata(declaringType, name, type, signature, accessorConstant, annotations, typeAnnotations)); + } + return metadata.toArray(new RecordComponentMetadata[0]); + } + + private static final Class recordComponentClass; + + static { + try { + recordComponentClass = (JavaVersionUtil.JAVA_SPEC >= 17) ? Class.forName("java.lang.reflect.RecordComponent") : null; + } catch (ClassNotFoundException e) { + throw VMError.shouldNotReachHere(e); + } + } + + private static final Method getRecordComponentName = (JavaVersionUtil.JAVA_SPEC >= 17) ? ReflectionUtil.lookupMethod(recordComponentClass, "getName") : null; + private static final Method getRecordComponentType = (JavaVersionUtil.JAVA_SPEC >= 17) ? ReflectionUtil.lookupMethod(recordComponentClass, "getType") : null; + private static final Method getRecordComponentSignature = (JavaVersionUtil.JAVA_SPEC >= 17) ? ReflectionUtil.lookupMethod(recordComponentClass, "getGenericSignature") : null; + private static final Method getRecordComponentAccessor = (JavaVersionUtil.JAVA_SPEC >= 17) ? ReflectionUtil.lookupMethod(recordComponentClass, "getAccessor") : null; + + private static String getRecordComponentName(Object recordComponent) { + try { + return (String) getRecordComponentName.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static Class getRecordComponentType(Object recordComponent) { + try { + return (Class) getRecordComponentType.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static String getRecordComponentSignature(Object recordComponent) { + try { + return (String) getRecordComponentSignature.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static Method getRecordComponentAccessor(Object recordComponent) { + try { + return (Method) getRecordComponentAccessor.invoke(recordComponent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + /** + * See {@link ReflectionMetadataDecoderImpl} for the encoding format description. + */ + @Override + public void encodeAllAndInstall() { + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + int typesIndex = encodeAndAddCollection(buf, sortedTypes.toArray(new HostedType[0]), this::encodeType, false); + assert typesIndex == 0; + for (HostedType declaringType : sortedTypes) { + DynamicHub hub = declaringType.getHub(); + ClassMetadata classMetadata = classData.get(declaringType); + + int enclosingMethodInfoIndex = encodeAndAddElement(buf, classMetadata.enclosingMethodInfo, this::encodeEnclosingMethod); + int annotationsIndex = encodeAndAddEncodedElement(buf, classMetadata.annotations, this::encodeAnnotations); + int typeAnnotationsIndex = encodeAndAddEncodedElement(buf, classMetadata.typeAnnotations, this::encodeTypeAnnotations); + int classesEncodingIndex = encodeAndAddCollection(buf, classMetadata.classes, this::encodeType, false); + int permittedSubclassesIndex = JavaVersionUtil.JAVA_SPEC >= 17 ? encodeAndAddCollection(buf, classMetadata.permittedSubclasses, this::encodeType, true) : NO_DATA; + if (anySet(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesIndex)) { + hub.setHubMetadata(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesIndex); + } + + int fieldsIndex = encodeAndAddCollection(buf, getFields(declaringType), this::encodeField, false); + int methodsIndex = encodeAndAddCollection(buf, getMethods(declaringType), this::encodeExecutable, false); + int constructorsIndex = encodeAndAddCollection(buf, getConstructors(declaringType), this::encodeExecutable, false); + int recordComponentsIndex = JavaVersionUtil.JAVA_SPEC >= 17 ? encodeAndAddCollection(buf, classMetadata.recordComponents, this::encodeRecordComponent, true) : NO_DATA; + if (anySet(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex)) { + hub.setReflectionMetadata(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex); + } + } + for (AccessibleObjectMetadata metadata : heapData) { + AccessibleObject heapObject = (AccessibleObject) SubstrateObjectConstant.asObject(metadata.heapObject); + annotationsEncodings.put(heapObject, encodeAnnotations(metadata.annotations)); + typeAnnotationsEncodings.put(heapObject, encodeTypeAnnotations(metadata.typeAnnotations)); + if (metadata instanceof ExecutableMetadata) { + parameterAnnotationsEncodings.put((Executable) heapObject, encodeParameterAnnotations(((ExecutableMetadata) metadata).parameterAnnotations)); + if (((ExecutableMetadata) metadata).reflectParameters != null) { + reflectParametersEncodings.put((Executable) heapObject, encodeReflectParameters(((ExecutableMetadata) metadata).reflectParameters)); + } + if (metadata instanceof MethodMetadata && ((Method) SubstrateObjectConstant.asObject(metadata.heapObject)).getDeclaringClass().isAnnotation() && + ((MethodMetadata) metadata).annotationDefault != null) { + annotationDefaultEncodings.put((Method) heapObject, encodeMemberValue(((MethodMetadata) metadata).annotationDefault)); + } + } + } + install(buf); + /* Enable field recomputers in reflection objects to see the computed values */ + ImageSingletons.add(ReflectionMetadataEncoder.class, this); + } + + private static int encodeAndAddCollection(UnsafeArrayTypeWriter buf, T[] data, BiConsumer encodeCallback, boolean canBeNull) { + if (data == null || (!canBeNull && data.length == 0)) { + /* + * We must encode a zero-length array if it does not have the same meaning as a null + * array (e.g. for permitted classes) + */ + return NO_DATA; + } + int offset = TypeConversion.asS4(buf.getBytesWritten()); + encodeArray(buf, data, element -> encodeCallback.accept(buf, element)); + return offset; + } + + private static int encodeAndAddElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback) { + byte[] encoding = encodeCallback.apply(data); + if (encoding == null) { + return NO_DATA; + } + int offset = TypeConversion.asS4(buf.getBytesWritten()); + encodeBytes(buf, encoding); + return offset; + } + + private static int encodeAndAddEncodedElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback) { + byte[] encoding = encodeCallback.apply(data); + if (encoding == null) { + return NO_DATA; + } + int offset = TypeConversion.asS4(buf.getBytesWritten()); + encodeByteArray(buf, encoding); + return offset; + } + + private static void install(UnsafeArrayTypeWriter encodingBuffer) { + int encodingSize = TypeConversion.asS4(encodingBuffer.getBytesWritten()); + byte[] dataEncoding = new byte[encodingSize]; + ImageSingletons.lookup(ReflectionMetadataEncoding.class).setEncoding(encodingBuffer.toArray(dataEncoding)); + } + + private static boolean anySet(int... indices) { + for (int index : indices) { + if (index != NO_DATA) { + return true; + } + } + return false; + } + + private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) { + /* Make sure we do not overwrite actual modifiers with our flags */ + assert (field.modifiers & COMPLETE_FLAG_MASK) == 0 && (field.modifiers & IN_HEAP_FLAG_MASK) == 0; + int modifiers = field.modifiers; + modifiers |= field.complete ? COMPLETE_FLAG_MASK : 0; + modifiers |= field.heapObject != null ? IN_HEAP_FLAG_MASK : 0; + buf.putUV(modifiers); + if (field.heapObject != null) { + encodeObject(buf, field.heapObject); + } else { + encodeName(buf, field.name); + if (field.complete) { + encodeType(buf, field.type); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + buf.putU1(field.trustedFinal ? 1 : 0); + } + encodeName(buf, field.signature); + encodeByteArray(buf, encodeAnnotations(field.annotations)); + encodeByteArray(buf, encodeTypeAnnotations(field.typeAnnotations)); + buf.putSV(field.offset); + encodeName(buf, field.deletedReason); + } + } + } + + private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata executable) { + boolean isMethod = executable instanceof MethodMetadata; + /* Make sure we do not overwrite actual modifiers with our flags */ + assert (executable.modifiers & COMPLETE_FLAG_MASK) == 0 && (executable.modifiers & IN_HEAP_FLAG_MASK) == 0 && (executable.modifiers & HIDING_FLAG_MASK) == 0; + int modifiers = executable.modifiers; + modifiers |= executable.complete ? COMPLETE_FLAG_MASK : 0; + modifiers |= executable.heapObject != null ? IN_HEAP_FLAG_MASK : 0; + modifiers |= isMethod && ((MethodMetadata) executable).hiding ? HIDING_FLAG_MASK : 0; + buf.putUV(modifiers); + if (executable.heapObject != null) { + encodeObject(buf, executable.heapObject); + } else { + if (isMethod) { + encodeName(buf, ((MethodMetadata) executable).name); + } + encodeArray(buf, executable.parameterTypes, parameterType -> encodeType(buf, parameterType)); + if (isMethod) { + encodeType(buf, ((MethodMetadata) executable).returnType); + } + if (executable.complete) { + encodeArray(buf, executable.exceptionTypes, exceptionType -> encodeType(buf, exceptionType)); + encodeName(buf, executable.signature); + encodeByteArray(buf, encodeAnnotations(executable.annotations)); + encodeByteArray(buf, encodeParameterAnnotations(executable.parameterAnnotations)); + if (isMethod && executable.declaringType.getHub().getHostedJavaClass().isAnnotation()) { + encodeByteArray(buf, encodeMemberValue(((MethodMetadata) executable).annotationDefault)); + } else { + assert !isMethod || ((MethodMetadata) executable).annotationDefault == null; + } + encodeByteArray(buf, encodeTypeAnnotations(executable.typeAnnotations)); + encodeByteArray(buf, encodeReflectParameters(executable.reflectParameters)); + encodeObject(buf, executable.accessor); + } + } + } + + private void encodeType(UnsafeArrayTypeWriter buf, HostedType type) { + buf.putSV(encoders.sourceClasses.getIndex(type.getJavaClass())); + } + + 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) { + buf.putUV(array.length); + for (T elem : array) { + elementEncoder.accept(elem); + } + } + + private static void encodeByteArray(UnsafeArrayTypeWriter buf, byte[] array) { + if (array == null) { + buf.putUV(NO_DATA); + return; + } + buf.putUV(array.length); + 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(e); + } + } + + private static String getSignature(Executable executable) { + try { + return (String) (executable instanceof Method ? getMethodSignature.invoke(executable) : getConstructorSignature.invoke(executable)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private static Parameter[] getRawParameters(Executable executable) { + try { + return (Parameter[]) getExecutableParameters.invoke(executable); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + /** + * 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 + * format we use differs from that one on a few points, based on the fact that the JDK encoding + * is based on constant pool indices, which are not available in that form at runtime. + * + * Class and String values are represented by their index in the source metadata encoders + * instead of their constant pool indices. Additionally, Class objects are encoded directly + * instead of through their type signature. Primitive values are written directly into the + * encoding. This means that our encoding can be of a different length from the JDK one. + * + * We use a modified version of the ConstantPool and AnnotationParser classes to decode the + * data, since those are not used in their original functions at runtime. (see + * {@link Target_jdk_internal_reflect_ConstantPool} and + * {@link Target_sun_reflect_annotation_AnnotationParser}) + */ + public byte[] encodeAnnotations(Annotation[] annotations) { + if (annotations.length == 0) { + return null; + } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); + buf.putU2(annotations.length); + for (Annotation annotation : annotations) { + encodeAnnotation(buf, annotation); + } + return buf.toArray(); + } + + private byte[] encodeParameterAnnotations(Annotation[][] annotations) { + if (!hasAnnotation(annotations)) { + return null; + } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); + buf.putU1(annotations.length); + for (Annotation[] parameterAnnotations : annotations) { + buf.putU2(parameterAnnotations.length); + for (Annotation parameterAnnotation : parameterAnnotations) { + encodeAnnotation(buf, parameterAnnotation); + } + } + return buf.toArray(); + } + + private static boolean hasAnnotation(Annotation[][] parameterAnnotations) { + for (Annotation[] annotations : parameterAnnotations) { + if (annotations.length != 0) { + return true; + } + } + return false; + } + + private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) { + buf.putS4(encoders.sourceClasses.getIndex(annotation.annotationType())); + AnnotationType type = AnnotationType.getInstance(annotation.annotationType()); + buf.putU2(type.members().size()); + for (Map.Entry entry : type.members().entrySet()) { + String memberName = entry.getKey(); + Method valueAccessor = entry.getValue(); + buf.putS4(encoders.sourceMethodNames.getIndex(memberName)); + try { + encodeValue(buf, valueAccessor.invoke(annotation), type.memberTypes().get(memberName)); + } catch (InvocationTargetException e) { + encodeValue(buf, annotationExceptionProxies.get(Pair.create(annotation, memberName)), Throwable.class); + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); + } + } + } + + private byte[] encodeMemberValue(Object value) { + if (value == null) { + return null; + } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); + Class type = value.getClass(); + if (Proxy.isProxyClass(type)) { + assert type.getInterfaces().length == 1; + type = type.getInterfaces()[0]; + } + encodeValue(buf, value, type); + return buf.toArray(); + } + + private void encodeValue(UnsafeArrayTypeWriter buf, Object value, Class type) { + buf.putU1(tag(type)); + if (type.isAnnotation()) { + encodeAnnotation(buf, (Annotation) value); + } else if (type.isEnum()) { + buf.putS4(encoders.sourceClasses.getIndex(type)); + buf.putS4(encoders.sourceMethodNames.getIndex(((Enum) value).name())); + } else if (type.isArray()) { + encodeArray(buf, value, type.getComponentType()); + } else if (type == Class.class) { + buf.putS4(encoders.sourceClasses.getIndex((Class) value)); + } else if (type == String.class) { + buf.putS4(encoders.sourceMethodNames.getIndex((String) value)); + } else if (type.isPrimitive() || Wrapper.isWrapperType(type)) { + Wrapper wrapper = type.isPrimitive() ? Wrapper.forPrimitiveType(type) : Wrapper.forWrapperType(type); + switch (wrapper) { + case BOOLEAN: + buf.putU1((boolean) value ? 1 : 0); + break; + case BYTE: + buf.putS1((byte) value); + break; + case SHORT: + buf.putS2((short) value); + break; + case CHAR: + buf.putU2((char) value); + break; + case INT: + buf.putS4((int) value); + break; + case LONG: + buf.putS8((long) value); + break; + case FLOAT: + buf.putS4(Float.floatToRawIntBits((float) value)); + break; + case DOUBLE: + buf.putS8(Double.doubleToRawLongBits((double) value)); + break; + default: + throw GraalError.shouldNotReachHere(); + } + } else if (type == Throwable.class) { + buf.putS4(encoders.objectConstants.getIndex((JavaConstant) value)); + } else { + throw GraalError.shouldNotReachHere(); + } + } + + private void encodeArray(UnsafeArrayTypeWriter buf, Object value, Class componentType) { + if (!componentType.isPrimitive()) { + Object[] array = (Object[]) value; + buf.putU2(array.length); + for (Object val : array) { + encodeValue(buf, val, componentType); + } + } else if (componentType == boolean.class) { + boolean[] array = (boolean[]) value; + buf.putU2(array.length); + for (boolean val : array) { + encodeValue(buf, val, componentType); + } + } else if (componentType == byte.class) { + byte[] array = (byte[]) value; + buf.putU2(array.length); + for (byte val : array) { + encodeValue(buf, val, componentType); + } + } else if (componentType == short.class) { + short[] array = (short[]) value; + buf.putU2(array.length); + for (short val : array) { + encodeValue(buf, val, componentType); + } + } else if (componentType == char.class) { + char[] array = (char[]) value; + buf.putU2(array.length); + for (char val : array) { + encodeValue(buf, val, componentType); + } + } else if (componentType == int.class) { + int[] array = (int[]) value; + buf.putU2(array.length); + for (int val : array) { + encodeValue(buf, val, componentType); + } + } else if (componentType == long.class) { + long[] array = (long[]) value; + buf.putU2(array.length); + for (long val : array) { + encodeValue(buf, val, componentType); + } + } else if (componentType == float.class) { + float[] array = (float[]) value; + buf.putU2(array.length); + for (float val : array) { + encodeValue(buf, val, componentType); + } + } else if (componentType == double.class) { + double[] array = (double[]) value; + buf.putU2(array.length); + for (double val : array) { + encodeValue(buf, val, componentType); + } + } + } + + private static byte tag(Class type) { + if (type.isAnnotation()) { + return '@'; + } else if (type.isEnum()) { + return 'e'; + } else if (type.isArray()) { + return '['; + } else if (type == Class.class) { + return 'c'; + } else if (type == String.class) { + return 's'; + } else if (type.isPrimitive()) { + return (byte) Wrapper.forPrimitiveType(type).basicTypeChar(); + } else if (Wrapper.isWrapperType(type)) { + return (byte) Wrapper.forWrapperType(type).basicTypeChar(); + } else if (type == Throwable.class) { + return 'E'; + } else { + throw GraalError.shouldNotReachHere(type.toString()); + } + } + + private byte[] encodeTypeAnnotations(TypeAnnotation[] annotations) { + if (annotations.length == 0) { + return null; + } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); + buf.putU2(annotations.length); + for (TypeAnnotation typeAnnotation : annotations) { + encodeTypeAnnotation(buf, typeAnnotation); + } + return buf.toArray(); + } + + private void encodeTypeAnnotation(UnsafeArrayTypeWriter buf, TypeAnnotation typeAnnotation) { + encodeTargetInfo(buf, typeAnnotation.getTargetInfo()); + encodeLocationInfo(buf, typeAnnotation.getLocationInfo()); + // Checkstyle: allow direct annotation access + encodeAnnotation(buf, typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access + } + + private static final byte CLASS_TYPE_PARAMETER = 0x00; + private static final byte METHOD_TYPE_PARAMETER = 0x01; + private static final byte CLASS_EXTENDS = 0x10; + private static final byte CLASS_TYPE_PARAMETER_BOUND = 0x11; + private static final byte METHOD_TYPE_PARAMETER_BOUND = 0x12; + private static final byte FIELD = 0x13; + private static final byte METHOD_RETURN = 0x14; + private static final byte METHOD_RECEIVER = 0x15; + private static final byte METHOD_FORMAL_PARAMETER = 0x16; + private static final byte THROWS = 0x17; + + private static void encodeTargetInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.TypeAnnotationTargetInfo targetInfo) { + switch (targetInfo.getTarget()) { + case CLASS_TYPE_PARAMETER: + buf.putU1(CLASS_TYPE_PARAMETER); + buf.putU1(targetInfo.getCount()); + break; + case METHOD_TYPE_PARAMETER: + buf.putU1(METHOD_TYPE_PARAMETER); + buf.putU1(targetInfo.getCount()); + break; + case CLASS_EXTENDS: + buf.putU1(CLASS_EXTENDS); + buf.putS2(-1); + break; + case CLASS_IMPLEMENTS: + buf.putU1(CLASS_EXTENDS); + buf.putS2(targetInfo.getCount()); + break; + case CLASS_TYPE_PARAMETER_BOUND: + buf.putU1(CLASS_TYPE_PARAMETER_BOUND); + buf.putU1(targetInfo.getCount()); + buf.putU1(targetInfo.getSecondaryIndex()); + break; + case METHOD_TYPE_PARAMETER_BOUND: + buf.putU1(METHOD_TYPE_PARAMETER_BOUND); + buf.putU1(targetInfo.getCount()); + buf.putU1(targetInfo.getSecondaryIndex()); + break; + case FIELD: + buf.putU1(FIELD); + break; + case METHOD_RETURN: + buf.putU1(METHOD_RETURN); + break; + case METHOD_RECEIVER: + buf.putU1(METHOD_RECEIVER); + break; + case METHOD_FORMAL_PARAMETER: + buf.putU1(METHOD_FORMAL_PARAMETER); + buf.putU1(targetInfo.getCount()); + break; + case THROWS: + buf.putU1(THROWS); + buf.putU2(targetInfo.getCount()); + break; + default: + throw GraalError.shouldNotReachHere("Unknown type annotation target: " + targetInfo.getTarget()); + } + } + + private static final Field locationInfoDepth = ReflectionUtil.lookupField(TypeAnnotation.LocationInfo.class, "depth"); + private static final Field locationInfoLocations = ReflectionUtil.lookupField(TypeAnnotation.LocationInfo.class, "locations"); + + private static void encodeLocationInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.LocationInfo locationInfo) { + try { + int depth = (int) locationInfoDepth.get(locationInfo); + buf.putU1(depth); + TypeAnnotation.LocationInfo.Location[] locations; + locations = (TypeAnnotation.LocationInfo.Location[]) locationInfoLocations.get(locationInfo); + for (TypeAnnotation.LocationInfo.Location location : locations) { + buf.putS1(location.tag); + buf.putU1(location.index); + } + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + private byte[] encodeReflectParameters(ReflectParameterMetadata[] reflectParameters) { + if (reflectParameters == null) { + return null; + } + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + encodeArray(buf, reflectParameters, reflectParameter -> encodeReflectParameter(buf, reflectParameter)); + return buf.toArray(); + } + + private void encodeReflectParameter(UnsafeArrayTypeWriter buf, ReflectParameterMetadata reflectParameter) { + encodeName(buf, reflectParameter.name); + buf.putUV(reflectParameter.modifiers); + } + + private void encodeRecordComponent(UnsafeArrayTypeWriter buf, RecordComponentMetadata recordComponent) { + encodeName(buf, recordComponent.name); + encodeType(buf, recordComponent.type); + encodeName(buf, recordComponent.signature); + encodeObject(buf, recordComponent.accessor); + encodeByteArray(buf, encodeAnnotations(recordComponent.annotations)); + encodeByteArray(buf, encodeTypeAnnotations(recordComponent.typeAnnotations)); + } + + private byte[] encodeEnclosingMethod(Object[] enclosingMethodInfo) { + if (enclosingMethodInfo == null) { + return null; + } + assert enclosingMethodInfo.length == 3; + UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); + encodeType(buf, (HostedType) enclosingMethodInfo[0]); + encodeName(buf, (String) enclosingMethodInfo[1]); + encodeName(buf, (String) enclosingMethodInfo[2]); + return buf.toArray(); + } + + static final class ReflectionDataAccessors { + private final Method privateGetDeclaredFields; + private final Method privateGetDeclaredMethods; + private final Method privateGetDeclaredConstructors; + + ReflectionDataAccessors() { + privateGetDeclaredFields = ReflectionUtil.lookupMethod(Class.class, "privateGetDeclaredFields", boolean.class); + privateGetDeclaredMethods = ReflectionUtil.lookupMethod(Class.class, "privateGetDeclaredMethods", boolean.class); + privateGetDeclaredConstructors = ReflectionUtil.lookupMethod(Class.class, "privateGetDeclaredConstructors", boolean.class); + } + + Field[] getDeclaredFields(Object obj) { + try { + return (Field[]) privateGetDeclaredFields.invoke(obj, false); + } catch (IllegalAccessException | InvocationTargetException e) { + /* Don't enforce an order if querying fails */ + return new Field[0]; + } + } + + Method[] getDeclaredMethods(Object obj) { + try { + return (Method[]) privateGetDeclaredMethods.invoke(obj, false); + } catch (IllegalAccessException | InvocationTargetException e) { + /* Don't enforce an order if querying fails */ + return new Method[0]; + } + } + + Constructor[] getDeclaredConstructors(Object obj) { + try { + return (Constructor[]) privateGetDeclaredConstructors.invoke(obj, false); + } catch (IllegalAccessException | InvocationTargetException e) { + /* Don't enforce an order if querying fails */ + return new Constructor[0]; + } + } + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java deleted file mode 100644 index 7102fdad5c20..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionObjectReplacer.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.reflect.hosted; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.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; - - Identity(Object wrapped) { - this.wrapped = wrapped; - } - - @Override - public int hashCode() { - return System.identityHashCode(wrapped); - } - - @Override - public boolean equals(Object obj) { - return ((Identity) obj).wrapped == wrapped; - } - } - - private final Set scanned = ConcurrentHashMap.newKeySet(); - - @Override - public Object apply(Object original) { - if (original instanceof AccessibleObject || original instanceof Parameter || - original instanceof AbstractRepository || original instanceof AbstractScope) { - if (scanned.add(new Identity(original))) { - scan(original); - } - } - return original; - } - - private 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(); - } - if (original instanceof AbstractScope) { - AbstractScope abstractScope = (AbstractScope) original; - /* - * Lookup a type variable in the scope to trigger creation of - * sun.reflect.generics.scope.AbstractScope.enclosingScope. The looked-up value is not - * important, we just want to trigger creation of lazy internal state. The same eager - * initialization is triggered by - * sun.reflect.generics.repository.MethodRepository.getReturnType() called above, - * however if the AbstractScope is seen first by the heap scanner then a `null` value - * will be snapshotted for the `enclosingScope`. - */ - try { - abstractScope.lookup(""); - } catch (LinkageError | InternalError e) { - /* The lookup calls Class.getEnclosingClass() which may fail. */ - } - } - } -} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java index 488832341a76..dab8810a4e19 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/DynamicProxySupport.java @@ -99,7 +99,11 @@ public void addProxyClass(Class... interfaces) { try { clazz = getJdkProxyClass(classLoader, intfs); } catch (Throwable e) { - return e; + try { + clazz = getJdkProxyClass(getCommonClassLoader(intfs), intfs); + } catch (Throwable e2) { + return e; + } } /* @@ -131,6 +135,21 @@ public void addProxyClass(Class... interfaces) { }); } + private static ClassLoader getCommonClassLoader(Class... intfs) { + ClassLoader classLoader = null; + for (Class intf : intfs) { + ClassLoader intfLoader = intf.getClassLoader(); + if (classLoader == null) { + classLoader = intfLoader; + } else { + if (intfLoader != classLoader) { + return null; + } + } + } + return classLoader; + } + @Override public Class getProxyClass(ClassLoader loader, Class... interfaces) { ProxyCacheKey key = new ProxyCacheKey(interfaces); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java deleted file mode 100644 index d140c440f1e2..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.reflect.target; - -import java.lang.reflect.Array; -import java.lang.reflect.Executable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Supplier; - -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 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; -import com.oracle.svm.core.code.CodeInfoAccess; -import com.oracle.svm.core.code.CodeInfoTable; -import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.reflect.MethodMetadataDecoder; -import com.oracle.svm.core.util.ByteArrayReader; - -/** - * The metadata for methods in the image is split into two arrays: one for the index and the other - * for data. The index contains an array of integers pointing to offsets in the data, and indexed by - * type ID. The data array contains arrays of method metadata, ordered by type ID, such that all - * methods declared by a class are stored consecutively, in the following format: - * - *
- * {
- *     int queriedMethodsCount;
- *     ReflectMethodEncoding[] queriedMethods[queriedMethodsCount];
- *     int hidingMethodsCount;
- *     SimpleMethodEncoding[] hidingMethods[hidingMethodsCount];
- *     int declaringTypeIndex;             // index in frameInfoSourceClasses
- *     int reachableMethodsCount;
- *     SimpleMethodEncoding[] reachableMethods[reachableMethodsCount];
- * } TypeEncoding;
- * 
- * - * The declaring class is encoded before the reachable methods to avoid having to be decoded when - * getting the queried and hiding methods, in which case the declaring class is available as an - * argument and doesn't need to be retrieved from the encoding. - * - * The data for a queried method is stored in the following format: - * - *
- * {
- *     int methodNameIndex;                // index in frameInfoSourceMethodNames ("" for constructors)
- *     int paramCount;
- *     int[] paramTypeIndices[paramCount]; // index in frameInfoSourceClasses
- *     int modifiers;
- *     int returnTypeIndex;                // index in frameInfoSourceClasses (void for constructors)
- *     int exceptionTypeCount;
- *     int[] exceptionTypeIndices[exceptionTypeCount]; // index in frameInfoSourceClasses
- *     int signatureIndex;                 // index in frameInfoSourceMethodNames
- *     int annotationsEncodingLength;
- *     byte[] annotationsEncoding[annotationsEncodingLength];
- *     int parameterAnnotationsEncodingLength;
- *     byte[] parameterAnnotationsEncoding[parameterAnnotationsEncodingLength];
- *     int typeAnnotationsEncodingLength;
- *     byte[] typeAnnotationsEncoding[typeAnnotationsEncodingLength];
- *     boolean hasRealParameterData;
- *     int reflectParameterCount;          // only if hasRealParameterData is true
- *     {
- *         int reflectParameterNameIndex;  // index in frameInfoSourceMethodNames
- *         int reflectParameterModifiers;
- *     } reflectParameters[reflectParameterCount];
- * } ReflectMethodEncoding;
- * 
- * - * The data for a hiding or reachable method is stored as follows: - * - *
- * {
- *     int methodNameIndex;                // index in frameInfoSourceMethodNames ("" for constructors)
- *     int paramCount;
- *     int[] paramTypeIndices[paramCount]; // index in frameInfoSourceClasses
- * } SimpleMethodEncoding;
- * 
- */ -public class MethodMetadataDecoderImpl implements MethodMetadataDecoder { - public static final int NO_METHOD_METADATA = -1; - - @Fold - static boolean hasQueriedMethods() { - return !ImageSingletons.lookup(RuntimeReflectionSupport.class).getQueriedOnlyMethods().isEmpty(); - } - - /** - * 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()); - - 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 MethodDescriptor[] getAllReachableMethods() { - if (!SubstrateOptions.IncludeMethodData.getValue()) { - return new MethodDescriptor[0]; - } - - CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); - byte[] data = ImageSingletons.lookup(MethodMetadataEncoding.class).getMethodsEncoding(); - UnsafeArrayTypeReader dataReader = UnsafeArrayTypeReader.create(data, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); - List allMethods = new ArrayList<>(); - for (int i = 0; i < ImageSingletons.lookup(MethodMetadataEncoding.class).getIndexEncoding().length / Integer.BYTES; ++i) { - int dataOffset = getOffset(i); - if (dataOffset != NO_METHOD_METADATA) { - dataReader.setByteIndex(dataOffset); - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - /* Skip the queried methods data */ - decodeArray(dataReader, Executable.class, () -> decodeReflectionMethod(dataReader, codeInfo, null)); - decodeArray(dataReader, MethodDescriptor.class, () -> decodeSimpleMethod(dataReader, codeInfo, null)); - } - Class declaringClass = decodeType(dataReader, codeInfo); - if (declaringClass != null) { - allMethods.addAll(Arrays.asList(decodeArray(dataReader, MethodDescriptor.class, () -> decodeSimpleMethod(dataReader, codeInfo, declaringClass)))); - } - } - } - return allMethods.toArray(new MethodDescriptor[0]); - } - - @Override - public long getMetadataByteLength() { - MethodMetadataEncoding encoding = ImageSingletons.lookup(MethodMetadataEncoding.class); - return encoding.getMethodsEncoding().length + encoding.getIndexEncoding().length; - } - - 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 Executable decodeReflectionMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { - String name = decodeName(buf, info); - Class[] parameterTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); - int modifiers = buf.getUVInt(); - Class returnType = decodeType(buf, info); - Class[] exceptionTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); - String signature = decodeName(buf, info); - byte[] annotations = decodeByteArray(buf); - byte[] parameterAnnotations = decodeByteArray(buf); - byte[] typeAnnotations = decodeByteArray(buf); - boolean hasRealParameterData = buf.getU1() == 1; - ReflectParameterDescriptor[] reflectParameters = hasRealParameterData ? decodeArray(buf, ReflectParameterDescriptor.class, () -> decodeReflectParameter(buf, info)) : null; - - Target_java_lang_reflect_Executable executable; - if (name.equals("")) { - assert returnType == void.class; - Target_java_lang_reflect_Constructor constructor = new Target_java_lang_reflect_Constructor(); - constructor.constructor(declaringClass, parameterTypes, exceptionTypes, modifiers, -1, signature, annotations, parameterAnnotations); - 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; - } - 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 MethodDescriptor decodeSimpleMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { - String name = decodeName(buf, info); - Class[] paramTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); - return new MethodDescriptor(declaringClass, name, paramTypes); - } - - private static Class decodeType(UnsafeArrayTypeReader buf, CodeInfo info) { - int classIndex = buf.getSVInt(); - if (classIndex == NO_METHOD_METADATA) { - return null; - } - return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), classIndex); - } - - private static String decodeName(UnsafeArrayTypeReader buf, CodeInfo info) { - int nameIndex = buf.getSVInt(); - String name = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), nameIndex); - /* Interning the string to ensure JDK8 method search succeeds */ - return name == null ? null : name.intern(); - } - - @SuppressWarnings("unchecked") - private static T[] decodeArray(UnsafeArrayTypeReader buf, Class elementType, Supplier elementDecoder) { - int length = buf.getUVInt(); - T[] result = (T[]) Array.newInstance(elementType, length); - for (int i = 0; i < length; ++i) { - result[i] = elementDecoder.get(); - } - return result; - } - - private static byte[] decodeByteArray(UnsafeArrayTypeReader buf) { - int length = buf.getUVInt(); - byte[] result = new byte[length]; - for (int i = 0; i < length; ++i) { - result[i] = (byte) buf.getS1(); - } - return result; - } - - private static ReflectParameterDescriptor decodeReflectParameter(UnsafeArrayTypeReader buf, CodeInfo info) { - String name = decodeName(buf, info); - int modifiers = buf.getS4(); - return new ReflectParameterDescriptor(name, modifiers); - } - - public static class ReflectParameterDescriptor { - private final String name; - private final int modifiers; - - public ReflectParameterDescriptor(String name, int modifiers) { - this.name = name; - this.modifiers = modifiers; - } - - public String getName() { - return name; - } - - public int getModifiers() { - return modifiers; - } - } -} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java new file mode 100644 index 000000000000..2d25bb594e28 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataDecoderImpl.java @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.reflect.target; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +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.function.Function; + +import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.c.NonmovableArrays; +import com.oracle.svm.core.code.CodeInfo; +import com.oracle.svm.core.code.CodeInfoAccess; +import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; +import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent; +import com.oracle.svm.core.util.ByteArrayReader; +import com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl; + +/** + * This class performs the parsing of reflection metadata at runtime. The encoding formats are + * specified as comments above each parsing method. + * + * See {@link ReflectionMetadataEncoderImpl} for details about the emission of the metadata. + */ +public class ReflectionMetadataDecoderImpl implements ReflectionMetadataDecoder { + public static final int NO_METHOD_METADATA = -1; + public static final int NULL_OBJECT = -1; + public static final int COMPLETE_FLAG_INDEX = 31; + public static final int COMPLETE_FLAG_MASK = 1 << COMPLETE_FLAG_INDEX; + public static final int IN_HEAP_FLAG_INDEX = 30; + public static final int IN_HEAP_FLAG_MASK = 1 << IN_HEAP_FLAG_INDEX; + public static final int HIDING_FLAG_INDEX = 29; + public static final int HIDING_FLAG_MASK = 1 << HIDING_FLAG_INDEX; + + static byte[] getEncoding() { + return ImageSingletons.lookup(ReflectionMetadataEncoding.class).getEncoding(); + } + + /** + * Fields encoding. + * + *
+     * FieldMetadata[] fields
+     * 
+ */ + @Override + public Field[] parseFields(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Field.class, (i) -> decodeField(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); + } + + /** + * Methods encoding. + * + *
+     * MethodMetadata[] methods
+     * 
+ */ + @Override + public Method[] parseMethods(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Method.class, (i) -> decodeMethod(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); + } + + /** + * Constructors encoding. + * + *
+     * ConstructorMetadata[] constructors
+     * 
+ */ + @Override + public Constructor[] parseConstructors(DynamicHub declaringType, int index, boolean publicOnly, boolean reflectOnly) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Constructor.class, (i) -> decodeConstructor(reader, codeInfo, DynamicHub.toClass(declaringType), publicOnly, reflectOnly)); + } + + /** + * Inner classes encoding. + * + *
+     * ClassIndex[] innerClasses
+     * 
+ */ + @Override + public Class[] parseClasses(int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Class.class, (i) -> decodeType(reader, codeInfo)); + } + + /** + * Record components encoding. + * + *
+     * RecordComponentMetadata[] recordComponents
+     * 
+ */ + @Override + public Target_java_lang_reflect_RecordComponent[] parseRecordComponents(DynamicHub declaringType, int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Target_java_lang_reflect_RecordComponent.class, (i) -> decodeRecordComponent(reader, codeInfo, DynamicHub.toClass(declaringType))); + } + + /** + * Parameters encoding for executables. + * + *
+     * ParameterMetadata[] parameters
+     * 
+ */ + @Override + public Parameter[] parseReflectParameters(Executable executable, byte[] encoding) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(encoding, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + return decodeArray(reader, Parameter.class, (i) -> decodeReflectParameter(reader, codeInfo, executable, i)); + } + + /** + * Class enclosing method information. {@link Class#getEnclosingMethod()} + * + *
+     * EnclosingMethodInfo {
+     *     ClassIndex  declaringClass
+     *     StringIndex name
+     *     StringIndex descriptor
+     * }
+     * 
+ */ + @Override + public Object[] parseEnclosingMethod(int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + Class declaringClass = decodeType(reader, codeInfo); + String name = decodeName(reader, codeInfo); + String descriptor = decodeName(reader, codeInfo); + return new Object[]{declaringClass, name, descriptor}; + } + + @Override + public byte[] parseByteArray(int index) { + UnsafeArrayTypeReader reader = UnsafeArrayTypeReader.create(getEncoding(), index, ByteArrayReader.supportsUnalignedMemoryAccess()); + return decodeByteArray(reader); + } + + @Override + public boolean isHidingMethod(int modifiers) { + return (modifiers & HIDING_FLAG_MASK) != 0; + } + + @Override + public long getMetadataByteLength() { + return ImageSingletons.lookup(ReflectionMetadataEncoding.class).getEncoding().length; + } + + /** + * + * Complete field encoding. + * + *
+     * CompleteFieldMetadata : FieldMetadata {
+     *     int         modifiers               (including COMPLETE flag)
+     *     StringIndex name
+     *     ClassIndex  type
+     *     boolean     trustedFinal            (only on JDK 17 and later)
+     *     StringIndex signature
+     *     byte[]      annotationsEncoding
+     *     byte[]      typeAnnotationsEncoding
+     *     int         offset
+     *     StringIndex deletedReason
+     * }
+     * 
+ *

+ * Heap field encoding. + * + *

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

+ * Partial field encoding. + * + *

+     * PartialFieldMetadata : FieldMetadata {
+     *     int         modifiers
+     *     StringIndex name
+     * }
+     * 
+ */ + private static Field decodeField(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { + int modifiers = buf.getUVInt(); + boolean inHeap = (modifiers & IN_HEAP_FLAG_MASK) != 0; + if (inHeap) { + Field field = (Field) decodeObject(buf, info); + if (publicOnly && !Modifier.isPublic(field.getModifiers())) { + return null; + } + return field; + } + boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; + modifiers &= ~COMPLETE_FLAG_MASK; + + String name = decodeName(buf, info); + if (!complete) { + if (reflectOnly) { + return null; + } + Target_java_lang_reflect_Field field = new Target_java_lang_reflect_Field(); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + field.constructorJDK17OrLater(declaringClass, name, null, modifiers, false, -1, null, null); + } else { + field.constructorJDK11OrEarlier(declaringClass, name, null, modifiers, -1, null, null); + } + return SubstrateUtil.cast(field, Field.class); + } + Class type = decodeType(buf, info); + boolean trustedFinal = (JavaVersionUtil.JAVA_SPEC >= 17) ? buf.getU1() == 1 : false; + String signature = decodeName(buf, info); + byte[] annotations = decodeByteArray(buf); + byte[] typeAnnotations = decodeByteArray(buf); + int offset = buf.getSVInt(); + String deletedReason = decodeName(buf, info); + if (publicOnly && !Modifier.isPublic(modifiers)) { + return null; + } + + Target_java_lang_reflect_Field field = new Target_java_lang_reflect_Field(); + if (JavaVersionUtil.JAVA_SPEC >= 17) { + field.constructorJDK17OrLater(declaringClass, name, type, modifiers, trustedFinal, -1, signature, annotations); + } else { + field.constructorJDK11OrEarlier(declaringClass, name, type, modifiers, -1, signature, annotations); + } + field.offset = offset; + field.deletedReason = deletedReason; + SubstrateUtil.cast(field, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations = typeAnnotations; + return SubstrateUtil.cast(field, Field.class); + } + + /** + * Complete method encoding. + * + *
+     * CompleteMethodMetadata : MethodMetadata {
+     *     int          modifiers                    (including COMPLETE flag)
+     *     StringIndex  name
+     *     ClassIndex[] parameterTypes
+     *     ClassIndex   returnType
+     *     StringIndex  signature
+     *     byte[]       annotationsEncoding
+     *     byte[]       parameterAnnotationsEncoding
+     *     byte[]       annotationDefaultEncoding    (annotation methods only)
+     *     byte[]       typeAnnotationsEncoding
+     *     byte[]       reflectParametersEncoding    ({@link #decodeReflectParameter(UnsafeArrayTypeReader, CodeInfo, Executable, int)})
+     *     ObjectIndex  accessor                     (null if registered as queried only)
+     * }
+     * 
+ * + * Heap method encoding. + * + *
+     * HeapMethodMetadata : MethodMetadata {
+     *     int         modifiers    (including IN_HEAP flag)
+     *     ObjectIndex methodObject
+     * }
+     * 
+ * + * Partial method encoding. + * + *
+     * PartialMethodMetadata : MethodMetadata {
+     *     int          modifiers
+     *     StringIndex  name
+     *     ClassIndex[] parameterTypes
+     *     ClassIndex   returnType
+     * }
+     * 
+ */ + private static Method decodeMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { + return (Method) decodeExecutable(buf, info, declaringClass, publicOnly, reflectOnly, true); + } + + /** + * Complete constructor encoding. + * + *
+     * CompleteConstructorMetadata : ConstructorMetadata {
+     *     int          modifiers                    (including COMPLETE flag)
+     *     ClassIndex[] parameterTypes
+     *     StringIndex  signature
+     *     byte[]       annotationsEncoding
+     *     byte[]       parameterAnnotationsEncoding
+     *     byte[]       typeAnnotationsEncoding
+     *     byte[]       reflectParametersEncoding    ({@link #parseReflectParameters(Executable, byte[])})
+     *     ObjectIndex  accessor                     (null if registered as queried only)
+     * }
+     * 
+ * + * Heap constructor encoding. + * + *
+     * HeapConstructorMetadata : ConstructorMetadata {
+     *     int         modifiers         (including IN_HEAP flag)
+     *     ObjectIndex constructorObject
+     * }
+     * 
+ * + * Partial constructor encoding. + * + *
+     * PartialConstructorMetadata : ConstructorMetadata {
+     *     int          modifiers
+     *     ClassIndex[] parameterTypes
+     * }
+     * 
+ */ + private static Constructor decodeConstructor(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly) { + return (Constructor) decodeExecutable(buf, info, declaringClass, publicOnly, reflectOnly, false); + } + + private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass, boolean publicOnly, boolean reflectOnly, boolean isMethod) { + int modifiers = buf.getUVInt(); + boolean inHeap = (modifiers & IN_HEAP_FLAG_MASK) != 0; + if (inHeap) { + Executable executable = (Executable) decodeObject(buf, info); + if (publicOnly && !Modifier.isPublic(executable.getModifiers())) { + return null; + } + return executable; + } + boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0; + boolean hiding = (modifiers & HIDING_FLAG_MASK) != 0; + assert !(complete && hiding); + modifiers &= ~COMPLETE_FLAG_MASK; + + String name = isMethod ? decodeName(buf, info) : null; + Class[] parameterTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf, info)); + Class returnType = isMethod ? decodeType(buf, info) : null; + if (!complete) { + if (reflectOnly != hiding) { + /* + * When querying for reflection methods, we want the hiding methods but not the + * reachable methods. When querying for reachable methods, we want the reachable + * methods but not the hiding methods. + */ + return null; + } + if (isMethod) { + Target_java_lang_reflect_Method method = new Target_java_lang_reflect_Method(); + method.constructor(declaringClass, name, parameterTypes, returnType, null, modifiers, -1, null, null, null, null); + return SubstrateUtil.cast(method, Executable.class); + } else { + Target_java_lang_reflect_Constructor constructor = new Target_java_lang_reflect_Constructor(); + constructor.constructor(declaringClass, parameterTypes, null, modifiers, -1, null, null, null); + return SubstrateUtil.cast(constructor, Executable.class); + } + } + 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); + byte[] reflectParameters = decodeByteArray(buf); + Object accessor = decodeObject(buf, info); + if (publicOnly && !Modifier.isPublic(modifiers)) { + return null; + } + + Target_java_lang_reflect_Executable executable; + 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); + } + executable.rawParameters = reflectParameters; + SubstrateUtil.cast(executable, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations = typeAnnotations; + return SubstrateUtil.cast(executable, Executable.class); + } + + /** + * Record component encoding. + * + *
+     * RecordComponentMetadata {
+     *     StringIndex name
+     *     ClassIndex  type
+     *     StringIndex signature
+     *     ObjectIndex accessor
+     *     byte[]      annotations
+     *     byte[]      typeAnnotations
+     * }
+     * 
+ */ + private static Target_java_lang_reflect_RecordComponent decodeRecordComponent(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { + String name = decodeName(buf, info); + Class type = decodeType(buf, info); + 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; + } + + /** + * Parameter encoding for executables. + * + *
+     * ParameterMetadata {
+     *     StringIndex name
+     *     int         modifiers
+     * }
+     * 
+ */ + + private static Parameter decodeReflectParameter(UnsafeArrayTypeReader buf, CodeInfo info, Executable executable, int i) { + String name = decodeName(buf, info); + int modifiers = buf.getUVInt(); + + Target_java_lang_reflect_Parameter parameter = new Target_java_lang_reflect_Parameter(); + parameter.constructor(name, modifiers, executable, i); + return SubstrateUtil.cast(parameter, Parameter.class); + } + + /** + * Types are encoded as indices in the frame info source classes array. + */ + private static Class decodeType(UnsafeArrayTypeReader buf, CodeInfo info) { + int classIndex = buf.getSVInt(); + if (classIndex == NO_METHOD_METADATA) { + return null; + } + return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), classIndex); + } + + /** + * Names are encoded as indices in the frame info source method names array. + */ + private static String decodeName(UnsafeArrayTypeReader buf, CodeInfo info) { + int nameIndex = buf.getSVInt(); + String name = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), nameIndex); + /* Interning the string to ensure JDK8 method search succeeds */ + return name == null ? null : name.intern(); + } + + /** + * Objects (method accessors and reflection objects in the heap) are encoded as indices in the + * frame info object constants array. + */ + private static Object decodeObject(UnsafeArrayTypeReader buf, CodeInfo info) { + int objectIndex = buf.getSVInt(); + if (objectIndex == NULL_OBJECT) { + return null; + } + return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoObjectConstants(info), objectIndex); + } + + /** + * Arrays are encoded by their length followed by the elements encoded one after the other. + */ + @SuppressWarnings("unchecked") + private static T[] decodeArray(UnsafeArrayTypeReader buf, Class elementType, Function elementDecoder) { + int length = buf.getUVInt(); + T[] result = (T[]) Array.newInstance(elementType, length); + int valueCount = 0; + for (int i = 0; i < length; ++i) { + T element = elementDecoder.apply(i); + if (element != null) { + result[valueCount++] = element; + } + } + return Arrays.copyOf(result, valueCount); + } + + private static byte[] decodeByteArray(UnsafeArrayTypeReader buf) { + int length = buf.getUVInt(); + if (length == NO_DATA) { + return null; + } + byte[] result = new byte[length]; + for (int i = 0; i < length; ++i) { + result[i] = (byte) buf.getS1(); + } + return result; + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataEncoding.java similarity index 67% rename from substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java rename to substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataEncoding.java index f5945a6e2989..fc33ab2c2b53 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/ReflectionMetadataEncoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,25 +29,15 @@ import com.oracle.svm.core.annotate.UnknownObjectField; -public class MethodMetadataEncoding { - @UnknownObjectField(types = {byte[].class}) private byte[] methodsEncoding; - @UnknownObjectField(types = {byte[].class}) private byte[] indexEncoding; +public class ReflectionMetadataEncoding { + @UnknownObjectField(types = {byte[].class}) private byte[] encoding; - public byte[] getMethodsEncoding() { - return methodsEncoding; + public byte[] getEncoding() { + return encoding; } @Platforms(Platform.HOSTED_ONLY.class) - public void setMethodsEncoding(byte[] methodsEncoding) { - this.methodsEncoding = methodsEncoding; - } - - public byte[] getIndexEncoding() { - return indexEncoding; - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void setIndexEncoding(byte[] indexEncoding) { - this.indexEncoding = indexEncoding; + public void setEncoding(byte[] encoding) { + this.encoding = encoding; } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java index e988e4f00540..df50222f2c57 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_AccessibleObject.java @@ -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.ReflectionMetadataEncoder.class).getTypeAnnotationsEncoding((AccessibleObject) receiver); + } + } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java index 4527c1b7ac15..2fa6e30e8bc9 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java @@ -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.ReflectionMetadataEncoder.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.ReflectionMetadataEncoder.class).getParameterAnnotationsEncoding((Executable) receiver); } } - } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java index f56e3a90520f..72813c6aef51 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java @@ -25,304 +25,61 @@ package com.oracle.svm.reflect.target; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.AnnotatedType; import java.lang.reflect.Executable; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; +import java.lang.reflect.Parameter; import java.util.Map; -import java.util.function.Supplier; + +import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.RecomputeFieldValue; -import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.LinkAtBuildTimeSupport; -import com.oracle.svm.reflect.hosted.ReflectionObjectReplacer; +import com.oracle.svm.core.annotate.UnknownObjectField; +import com.oracle.svm.core.reflect.ReflectionMetadataDecoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; -import sun.reflect.annotation.TypeAnnotation; @TargetClass(value = Executable.class) public final class Target_java_lang_reflect_Executable { - /** - * The parameters field doesn't need a value recomputation. Its value is pre-loaded in the - * {@link ReflectionObjectReplacer}. - */ - @Alias // - Target_java_lang_reflect_Parameter[] parameters; - - @Alias // - boolean hasRealParameterData; + @Alias @RecomputeFieldValue(kind = Kind.Reset)// + Parameter[] parameters; - /** - * The declaredAnnotations field doesn't need a value recomputation. Its value is pre-loaded in - * the {@link ReflectionObjectReplacer}. - */ - @Alias // + @Alias @RecomputeFieldValue(kind = Kind.Reset)// Map, Annotation> declaredAnnotations; - @Inject @RecomputeFieldValue(kind = Kind.Reset) // - byte[] typeAnnotations; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = ParameterAnnotationsComputer.class) // - Annotation[][] parameterAnnotations; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedReceiverTypeComputer.class) // - Object annotatedReceiverType; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedParameterTypesComputer.class) // - Object[] annotatedParameterTypes; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedReturnTypeComputer.class) // - Object annotatedReturnType; - - @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = AnnotatedExceptionTypesComputer.class) // - Object[] annotatedExceptionTypes; - - @Alias // - public native int getModifiers(); - - @Alias // - native byte[] getAnnotationBytes(); - - @Alias // - public native Class getDeclaringClass(); - - @Alias // - native Type[] getAllGenericParameterTypes(); - - @Alias // - public native Type[] getGenericExceptionTypes(); - - @Alias // - private native Target_java_lang_reflect_Parameter[] synthesizeAllParams(); - - @Substitute - private Target_java_lang_reflect_Parameter[] privateGetParameters() { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.parameters != null) { - return holder.parameters; - } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - assert !hasRealParameterData; - holder.parameters = synthesizeAllParams(); - return holder.parameters; - } - throw VMError.shouldNotReachHere(); - } + @Inject @RecomputeFieldValue(kind = Kind.Custom, declClass = RawParametersComputer.class)// + @UnknownObjectField(types = {byte[].class}) byte[] rawParameters; @Substitute - Map, Annotation> declaredAnnotations() { - Map, Annotation> declAnnos; - if ((declAnnos = declaredAnnotations) == null) { - if (!MethodMetadataDecoderImpl.hasQueriedMethods()) { - throw VMError.shouldNotReachHere(); - } - synchronized (this) { - if ((declAnnos = declaredAnnotations) == null) { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - declAnnos = Target_sun_reflect_annotation_AnnotationParser.parseAnnotations( - holder.getAnnotationBytes(), - new Target_jdk_internal_reflect_ConstantPool(), - holder.getDeclaringClass()); - declaredAnnotations = declAnnos; - } - } - } - return declAnnos; - } - - @Alias - native Annotation[][] sharedGetParameterAnnotations(Class[] parameterTypes, byte[] annotations); - - @Substitute - @SuppressWarnings({"unused", "hiding", "static-method"}) - Annotation[][] parseParameterAnnotations(byte[] parameterAnnotations) { - if (!MethodMetadataDecoderImpl.hasQueriedMethods()) { - throw VMError.shouldNotReachHere(); - } - return Target_sun_reflect_annotation_AnnotationParser.parseParameterAnnotations( - parameterAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - getDeclaringClass()); - } - - @Substitute - public AnnotatedType getAnnotatedReceiverType() { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.annotatedReceiverType != null) { - return (AnnotatedType) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedReceiverType); - } - if (Modifier.isStatic(this.getModifiers())) { + private Parameter[] getParameters0() { + if (rawParameters == null) { return null; } - if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - AnnotatedType annotatedRecvType = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedType(typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - getDeclaringClass(), - getDeclaringClass(), - TypeAnnotation.TypeAnnotationTarget.METHOD_RECEIVER); - holder.annotatedReceiverType = annotatedRecvType; - return annotatedRecvType; - } - throw VMError.shouldNotReachHere(); + return ImageSingletons.lookup(ReflectionMetadataDecoder.class).parseReflectParameters(SubstrateUtil.cast(this, Executable.class), rawParameters); } @Substitute - public AnnotatedType[] getAnnotatedParameterTypes() { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.annotatedParameterTypes != null) { - return (AnnotatedType[]) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedParameterTypes); - } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - AnnotatedType[] annotatedParamTypes = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedTypes(typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - getDeclaringClass(), - getAllGenericParameterTypes(), - TypeAnnotation.TypeAnnotationTarget.METHOD_FORMAL_PARAMETER); - holder.annotatedParameterTypes = annotatedParamTypes; - return annotatedParamTypes; - } - throw VMError.shouldNotReachHere(); + byte[] getTypeAnnotationBytes0() { + return SubstrateUtil.cast(this, Target_java_lang_reflect_AccessibleObject.class).typeAnnotations; } - @Substitute - public AnnotatedType getAnnotatedReturnType0(@SuppressWarnings("unused") Type returnType) { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.annotatedReturnType != null) { - return (AnnotatedType) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedReturnType); - } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - AnnotatedType annotatedRetType = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedType(typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - getDeclaringClass(), - returnType, - TypeAnnotation.TypeAnnotationTarget.METHOD_RETURN); - holder.annotatedReturnType = annotatedRetType; - return annotatedRetType; - } - throw VMError.shouldNotReachHere(); - } + static class RawParametersComputer implements RecomputeFieldValue.CustomFieldValueComputer { - @Substitute - public AnnotatedType[] getAnnotatedExceptionTypes() { - Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); - if (holder.annotatedExceptionTypes != null) { - return (AnnotatedType[]) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedExceptionTypes); - } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - AnnotatedType[] annotatedExcTypes = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedTypes(typeAnnotations, - new Target_jdk_internal_reflect_ConstantPool(), - SubstrateUtil.cast(this, AnnotatedElement.class), - getDeclaringClass(), - getGenericExceptionTypes(), - TypeAnnotation.TypeAnnotationTarget.THROWS); - holder.annotatedExceptionTypes = annotatedExcTypes; - return annotatedExcTypes; - } - throw VMError.shouldNotReachHere(); - } - - public static final class ParameterAnnotationsComputer implements CustomFieldValueComputer { @Override public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; + return RecomputeFieldValue.ValueAvailability.AfterCompilation; } @Override public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return executable.getParameterAnnotations(); - } - } - - public static final class AnnotatedReceiverTypeComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedReceiverType, executable); - } - } - - public static final class AnnotatedParameterTypesComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedParameterTypes, executable); - } - } - - public static final class AnnotatedReturnTypeComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedReturnType, executable); - } - } - - public static final class AnnotatedExceptionTypesComputer implements CustomFieldValueComputer { - @Override - public RecomputeFieldValue.ValueAvailability valueAvailability() { - return RecomputeFieldValue.ValueAvailability.BeforeAnalysis; - } - - @Override - public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { - Executable executable = (Executable) receiver; - return AnnotatedTypeEncoder.encodeAnnotationTypes(executable::getAnnotatedExceptionTypes, executable); - } - } - - private static final class AnnotatedTypeEncoder { - static Object encodeAnnotationTypes(Supplier supplier, Executable executable) { - try { - return supplier.get(); - } catch (InternalError e) { - return e; - } catch (LinkageError e) { - if (!LinkAtBuildTimeSupport.singleton().linkAtBuildTime(executable.getDeclaringClass())) { - return e; - } - String message = "Encountered an error while processing annotated types for type: " + executable.getDeclaringClass().getName() + ", executable: " + executable.getName() + ". " + - LinkAtBuildTimeSupport.singleton().errorMessageFor(executable.getDeclaringClass()); - throw new UnsupportedOperationException(message, e); - } - } - - static Object decodeAnnotationTypes(Object value) { - if (value == null) { - return null; - } else if (value instanceof AnnotatedType || value instanceof AnnotatedType[]) { - return value; - } else if (value instanceof LinkageError) { - throw (LinkageError) value; - } else if (value instanceof InternalError) { - throw (InternalError) value; - } else { - throw VMError.shouldNotReachHere("Unexpected value while decoding annotation types: " + value.toString()); - } + return ImageSingletons.lookup(NativeImageCodeCache.ReflectionMetadataEncoder.class).getReflectParametersEncoding((Executable) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java index 7df97126af7e..e1b3e7dbe8d6 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Field.java @@ -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.ReflectionMetadataEncoder.class).getAnnotationsEncoding((AccessibleObject) receiver); } } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java index 6163dfe0e6e9..08f89e20e68b 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Method.java @@ -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.ReflectionMetadataEncoder.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.ReflectionMetadataEncoder.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.ReflectionMetadataEncoder.class).getAnnotationDefaultEncoding((Method) receiver); + } + } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Parameter.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Parameter.java index d4483747a870..9b9d03d68b31 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Parameter.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Parameter.java @@ -24,18 +24,22 @@ */ package com.oracle.svm.reflect.target; +import java.lang.reflect.Executable; import java.lang.reflect.Parameter; +import java.lang.reflect.Type; import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; @TargetClass(Parameter.class) public final class Target_java_lang_reflect_Parameter { + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// + private Type parameterTypeCache; + @Alias // @TargetElement(name = TargetElement.CONSTRUCTOR_NAME) - public native void constructor(String name, - int modifiers, - Target_java_lang_reflect_Executable executable, - int index); + public native void constructor(String name, int modifiers, Executable executable, int index); } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java index a240a6c7d8cb..f46bed4838f1 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java @@ -24,15 +24,22 @@ */ package com.oracle.svm.reflect.target; +import static com.oracle.svm.reflect.target.Util_java_lang_reflect_ReflectAccess.copyAccessibleObject; +import static com.oracle.svm.reflect.target.Util_java_lang_reflect_ReflectAccess.copyExecutable; + import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; /** - * These substitutions are needed to set the genericInfo field on Method, Field, Constructor. The - * genericInfo is eagerly loaded at image build time. The Method, Field, Constructor elements, when - * accessed via reflection a copy is returned. The original implementation of copy() doesn't - * propagate the genericInfo. + * These substitutions are needed to set the injected fields on Method, Field, Constructor. The + * metadata for those fields is generated at image build time. The Method, Field, Constructor + * elements, when accessed via reflection a copy is returned. The original implementation of copy() + * doesn't propagate the injected fields. + * + * We substitute these methods instead of the copy constructors on the reflection objects to avoid + * having to copy the contents of those methods. This is ok since the ReflectAccess methods are the + * only users of those copy constructors. */ @TargetClass(className = "java.lang.reflect.ReflectAccess") @@ -42,8 +49,7 @@ public final class Target_java_lang_reflect_ReflectAccess { @SuppressWarnings("static-method") public Target_java_lang_reflect_Method copyMethod(Target_java_lang_reflect_Method method) { Target_java_lang_reflect_Method copy = method.copy(); - copy.genericInfo = method.genericInfo; - Util_java_lang_reflect_ReflectAccess.copyExecutable(SubstrateUtil.cast(copy, Target_java_lang_reflect_Executable.class), + copyExecutable(SubstrateUtil.cast(copy, Target_java_lang_reflect_Executable.class), SubstrateUtil.cast(method, Target_java_lang_reflect_Executable.class)); return copy; } @@ -52,7 +58,10 @@ public Target_java_lang_reflect_Method copyMethod(Target_java_lang_reflect_Metho @SuppressWarnings("static-method") public Target_java_lang_reflect_Field copyField(Target_java_lang_reflect_Field field) { Target_java_lang_reflect_Field copy = field.copy(); - copy.genericInfo = field.genericInfo; + copy.offset = field.offset; + copy.deletedReason = field.deletedReason; + copyAccessibleObject(SubstrateUtil.cast(copy, Target_java_lang_reflect_AccessibleObject.class), + SubstrateUtil.cast(field, Target_java_lang_reflect_AccessibleObject.class)); return copy; } @@ -60,8 +69,7 @@ public Target_java_lang_reflect_Field copyField(Target_java_lang_reflect_Field f @SuppressWarnings("static-method") public Target_java_lang_reflect_Constructor copyConstructor(Target_java_lang_reflect_Constructor constructor) { Target_java_lang_reflect_Constructor copy = constructor.copy(); - copy.genericInfo = constructor.genericInfo; - Util_java_lang_reflect_ReflectAccess.copyExecutable(SubstrateUtil.cast(copy, Target_java_lang_reflect_Executable.class), + copyExecutable(SubstrateUtil.cast(copy, Target_java_lang_reflect_Executable.class), SubstrateUtil.cast(constructor, Target_java_lang_reflect_Executable.class)); return copy; } @@ -69,16 +77,12 @@ public Target_java_lang_reflect_Constructor copyConstructor(Target_java_lang_ref class Util_java_lang_reflect_ReflectAccess { static void copyExecutable(Target_java_lang_reflect_Executable copy, Target_java_lang_reflect_Executable executable) { - if (MethodMetadataDecoderImpl.hasQueriedMethods()) { - /* Isolated to avoid pulling the full signature parsing capabilities from the JDK. */ - copy.parameters = executable.parameters; - } - copy.declaredAnnotations = executable.declaredAnnotations; - copy.parameterAnnotations = executable.parameterAnnotations; - copy.typeAnnotations = executable.typeAnnotations; - copy.annotatedReceiverType = executable.annotatedReceiverType; - copy.annotatedReturnType = executable.annotatedReturnType; - copy.annotatedParameterTypes = executable.annotatedParameterTypes; - copy.annotatedExceptionTypes = executable.annotatedExceptionTypes; + copy.rawParameters = executable.rawParameters; + copyAccessibleObject(SubstrateUtil.cast(copy, Target_java_lang_reflect_AccessibleObject.class), + SubstrateUtil.cast(executable, Target_java_lang_reflect_AccessibleObject.class)); + } + + static void copyAccessibleObject(Target_java_lang_reflect_AccessibleObject copy, Target_java_lang_reflect_AccessibleObject accessibleObject) { + copy.typeAnnotations = accessibleObject.typeAnnotations; } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationInvocationHandler.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationInvocationHandler.java new file mode 100644 index 000000000000..25713cd62281 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationInvocationHandler.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.reflect.target; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.jdk.JDK17OrLater; + +@TargetClass(className = "sun.reflect.annotation.AnnotationInvocationHandler") +public final class Target_sun_reflect_annotation_AnnotationInvocationHandler { + + @Alias + static native String toSourceString(Class clazz); + + @Alias + static native String toSourceString(float clazz); + + @Alias + static native String toSourceString(double clazz); + + @Alias + @TargetElement(onlyWith = JDK17OrLater.class) + static native String toSourceString(byte clazz); + + @Alias + static native String toSourceString(char clazz); + + @Alias + static native String toSourceString(long clazz); + + @Alias + static native String toSourceString(String clazz); + + /* Prevent streams from ending up in simple images */ + @Substitute + private static String memberValueToString(Object value) { + Class type = value.getClass(); + if (!type.isArray()) { + // primitive value, string, class, enum const, or annotation + if (type == Class.class) { + return toSourceString((Class) value); + } else if (type == String.class) { + return toSourceString((String) value); + } else if (type == Character.class) { + return toSourceString((char) value); + } else if (type == Double.class) { + return toSourceString((double) value); + } else if (type == Float.class) { + return toSourceString((float) value); + } else if (type == Long.class) { + return toSourceString((long) value); + } else if (JavaVersionUtil.JAVA_SPEC >= 17 && type == Byte.class) { + return toSourceString((byte) value); + } else { + return value.toString(); + } + } else { + List stringList; + if (type == byte[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((byte[]) value); + } else if (type == char[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((char[]) value); + } else if (type == double[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((double[]) value); + } else if (type == float[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((float[]) value); + } else if (type == int[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((int[]) value); + } else if (type == long[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((long[]) value); + } else if (type == short[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((short[]) value); + } else if (type == boolean[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((boolean[]) value); + } else if (type == Class[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((Class[]) value); + } else if (type == String[].class) { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((String[]) value); + } else { + stringList = Util_sun_reflect_annotation_AnnotationInvocationHandler.convert((Object[]) value); + } + + return Util_sun_reflect_annotation_AnnotationInvocationHandler.stringListToString(stringList); + } + } +} + +class Util_sun_reflect_annotation_AnnotationInvocationHandler { + static List convert(boolean[] values) { + List list = new ArrayList<>(values.length); + for (boolean b : values) { + list.add(Boolean.toString(b)); + } + return list; + } + + static List convert(byte[] values) { + List list = new ArrayList<>(values.length); + for (byte b : values) { + if (JavaVersionUtil.JAVA_SPEC >= 17) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(b)); + } else { + list.add(Byte.toString(b)); + } + } + return list; + } + + static List convert(short[] values) { + List list = new ArrayList<>(values.length); + for (short s : values) { + list.add(Short.toString(s)); + } + return list; + } + + static List convert(char[] values) { + List list = new ArrayList<>(values.length); + for (char c : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(c)); + } + return list; + } + + static List convert(int[] values) { + List list = new ArrayList<>(values.length); + for (int i : values) { + list.add(String.valueOf(i)); + } + return list; + } + + static List convert(long[] values) { + List list = new ArrayList<>(values.length); + for (long l : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(l)); + } + return list; + } + + static List convert(float[] values) { + List list = new ArrayList<>(values.length); + for (float f : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(f)); + } + return list; + } + + static List convert(double[] values) { + List list = new ArrayList<>(values.length); + for (double d : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(d)); + } + return list; + } + + static List convert(Class[] values) { + List list = new ArrayList<>(values.length); + for (Class clazz : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(clazz)); + } + return list; + } + + static List convert(String[] values) { + List list = new ArrayList<>(values.length); + for (String string : values) { + list.add(Target_sun_reflect_annotation_AnnotationInvocationHandler.toSourceString(string)); + } + return list; + } + + static List convert(Object[] values) { + List list = new ArrayList<>(values.length); + for (Object obj : values) { + list.add(Objects.toString(obj)); + } + return list; + } + + static String stringListToString(List list) { + StringBuilder string = new StringBuilder(); + string.append("{"); + for (int i = 0; i < list.size(); ++i) { + string.append(list.get(i)); + if (i < list.size() - 1) { + string.append(", "); + } + } + string.append("}"); + return string.toString(); + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java index 9032338213bd..b7253aef0c8c 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java @@ -26,21 +26,21 @@ import java.lang.annotation.Annotation; import java.lang.annotation.AnnotationFormatError; -import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; -import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.c.NonmovableArrays; +import com.oracle.svm.core.code.CodeInfoAccess; +import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.reflect.hosted.MethodMetadataEncoderImpl; +import com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl; import sun.reflect.annotation.AnnotationParser; import sun.reflect.annotation.AnnotationType; @@ -49,86 +49,11 @@ /** * Substitutions in this class are required to adapt the JDK encoding for annotations to our - * modified version of it. See {@link MethodMetadataEncoderImpl#encodeAnnotations(Annotation[])} for - * a description of the changes and the rationale behind them. + * modified version of it. See {@link ReflectionMetadataEncoderImpl#encodeAnnotations(Annotation[])} + * for a description of the changes and the rationale behind them. */ @TargetClass(AnnotationParser.class) -@SuppressWarnings("unused") public final class Target_sun_reflect_annotation_AnnotationParser { - @Alias - public static native Map, Annotation> parseAnnotations( - byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container); - - @Substitute - private static Map, Annotation> parseAnnotations2( - byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container, - Class[] selectAnnotationClasses) { - Map, Annotation> result = new LinkedHashMap, Annotation>(); - ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); - buf.order(ConfigurationValues.getTarget().arch.getByteOrder()); - int numAnnotations = buf.getShort() & 0xFFFF; - for (int i = 0; i < numAnnotations; i++) { - Annotation a = parseAnnotation2(buf, constPool, container, false, selectAnnotationClasses); - if (a != null) { - Class klass = a.annotationType(); - if (AnnotationType.getInstance(klass).retention() == RetentionPolicy.RUNTIME && - result.put(klass, a) != null) { - throw new AnnotationFormatError( - "Duplicate annotation for class: " + klass + ": " + a); - } - } - } - return result; - } - - @Alias - public static native Annotation[][] parseParameterAnnotations( - byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container); - - @Substitute - private static Annotation[][] parseParameterAnnotations2( - byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container) { - ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); - buf.order(ConfigurationValues.getTarget().arch.getByteOrder()); - int numParameters = buf.get() & 0xFF; - Annotation[][] result = new Annotation[numParameters][]; - - for (int i = 0; i < numParameters; i++) { - int numAnnotations = buf.getShort() & 0xFFFF; - List annotations = new ArrayList(numAnnotations); - for (int j = 0; j < numAnnotations; j++) { - Annotation a = parseAnnotation(buf, constPool, container, false); - if (a != null) { - AnnotationType type = AnnotationType.getInstance( - a.annotationType()); - if (type.retention() == RetentionPolicy.RUNTIME) { - annotations.add(a); - } - } - } - result[i] = annotations.toArray(EMPTY_ANNOTATIONS_ARRAY); - } - return result; - } - - // Checkstyle: stop - @Alias// - private static Annotation[] EMPTY_ANNOTATIONS_ARRAY; - // Checkstyle: resume - - @Alias - static native Annotation parseAnnotation(ByteBuffer buf, - Target_jdk_internal_reflect_ConstantPool constPool, - Class container, - boolean exceptionOnMissingAnnotationClass); @Substitute @SuppressWarnings("unchecked") @@ -193,7 +118,7 @@ public static native Object parseMemberValue(Class memberType, @Substitute private static Object parseClassValue(ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool, - Class container) { + @SuppressWarnings("unused") Class container) { int classIndex = buf.getInt(); try { return constPool.getClassAt(classIndex); @@ -206,12 +131,12 @@ private static Object parseClassValue(ByteBuffer buf, @SuppressWarnings({"unchecked", "rawtypes"}) private static Object parseEnumValue(Class enumType, ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool, - Class container) { + @SuppressWarnings("unused") Class container) { int typeIndex = buf.getInt(); int constNameIndex = buf.getInt(); String constName = constPool.getUTF8At(constNameIndex); - if (enumType != constPool.getClassAt(typeIndex)) { + if (!enumType.isEnum() || enumType != constPool.getClassAt(typeIndex)) { Target_sun_reflect_annotation_AnnotationTypeMismatchExceptionProxy e = new Target_sun_reflect_annotation_AnnotationTypeMismatchExceptionProxy(); e.constructor(enumType.getTypeName() + "." + constName); return e; @@ -248,6 +173,8 @@ private static Object parseConst(int tag, return value == 1; case 's': return constPool.getUTF8At(buf.getInt()); + case 'E': + return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoObjectConstants(CodeInfoTable.getImageCodeInfo()), buf.getInt()); default: throw new AnnotationFormatError( "Invalid member-value tag in annotation: " + tag); @@ -256,7 +183,7 @@ private static Object parseConst(int tag, @Substitute private static Object parseByteArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { byte[] result = new byte[length]; boolean typeMismatch = false; int tag = 0; @@ -275,7 +202,7 @@ private static Object parseByteArray(int length, @Substitute private static Object parseCharArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { char[] result = new char[length]; boolean typeMismatch = false; byte tag = 0; @@ -294,7 +221,7 @@ private static Object parseCharArray(int length, @Substitute private static Object parseDoubleArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { double[] result = new double[length]; boolean typeMismatch = false; int tag = 0; @@ -313,7 +240,7 @@ private static Object parseDoubleArray(int length, @Substitute private static Object parseFloatArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { float[] result = new float[length]; boolean typeMismatch = false; int tag = 0; @@ -332,7 +259,7 @@ private static Object parseFloatArray(int length, @Substitute private static Object parseIntArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { int[] result = new int[length]; boolean typeMismatch = false; int tag = 0; @@ -351,7 +278,7 @@ private static Object parseIntArray(int length, @Substitute private static Object parseLongArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { long[] result = new long[length]; boolean typeMismatch = false; int tag = 0; @@ -370,7 +297,7 @@ private static Object parseLongArray(int length, @Substitute private static Object parseShortArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { short[] result = new short[length]; boolean typeMismatch = false; int tag = 0; @@ -389,7 +316,7 @@ private static Object parseShortArray(int length, @Substitute private static Object parseBooleanArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { boolean[] result = new boolean[length]; boolean typeMismatch = false; int tag = 0; @@ -413,7 +340,7 @@ private static Object parseBooleanArray(int length, @Substitute private static Object parseStringArray(int length, - ByteBuffer buf, Target_jdk_internal_reflect_ConstantPool constPool) { + ByteBuffer buf, @SuppressWarnings("unused") Target_jdk_internal_reflect_ConstantPool constPool) { String[] result = new String[length]; boolean typeMismatch = false; int tag = 0; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java deleted file mode 100644 index 432d09b858e5..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.reflect.target; - -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.AnnotatedType; -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.config.ConfigurationValues; - -import sun.reflect.annotation.TypeAnnotation; -import sun.reflect.annotation.TypeAnnotationParser; - -@TargetClass(TypeAnnotationParser.class) -public final class Target_sun_reflect_annotation_TypeAnnotationParser { - @Alias - public static native AnnotatedType buildAnnotatedType(byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool cp, - AnnotatedElement decl, - Class container, - Type type, - TypeAnnotation.TypeAnnotationTarget filter); - - @Alias - public static native AnnotatedType[] buildAnnotatedTypes(byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool cp, - AnnotatedElement decl, - Class container, - Type[] types, - TypeAnnotation.TypeAnnotationTarget filter); - - @Alias - // Checkstyle: stop - private static TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY; - // Checkstyle: resume - - @Substitute - private static TypeAnnotation[] parseTypeAnnotations(byte[] rawAnnotations, - Target_jdk_internal_reflect_ConstantPool cp, - AnnotatedElement baseDecl, - Class container) { - if (rawAnnotations == null) { - return EMPTY_TYPE_ANNOTATION_ARRAY; - } - - ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); - buf.order(ConfigurationValues.getTarget().arch.getByteOrder()); - int annotationCount = buf.getShort() & 0xFFFF; - List typeAnnotations = new ArrayList<>(annotationCount); - - // Parse each TypeAnnotation - for (int i = 0; i < annotationCount; i++) { - TypeAnnotation ta = parseTypeAnnotation(buf, cp, baseDecl, container); - if (ta != null) { - typeAnnotations.add(ta); - } - } - - return typeAnnotations.toArray(EMPTY_TYPE_ANNOTATION_ARRAY); - } - - @Alias // - private static native TypeAnnotation parseTypeAnnotation(ByteBuffer buf, - Target_jdk_internal_reflect_ConstantPool cp, - AnnotatedElement baseDecl, - Class container); -} diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 22af88086e4b..b5ccf33de4b3 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -784,6 +784,15 @@ public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField origina final class Target_java_lang_ProcessBuilder { } +/* + * Ensure ProcessBuilder is not reachable through the enclosing class of Redirect. + */ +@Delete +@TargetClass(className = "java.lang.ProcessBuilder", innerClass = "Redirect", onlyWith = {TruffleBaseFeature.IsEnabled.class, + TruffleBaseFeature.IsCreateProcessDisabled.class}) +final class Target_java_lang_ProcessBuilder_Redirect { +} + /* * If allowProcess() is disabled at build time, then we ensure ObjdumpDisassemblerProvider does not * try to invoke the nonexistent ProcessBuilder.