diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java index cf3ba615a4842..21d7de89e28ee 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java @@ -24,6 +24,7 @@ */ package org.graalvm.compiler.nodes; +import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE; import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; import java.util.ArrayList; @@ -506,7 +507,7 @@ private StructuredGraph(String name, } private static boolean checkIsSubstitutionInvariants(ResolvedJavaMethod method, boolean isSubstitution) { - if (!IS_IN_NATIVE_IMAGE) { + if (!IS_IN_NATIVE_IMAGE && !IS_BUILDING_NATIVE_IMAGE) { if (method != null) { if (method.getAnnotation(Snippet.class) != null) { assert isSubstitution : "Graph for method " + method.format("%H.%n(%p)") + diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 6169fd94d9cc7..03d23b4993c3a 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -439,7 +439,8 @@ "sun.text.spi", "jdk.internal.reflect", "sun.util.cldr", - "sun.util.locale" + "sun.util.locale", + "sun.invoke.util", ], "jdk.internal.vm.ci" : [ "jdk.vm.ci.meta", @@ -1130,6 +1131,8 @@ "com.oracle.svm.hosted.agent to java.instrument", "com.oracle.svm.truffle.api to org.graalvm.truffle", "com.oracle.svm.core.option to com.oracle.svm_enterprise.ml_dataset", + "com.oracle.svm.core.annotate", + "com.oracle.svm.core.graal.code", "* to org.graalvm.nativeimage.base,jdk.internal.vm.compiler,org.graalvm.nativeimage.driver,org.graalvm.nativeimage.configure,org.graalvm.nativeimage.librarysupport,org.graalvm.nativeimage.junitsupport,org.graalvm.nativeimage.llvm,org.graalvm.nativeimage.agent.jvmtibase,org.graalvm.nativeimage.agent.tracing,org.graalvm.nativeimage.agent.diagnostics,com.oracle.svm.svm_enterprise", ], "opens" : [ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java index cd319eb2d7eb5..4d588041bb3e0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java @@ -105,7 +105,7 @@ import org.graalvm.compiler.replacements.nodes.ObjectClone; import org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode; import org.graalvm.compiler.word.WordCastNode; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.flow.LoadFieldTypeFlow.LoadInstanceFieldTypeFlow; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaField.java new file mode 100644 index 0000000000000..e3741eaaac5ca --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaField.java @@ -0,0 +1,42 @@ +/* + * 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.graal.pointsto.infrastructure; + +import java.lang.reflect.AnnotatedElement; + +import com.oracle.svm.util.AnnotationWrapper; + +import jdk.vm.ci.meta.ResolvedJavaField; + +public interface WrappedJavaField extends WrappedElement, ResolvedJavaField, AnnotationWrapper { + + @Override + ResolvedJavaField getWrapped(); + + @Override + default AnnotatedElement getAnnotationRoot() { + return getWrapped(); + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaMethod.java index ec61abfae10f6..af882539cf00d 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaMethod.java @@ -24,10 +24,19 @@ */ package com.oracle.graal.pointsto.infrastructure; +import java.lang.reflect.AnnotatedElement; + +import com.oracle.svm.util.AnnotationWrapper; + import jdk.vm.ci.meta.ResolvedJavaMethod; -public interface WrappedJavaMethod extends WrappedElement, ResolvedJavaMethod { +public interface WrappedJavaMethod extends WrappedElement, ResolvedJavaMethod, AnnotationWrapper { @Override ResolvedJavaMethod getWrapped(); + + @Override + default AnnotatedElement getAnnotationRoot() { + return getWrapped(); + } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaType.java index 7bad263954353..783c6d56000cf 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/WrappedJavaType.java @@ -24,10 +24,19 @@ */ package com.oracle.graal.pointsto.infrastructure; +import java.lang.reflect.AnnotatedElement; + +import com.oracle.svm.util.AnnotationWrapper; + import jdk.vm.ci.meta.ResolvedJavaType; -public interface WrappedJavaType extends WrappedElement, ResolvedJavaType { +public interface WrappedJavaType extends WrappedElement, ResolvedJavaType, AnnotationWrapper { @Override ResolvedJavaType getWrapped(); + + @Override + default AnnotatedElement getAnnotationRoot() { + return getWrapped(); + } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java index 096884a1382a5..ecbb8e1f52228 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java @@ -24,7 +24,7 @@ */ package com.oracle.graal.pointsto.meta; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Set; @@ -34,7 +34,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.graalvm.compiler.debug.GraalError; -import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.api.DefaultUnsafePartition; import com.oracle.graal.pointsto.api.HostVM; @@ -42,16 +41,18 @@ import com.oracle.graal.pointsto.flow.ContextInsensitiveFieldTypeFlow; import com.oracle.graal.pointsto.flow.FieldTypeFlow; import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; +import com.oracle.graal.pointsto.infrastructure.WrappedJavaField; import com.oracle.graal.pointsto.typestate.TypeState; import com.oracle.graal.pointsto.util.AtomicUtils; import com.oracle.graal.pointsto.util.ConcurrentLightHashSet; +import com.oracle.svm.util.AnnotationWrapper; import com.oracle.svm.util.UnsafePartitionKind; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; -public abstract class AnalysisField extends AnalysisElement implements ResolvedJavaField, OriginalFieldProvider { +public abstract class AnalysisField extends AnalysisElement implements WrappedJavaField, OriginalFieldProvider, AnnotationWrapper { @SuppressWarnings("rawtypes")// private static final AtomicReferenceFieldUpdater OBSERVERS_UPDATER = // @@ -167,6 +168,11 @@ private static AnalysisType getDeclaredType(AnalysisUniverse universe, ResolvedJ return universe.lookup(resolvedType); } + @Override + public ResolvedJavaField getWrapped() { + return wrapped; + } + public void copyAccessInfos(AnalysisField other) { isAccessedUpdater.set(this, other.isAccessed); isUnsafeAccessedUpdater.set(this, other.isUnsafeAccessed); @@ -482,18 +488,8 @@ public boolean isStatic() { } @Override - public Annotation[] getAnnotations() { - return GuardedAnnotationAccess.getAnnotations(wrapped); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return GuardedAnnotationAccess.getDeclaredAnnotations(wrapped); - } - - @Override - public T getAnnotation(Class annotationClass) { - return GuardedAnnotationAccess.getAnnotation(wrapped, annotationClass); + public AnnotatedElement getAnnotationRoot() { + return wrapped; } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index 1da5f7cccd183..fa3b92bbf773c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -45,7 +45,6 @@ import org.graalvm.compiler.nodes.GraphDecoder; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; -import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.PointstoOptions; @@ -572,21 +571,6 @@ public ConstantPool getConstantPool() { return getUniverse().lookup(wrapped.getConstantPool(), getDeclaringClass()); } - @Override - public Annotation[] getAnnotations() { - return GuardedAnnotationAccess.getAnnotations(wrapped); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return GuardedAnnotationAccess.getDeclaredAnnotations(wrapped); - } - - @Override - public T getAnnotation(Class annotationClass) { - return GuardedAnnotationAccess.getAnnotation(wrapped, annotationClass); - } - @Override public Annotation[][] getParameterAnnotations() { return wrapped.getParameterAnnotations(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index 206e643b6dbcb..c8da7741aa9f5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -24,7 +24,6 @@ */ package com.oracle.graal.pointsto.meta; -import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,7 +41,6 @@ import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; -import org.graalvm.util.GuardedAnnotationAccess; import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.BigBang; @@ -1100,21 +1098,6 @@ public AnalysisField[] getStaticFields() { return convertFields(wrapped.getStaticFields(), new ArrayList<>(), false); } - @Override - public Annotation[] getAnnotations() { - return GuardedAnnotationAccess.getAnnotations(wrapped); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return GuardedAnnotationAccess.getDeclaredAnnotations(wrapped); - } - - @Override - public T getAnnotation(Class annotationClass) { - return GuardedAnnotationAccess.getAnnotation(wrapped, annotationClass); - } - @Override public String getSourceFileName() { // getSourceFileName is not implemented for primitive types diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java index ab768c1e14382..96f3464218ea8 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/LLVMGenerator.java @@ -82,7 +82,7 @@ import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.c.constant.CEnum; import org.graalvm.nativeimage.c.function.CEntryPoint; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.core.FrameAccess; import com.oracle.svm.core.ReservedRegisters; diff --git a/substratevm/src/com.oracle.svm.core/.checkstyle_checks.xml b/substratevm/src/com.oracle.svm.core/.checkstyle_checks.xml index 4f29bae26e218..75cc4e32ceb28 100644 --- a/substratevm/src/com.oracle.svm.core/.checkstyle_checks.xml +++ b/substratevm/src/com.oracle.svm.core/.checkstyle_checks.xml @@ -14,7 +14,7 @@ - + diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/StubCallingConvention.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/StubCallingConvention.java index 7e46df4033c15..742d1423a662f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/StubCallingConvention.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/StubCallingConvention.java @@ -31,7 +31,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.core.CalleeSavedRegisters; import com.oracle.svm.core.snippets.SubstrateForeignCallTarget; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Uninterruptible.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Uninterruptible.java index 85728d1bca114..2cc42fd7af691 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Uninterruptible.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/annotate/Uninterruptible.java @@ -33,8 +33,8 @@ import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer; -import org.graalvm.util.DirectAnnotationAccess; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import org.graalvm.word.WordBase; import com.oracle.svm.core.snippets.KnownIntrinsics; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java index 0cf4b073ded11..466234ce30120 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointErrors.java @@ -31,7 +31,7 @@ import java.lang.reflect.Field; import java.util.Arrays; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; import com.oracle.svm.core.util.VMError; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/libc/LibCBase.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/libc/LibCBase.java index 6aaf8b5acc243..3e3791e382a9a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/libc/LibCBase.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/libc/LibCBase.java @@ -33,7 +33,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; public interface LibCBase { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java index affaabcdada1b..edf1661e8b1e4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/config/ObjectLayout.java @@ -27,7 +27,7 @@ import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.nativeimage.c.constant.CEnum; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import org.graalvm.word.WordBase; import com.oracle.svm.core.SubstrateTargetDescription; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java index fc7cfe3282264..56ff4a4ccbc1a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java @@ -81,7 +81,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; import com.oracle.svm.core.SubstrateTargetDescription; import com.oracle.svm.core.config.ConfigurationValues; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/phases/SubstrateSafepointInsertionPhase.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/phases/SubstrateSafepointInsertionPhase.java index a33c73c6dc077..02c298be44091 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/phases/SubstrateSafepointInsertionPhase.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/phases/SubstrateSafepointInsertionPhase.java @@ -31,7 +31,7 @@ import org.graalvm.compiler.phases.tiers.MidTierContext; import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.graal.code.SubstrateBackend; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/word/SubstrateWordOperationPlugins.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/word/SubstrateWordOperationPlugins.java index 5f5c77299c711..a2bbb567017ad 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/word/SubstrateWordOperationPlugins.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/word/SubstrateWordOperationPlugins.java @@ -37,7 +37,7 @@ import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.word.WordOperationPlugin; import org.graalvm.compiler.word.WordTypes; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import org.graalvm.word.LocationIdentity; import jdk.vm.ci.meta.JavaKind; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/word/SubstrateWordTypes.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/word/SubstrateWordTypes.java index c2c0b25fc3503..7cd26258ab58b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/word/SubstrateWordTypes.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/word/SubstrateWordTypes.java @@ -26,7 +26,7 @@ import org.graalvm.compiler.word.WordTypes; import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.core.util.UserError; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/AnnotationTypeSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/AnnotationTypeSupport.java deleted file mode 100644 index a5e9bb649686c..0000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/AnnotationTypeSupport.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2017, 2017, 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.hub; - -import java.lang.annotation.Annotation; - -import org.graalvm.collections.EconomicMap; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.util.ImageHeapMap; - -import sun.reflect.annotation.AnnotationType; - -public class AnnotationTypeSupport { - private final EconomicMap, AnnotationType> annotationTypeMap = ImageHeapMap.create(); - - @Platforms(Platform.HOSTED_ONLY.class) - public void createInstance(Class annotationClass) { - annotationTypeMap.putIfAbsent(annotationClass, AnnotationType.getInstance(annotationClass)); - } - - public AnnotationType getInstance(Class annotationClass) { - return annotationTypeMap.get(annotationClass); - } - -} - -@TargetClass(className = "sun.reflect.annotation.AnnotationType") -final class Target_sun_reflect_annotation_AnnotationType { - - /** - * In JDK this class lazily initializes AnnotationTypes as they are requested. - * - * In SVM we analyze only the types that are used as {@link java.lang.annotation.Repeatable} - * annotations and pre-initialize those. - * - * If this method fails, introduce missing pre-initialization rules in AnnotationTypeFeature. - */ - @Substitute - public static AnnotationType getInstance(Class annotationClass) { - return ImageSingletons.lookup(AnnotationTypeSupport.class).getInstance(annotationClass); - } -} 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 a82da86a4888f..b3bcd1e82c0e1 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 @@ -61,7 +61,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunctionPointer; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; import com.oracle.svm.core.RuntimeAssertionsSupport; import com.oracle.svm.core.SubstrateUtil; @@ -1517,6 +1517,9 @@ private Class[] getPermittedSubclasses0() { @TargetElement(onlyWith = JDK17OrLater.class) private native boolean isDirectSubType(Class c); + @KeepOriginal + native boolean casAnnotationType(AnnotationType oldType, AnnotationType newType); + /* * We need to filter out hiding elements at the last moment. This ensures that the JDK internals * see them as regular methods and fields and ensure their visibility is correct, but they diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ClassLoaderSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ClassLoaderSupport.java index d484f6e667fc5..e82e84ee94ca5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ClassLoaderSupport.java @@ -31,7 +31,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.core.annotate.AutomaticFeature; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/LambdaFormHiddenMethod.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/LambdaFormHiddenMethod.java index d7df81962c2b9..8e06c72885fc0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/LambdaFormHiddenMethod.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/LambdaFormHiddenMethod.java @@ -30,7 +30,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; /** * Annotation for types whose methods are synthetic methods for lambda invocations, and ignored for diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java index 4b658705aff19..e770d2fab3974 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StackTraceUtils.java @@ -30,7 +30,7 @@ import java.util.ArrayList; import org.graalvm.nativeimage.IsolateThread; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; import org.graalvm.word.Pointer; import com.oracle.svm.core.SubstrateOptions; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_util_ServiceLoader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_util_ServiceLoader.java index 6fc97073eca50..2079b7c4ea886 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_util_ServiceLoader.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_util_ServiceLoader.java @@ -26,13 +26,18 @@ package com.oracle.svm.core.jdk; import java.lang.reflect.Constructor; +import java.net.URL; import java.security.AccessControlContext; +import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashSet; +import java.util.List; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.Set; import com.oracle.svm.core.annotate.Alias; +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; @@ -52,6 +57,9 @@ final class Target_java_util_ServiceLoader { @Alias native Constructor getConstructor(Class clazz); + + @Alias @RecomputeFieldValue(declClass = ArrayList.class, kind = RecomputeFieldValue.Kind.NewInstance)// + private List instantiatedProviders; } @TargetClass(value = java.util.ServiceLoader.class, innerClass = "ModuleServicesLookupIterator") @@ -85,6 +93,9 @@ final class Target_java_util_ServiceLoader_LazyClassPathLookupIterator { @TargetElement(name = "this$0")// Target_java_util_ServiceLoader outer; + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// + Enumeration configs; + @Substitute Target_java_util_ServiceLoader_LazyClassPathLookupIterator(Target_java_util_ServiceLoader outer) { this.outer = outer; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/SnippetRuntime.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/SnippetRuntime.java index d0d8bebb03ff0..78052d21ec2cd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/SnippetRuntime.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/SnippetRuntime.java @@ -31,7 +31,7 @@ import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; import org.graalvm.compiler.replacements.nodes.BinaryMathIntrinsicNode.BinaryOperation; import org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; import org.graalvm.word.LocationIdentity; import com.oracle.svm.core.annotate.Uninterruptible; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index dd1077bf0525c..4b3bc9503d2f1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -53,7 +53,7 @@ import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; import org.graalvm.nativeimage.hosted.RuntimeReflection; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.DefaultUnsafePartition; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java index c45dee0ff2a30..f7d0a6335af6b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java @@ -46,11 +46,14 @@ import java.util.stream.StreamSupport; import org.graalvm.collections.EconomicSet; +import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.TypeResult; +import com.oracle.svm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.ReflectionUtil; public final class ImageClassLoader { @@ -101,7 +104,7 @@ private void findSystemElements(Class systemClass) { } if (declaredMethods != null) { for (Method systemMethod : declaredMethods) { - if (annotationsAvailable(systemMethod) && NativeImageGenerator.includedIn(platform, systemMethod.getAnnotation(Platforms.class))) { + if (isInPlatform(systemMethod)) { synchronized (systemMethods) { systemMethods.add(systemMethod); } @@ -117,7 +120,7 @@ private void findSystemElements(Class systemClass) { } if (declaredFields != null) { for (Field systemField : declaredFields) { - if (annotationsAvailable(systemField) && NativeImageGenerator.includedIn(platform, systemField.getAnnotation(Platforms.class))) { + if (isInPlatform(systemField)) { synchronized (systemFields) { systemFields.add(systemField); } @@ -126,30 +129,10 @@ private void findSystemElements(Class systemClass) { } } - /** - * @param element The element to check - * @return Returns true if the annotations on the {@code element} can be loaded without any - * errors. - */ - private static boolean canLoadAnnotations(AnnotatedElement element) { - try { - element.getAnnotations(); - return true; - } catch (Throwable t) { - handleClassLoadingError(t); - return false; - } - } - - /** - * @param element The element to check - * @return Returns true if and only if the the {@code element} has any annotations present and - * the {@link AnnotatedElement#getAnnotations()} did not throw any error. - */ - private static boolean annotationsAvailable(AnnotatedElement element) { + private boolean isInPlatform(AnnotatedElement element) { try { - final Annotation[] annotations = element.getAnnotations(); - return annotations.length != 0; + Platforms platformAnnotation = GuardedAnnotationAccess.getAnnotation(classLoaderSupport.annotationExtracter, element, Platforms.class); + return NativeImageGenerator.includedIn(platform, platformAnnotation); } catch (Throwable t) { handleClassLoadingError(t); return false; @@ -161,7 +144,16 @@ static void handleClassLoadingError(Throwable t) { /* we ignore class loading errors due to incomplete paths that people often have */ } + private static final Field classAnnotationData = ReflectionUtil.lookupField(Class.class, "annotationData"); + void handleClass(Class clazz) { + Object initialAnnotationData; + try { + initialAnnotationData = classAnnotationData.get(clazz); + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); + } + boolean inPlatform = true; boolean isHostedOnly = false; @@ -170,10 +162,13 @@ void handleClass(Class clazz) { cur = clazz; } do { - if (!canLoadAnnotations(cur)) { + Platforms platformsAnnotation; + try { + platformsAnnotation = GuardedAnnotationAccess.getAnnotation(classLoaderSupport.annotationExtracter, cur, Platforms.class); + } catch (Throwable t) { + handleClassLoadingError(t); return; } - Platforms platformsAnnotation = cur.getAnnotation(Platforms.class); if (containsHostedOnly(platformsAnnotation)) { isHostedOnly = true; } else if (!NativeImageGenerator.includedIn(platform, platformsAnnotation)) { @@ -205,6 +200,16 @@ void handleClass(Class clazz) { findSystemElements(clazz); } } + + try { + /* + * Annotations should not be computed during the scanning of classes, to avoid issues + * with the Native Image module access setup. + */ + assert classAnnotationData.get(clazz) == initialAnnotationData; + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(e); + } } private static boolean containsHostedOnly(Platforms platformsAnnotation) { @@ -350,9 +355,9 @@ public List> findAnnotatedClasses(Class annotatio return result; } - private static void addAnnotatedClasses(EconomicSet> classes, Class annotationClass, ArrayList> result) { + private void addAnnotatedClasses(EconomicSet> classes, Class annotationClass, ArrayList> result) { for (Class systemClass : classes) { - if (systemClass.getAnnotation(annotationClass) != null) { + if (GuardedAnnotationAccess.isAnnotationPresent(classLoaderSupport.annotationExtracter, systemClass, annotationClass)) { result.add(systemClass); } } @@ -361,7 +366,7 @@ private static void addAnnotatedClasses(EconomicSet> classes, Class findAnnotatedMethods(Class annotationClass) { ArrayList result = new ArrayList<>(); for (Method method : systemMethods) { - if (method.getAnnotation(annotationClass) != null) { + if (GuardedAnnotationAccess.isAnnotationPresent(classLoaderSupport.annotationExtracter, method, annotationClass)) { result.add(method); } } @@ -373,7 +378,7 @@ public List findAnnotatedMethods(Class[] annotatio for (Method method : systemMethods) { boolean match = true; for (Class annotationClass : annotationClasses) { - if (method.getAnnotation(annotationClass) == null) { + if (!GuardedAnnotationAccess.isAnnotationPresent(classLoaderSupport.annotationExtracter, method, annotationClass)) { match = false; break; } @@ -388,7 +393,7 @@ public List findAnnotatedMethods(Class[] annotatio public List findAnnotatedFields(Class annotationClass) { ArrayList result = new ArrayList<>(); for (Field field : systemFields) { - if (field.getAnnotation(annotationClass) != null) { + if (GuardedAnnotationAccess.isAnnotationPresent(classLoaderSupport.annotationExtracter, field, annotationClass)) { result.add(field); } } @@ -410,13 +415,13 @@ public List> allAnnotations() { public List findAnnotations(Class annotationClass) { List result = new ArrayList<>(); for (Class clazz : findAnnotatedClasses(annotationClass, false)) { - result.add(clazz.getAnnotation(annotationClass)); + result.add(GuardedAnnotationAccess.getAnnotation(classLoaderSupport.annotationExtracter, clazz, annotationClass)); } for (Method method : findAnnotatedMethods(annotationClass)) { - result.add(method.getAnnotation(annotationClass)); + result.add(GuardedAnnotationAccess.getAnnotation(classLoaderSupport.annotationExtracter, method, annotationClass)); } for (Field field : findAnnotatedFields(annotationClass)) { - result.add(field.getAnnotation(annotationClass)); + result.add(GuardedAnnotationAccess.getAnnotation(classLoaderSupport.annotationExtracter, field, annotationClass)); } return result; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index 40750d7356b49..b666c7e990fbd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -65,6 +65,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.oracle.svm.hosted.annotation.SubstrateAnnotationExtracter; +import com.oracle.svm.util.AnnotationExtracter; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicSet; import org.graalvm.collections.Pair; @@ -102,6 +104,8 @@ public class NativeImageClassLoaderSupport { public final ModuleLayer moduleLayerForImageBuild; public final ModuleFinder modulepathModuleFinder; + public final AnnotationExtracter annotationExtracter; + static final class ClassPathClassLoader extends URLClassLoader { ClassPathClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); @@ -149,6 +153,8 @@ protected NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, St classLoader = getSingleClassloader(moduleLayer); modulepathModuleFinder = ModuleFinder.of(modulepath().toArray(Path[]::new)); + + annotationExtracter = new SubstrateAnnotationExtracter(); } List classpath() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 49774b73e947b..fe47c530dfcc2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -55,6 +55,7 @@ import java.util.function.BooleanSupplier; import java.util.stream.Collectors; +import com.oracle.svm.util.AnnotationExtracter; import org.graalvm.collections.EconomicSet; import org.graalvm.collections.Pair; import org.graalvm.compiler.api.replacements.Fold; @@ -510,6 +511,7 @@ public void run(Map entryPoints, ImageSingletons.add(ProgressReporter.class, reporter); ImageSingletons.add(TimerCollection.class, timerCollection); ImageSingletons.add(ImageBuildStatistics.TimerCollectionPrinter.class, timerCollection); + ImageSingletons.add(AnnotationExtracter.class, loader.classLoaderSupport.annotationExtracter); ImageSingletons.add(BuildArtifacts.class, (type, artifact) -> buildArtifacts.computeIfAbsent(type, t -> new ArrayList<>()).add(artifact)); ImageSingletons.add(HostedOptionValues.class, new HostedOptionValues(optionProvider.getHostedValues())); ImageSingletons.add(RuntimeOptionValues.class, new RuntimeOptionValues(optionProvider.getRuntimeValues(), allOptionNames)); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 3ca2e0f3607bd..7d87c7fb75ad6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -66,7 +66,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.RelocatedPointer; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.PointsToAnalysis; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationArrayValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationArrayValue.java new file mode 100644 index 0000000000000..2b2700f516091 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationArrayValue.java @@ -0,0 +1,145 @@ +/* + * 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.hosted.annotation; + +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import jdk.internal.reflect.ConstantPool; +import jdk.vm.ci.meta.JavaConstant; +import sun.reflect.annotation.ExceptionProxy; + +public final class AnnotationArrayValue implements AnnotationMemberValue { + private static final AnnotationArrayValue EMPTY_ARRAY_VALUE = new AnnotationArrayValue(); + + private final AnnotationMemberValue[] elements; + + static AnnotationArrayValue extract(ByteBuffer buf, ConstantPool cp, Class container, boolean skip) { + int length = buf.getShort() & 0xFFFF; + if (length == 0) { + return EMPTY_ARRAY_VALUE; + } + AnnotationMemberValue[] elements = new AnnotationMemberValue[length]; + for (int i = 0; i < length; ++i) { + elements[i] = AnnotationMemberValue.extract(buf, cp, container, skip); + } + return skip ? null : new AnnotationArrayValue(elements); + } + + AnnotationArrayValue(Class elementType, Object[] values) { + this.elements = new AnnotationMemberValue[values.length]; + for (int i = 0; i < values.length; ++i) { + this.elements[i] = AnnotationMemberValue.from(elementType, values[i]); + } + } + + private AnnotationArrayValue(AnnotationMemberValue... elements) { + this.elements = elements; + } + + public int getElementCount() { + return elements.length; + } + + public void forEachElement(Consumer callback) { + for (AnnotationMemberValue element : elements) { + callback.accept(element); + } + } + + @Override + public List> getTypes() { + List> types = new ArrayList<>(); + for (AnnotationMemberValue element : elements) { + types.addAll(element.getTypes()); + } + return types; + } + + @Override + public List getStrings() { + List strings = new ArrayList<>(); + for (AnnotationMemberValue element : elements) { + strings.addAll(element.getStrings()); + } + return strings; + } + + @Override + public List getExceptionProxies() { + List exceptionProxies = new ArrayList<>(); + for (AnnotationMemberValue element : elements) { + exceptionProxies.addAll(element.getExceptionProxies()); + } + return exceptionProxies; + } + + @Override + public char getTag() { + return '['; + } + + @Override + public Object get(Class memberType) { + Class componentType = memberType.getComponentType(); + Object[] result = (Object[]) Array.newInstance(memberType.getComponentType(), elements.length); + int tag = 0; + boolean typeMismatch = false; + for (int i = 0; i < elements.length; ++i) { + Object value = elements[i].get(componentType); + if (value instanceof ExceptionProxy) { + typeMismatch = true; + tag = elements[i].getTag(); + } else { + result[i] = value; + } + } + if (typeMismatch) { + return AnnotationMetadata.createAnnotationTypeMismatchExceptionProxy("Array with component tag: " + (tag == 0 ? "0" : (char) tag)); + } + return AnnotationMetadata.checkResult(result, memberType); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnnotationArrayValue that = (AnnotationArrayValue) o; + return Arrays.equals(elements, that.elements); + } + + @Override + public int hashCode() { + return Arrays.hashCode(elements); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationClassValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationClassValue.java new file mode 100644 index 0000000000000..1e79f2c622041 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationClassValue.java @@ -0,0 +1,88 @@ +/* + * 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.hosted.annotation; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import jdk.internal.reflect.ConstantPool; +import sun.reflect.annotation.ExceptionProxy; + +public final class AnnotationClassValue implements AnnotationMemberValue { + private final Class value; + + static AnnotationMemberValue extract(ByteBuffer buf, ConstantPool cp, Class container, boolean skip) { + Object typeOrException = AnnotationMetadata.extractType(buf, cp, container, skip); + if (skip) { + return null; + } + if (typeOrException instanceof ExceptionProxy) { + return new AnnotationExceptionProxyValue((ExceptionProxy) typeOrException); + } + return new AnnotationClassValue((Class) typeOrException); + } + + AnnotationClassValue(Class value) { + this.value = value; + } + + public Class getValue() { + return value; + } + + @Override + public List> getTypes() { + return Collections.singletonList(value); + } + + @Override + public char getTag() { + return 'c'; + } + + @Override + public Object get(Class memberType) { + return AnnotationMetadata.checkResult(value, memberType); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnnotationClassValue that = (AnnotationClassValue) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationEnumValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationEnumValue.java new file mode 100644 index 0000000000000..c78d69bc3c7bf --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationEnumValue.java @@ -0,0 +1,109 @@ +/* + * 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.hosted.annotation; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import jdk.internal.reflect.ConstantPool; +import sun.reflect.annotation.ExceptionProxy; + +public final class AnnotationEnumValue implements AnnotationMemberValue { + private final Class> type; + private final String name; + + @SuppressWarnings({"unchecked"}) + static AnnotationMemberValue extract(ByteBuffer buf, ConstantPool cp, Class container, boolean skip) { + Object typeOrException = AnnotationMetadata.extractType(buf, cp, container, skip); + String constName = AnnotationMetadata.extractString(buf, cp, skip); + if (skip) { + return null; + } + if (typeOrException instanceof ExceptionProxy) { + return new AnnotationExceptionProxyValue((ExceptionProxy) typeOrException); + } + Class> type = (Class>) typeOrException; + return new AnnotationEnumValue(type, constName); + } + + AnnotationEnumValue(Enum value) { + this.type = value.getDeclaringClass(); + this.name = value.name(); + } + + private AnnotationEnumValue(Class> type, String name) { + this.type = type; + this.name = name; + } + + public Class> getType() { + return type; + } + + public String getName() { + return name; + } + + @Override + public List> getTypes() { + return Collections.singletonList(type); + } + + @Override + public List getStrings() { + return Collections.singletonList(name); + } + + @Override + public char getTag() { + return 'e'; + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public Object get(Class memberType) { + Enum> value = Enum.valueOf((Class) type, name); + return AnnotationMetadata.checkResult(value, memberType); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnnotationEnumValue enumValue = (AnnotationEnumValue) o; + return Objects.equals(type, enumValue.type) && Objects.equals(name, enumValue.name); + } + + @Override + public int hashCode() { + return Objects.hash(type, name); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationExceptionProxyValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationExceptionProxyValue.java new file mode 100644 index 0000000000000..bbc45bce56de9 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationExceptionProxyValue.java @@ -0,0 +1,67 @@ +/* + * 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.hosted.annotation; + +import java.util.Collections; +import java.util.List; + +import com.oracle.svm.core.meta.SubstrateObjectConstant; + +import jdk.vm.ci.meta.JavaConstant; +import sun.reflect.annotation.ExceptionProxy; + +public final class AnnotationExceptionProxyValue implements AnnotationMemberValue { + private final ExceptionProxy exceptionProxy; + private final JavaConstant objectConstant; + + AnnotationExceptionProxyValue(ExceptionProxy exceptionProxy) { + this.exceptionProxy = exceptionProxy; + this.objectConstant = SubstrateObjectConstant.forObject(exceptionProxy); + } + + public JavaConstant getObjectConstant() { + return objectConstant; + } + + @Override + public char getTag() { + return 'E'; + } + + @Override + public Object get(Class memberType) { + return exceptionProxy; + } + + @Override + public List> getTypes() { + return Collections.singletonList(exceptionProxy.getClass()); + } + + @Override + public List getExceptionProxies() { + return Collections.singletonList(objectConstant); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMemberValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMemberValue.java new file mode 100644 index 0000000000000..c77364580ef5e --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMemberValue.java @@ -0,0 +1,85 @@ +/* + * 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.hosted.annotation; + +import java.lang.annotation.Annotation; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; + +import jdk.internal.reflect.ConstantPool; +import jdk.vm.ci.meta.JavaConstant; + +public interface AnnotationMemberValue { + static AnnotationMemberValue extract(ByteBuffer buf, ConstantPool cp, Class container, boolean skip) { + char tag = (char) buf.get(); + switch (tag) { + case 'e': + return AnnotationEnumValue.extract(buf, cp, container, skip); + case 'c': + return AnnotationClassValue.extract(buf, cp, container, skip); + case 's': + return AnnotationStringValue.extract(buf, cp, skip); + case '@': + return AnnotationValue.extract(buf, cp, container, true, skip); + case '[': + return AnnotationArrayValue.extract(buf, cp, container, skip); + default: + return AnnotationPrimitiveValue.extract(buf, cp, tag, skip); + } + } + + static AnnotationMemberValue from(Class type, Object value) { + if (type.isAnnotation()) { + return new AnnotationValue((Annotation) value); + } else if (type.isEnum()) { + return new AnnotationEnumValue((Enum) value); + } else if (type == Class.class) { + return new AnnotationClassValue((Class) value); + } else if (type == String.class) { + return new AnnotationStringValue((String) value); + } else if (type.isArray()) { + return new AnnotationArrayValue(type.getComponentType(), (Object[]) value); + } else { + return new AnnotationPrimitiveValue(type, value); + } + } + + default List> getTypes() { + return Collections.emptyList(); + } + + default List getStrings() { + return Collections.emptyList(); + } + + default List getExceptionProxies() { + return Collections.emptyList(); + } + + char getTag(); + + Object get(Class memberType); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMetadata.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMetadata.java new file mode 100644 index 0000000000000..66e956bbe5f39 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationMetadata.java @@ -0,0 +1,109 @@ +/* + * 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.hosted.annotation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; + +import org.graalvm.compiler.debug.GraalError; + +import com.oracle.svm.util.ReflectionUtil; + +import jdk.internal.reflect.ConstantPool; +import sun.reflect.annotation.AnnotationParser; +import sun.reflect.annotation.TypeNotPresentExceptionProxy; + +public class AnnotationMetadata { + + @SuppressWarnings("serial") + static final class AnnotationExtractionError extends Error { + AnnotationExtractionError(Throwable cause) { + super(cause); + } + + AnnotationExtractionError(String message) { + super(message); + } + } + + private static final Method annotationParserParseSig = ReflectionUtil.lookupMethod(AnnotationParser.class, "parseSig", String.class, Class.class); + private static final Constructor annotationTypeMismatchExceptionProxyConstructor; + + static { + try { + annotationTypeMismatchExceptionProxyConstructor = ReflectionUtil.lookupConstructor(Class.forName("sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy"), String.class); + } catch (ClassNotFoundException e) { + throw GraalError.shouldNotReachHere(); + } + } + + static Object extractType(ByteBuffer buf, ConstantPool cp, Class container, boolean skip) { + int typeIndex = buf.getShort() & 0xFFFF; + if (skip) { + return null; + } + Class type; + String signature = cp.getUTF8At(typeIndex); + try { + type = (Class) annotationParserParseSig.invoke(null, signature, container); + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + if (targetException instanceof LinkageError || targetException instanceof TypeNotPresentException) { + return new TypeNotPresentExceptionProxy(signature, targetException); + } + throw new AnnotationExtractionError(e); + } catch (ReflectiveOperationException e) { + throw new AnnotationExtractionError(e); + } + return type; + } + + static String extractString(ByteBuffer buf, ConstantPool cp, boolean skip) { + int index = buf.getShort() & 0xFFFF; + return skip ? null : cp.getUTF8At(index); + } + + static Object checkResult(Object value, Class expectedType) { + if (!expectedType.isInstance(value)) { + if (value instanceof Annotation) { + return createAnnotationTypeMismatchExceptionProxy(value.toString()); + } else { + return createAnnotationTypeMismatchExceptionProxy(value.getClass().getName() + "[" + value + "]"); + } + } + return value; + } + + static Object createAnnotationTypeMismatchExceptionProxy(String message) { + try { + return annotationTypeMismatchExceptionProxyConstructor.newInstance(message); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new AnnotationExtractionError(e); + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationPrimitiveValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationPrimitiveValue.java new file mode 100644 index 0000000000000..3487e7a6d291f --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationPrimitiveValue.java @@ -0,0 +1,114 @@ +/* + * 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.hosted.annotation; + +import java.nio.ByteBuffer; +import java.util.Objects; + +import jdk.internal.reflect.ConstantPool; +import sun.invoke.util.Wrapper; + +public final class AnnotationPrimitiveValue implements AnnotationMemberValue { + private final char tag; + private final Object value; + + static AnnotationPrimitiveValue extract(ByteBuffer buf, ConstantPool cp, char tag, boolean skip) { + int constIndex = buf.getShort() & 0xFFFF; + if (skip) { + return null; + } + Object value; + switch (tag) { + case 'B': + value = (byte) cp.getIntAt(constIndex); + break; + case 'C': + value = (char) cp.getIntAt(constIndex); + break; + case 'D': + value = cp.getDoubleAt(constIndex); + break; + case 'F': + value = cp.getFloatAt(constIndex); + break; + case 'I': + value = cp.getIntAt(constIndex); + break; + case 'J': + value = cp.getLongAt(constIndex); + break; + case 'S': + value = (short) cp.getIntAt(constIndex); + break; + case 'Z': + value = cp.getIntAt(constIndex) != 0; + break; + default: + throw new AnnotationMetadata.AnnotationExtractionError("Invalid annotation encoding. Unknown tag " + tag); + } + assert Wrapper.forWrapperType(value.getClass()).basicTypeChar() == tag; + return new AnnotationPrimitiveValue(tag, value); + } + + AnnotationPrimitiveValue(Class type, Object value) { + this((type.isPrimitive() ? Wrapper.forPrimitiveType(type) : Wrapper.forWrapperType(type)).basicTypeChar(), value); + } + + private AnnotationPrimitiveValue(char tag, Object value) { + this.tag = tag; + this.value = value; + } + + @Override + public char getTag() { + return tag; + } + + public Object getValue() { + return value; + } + + @Override + public Object get(Class memberType) { + return AnnotationMetadata.checkResult(value, memberType); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnnotationPrimitiveValue that = (AnnotationPrimitiveValue) o; + return tag == that.tag && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(tag, value); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationStringValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationStringValue.java new file mode 100644 index 0000000000000..3153aa8ff23c3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationStringValue.java @@ -0,0 +1,81 @@ +/* + * 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.hosted.annotation; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import jdk.internal.reflect.ConstantPool; + +public final class AnnotationStringValue implements AnnotationMemberValue { + private final String value; + + static AnnotationStringValue extract(ByteBuffer buf, ConstantPool cp, boolean skip) { + String value = AnnotationMetadata.extractString(buf, cp, skip); + return skip ? null : new AnnotationStringValue(value); + } + + AnnotationStringValue(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public List getStrings() { + return Collections.singletonList(value); + } + + @Override + public char getTag() { + return 's'; + } + + @Override + public Object get(Class memberType) { + return AnnotationMetadata.checkResult(value, memberType); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnnotationStringValue that = (AnnotationStringValue) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java index 81baa14f6f26b..3ca17516116c3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java @@ -24,39 +24,16 @@ */ package com.oracle.svm.hosted.annotation; -import java.lang.annotation.Annotation; -import java.lang.annotation.Repeatable; -import java.lang.reflect.AnnotatedElement; -import java.util.Objects; -import java.util.stream.Stream; - -import org.graalvm.collections.EconomicSet; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.hub.AnnotationTypeSupport; -import com.oracle.svm.hosted.FeatureImpl.AfterRegistrationAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; @AutomaticFeature public class AnnotationTypeFeature implements Feature { - private EconomicSet repeatableAnnotationClasses = EconomicSet.create(); - private EconomicSet visitedElements = EconomicSet.create(); - - @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(AnnotationTypeSupport.class, new AnnotationTypeSupport()); - ((AfterRegistrationAccessImpl) access).getImageClassLoader().allAnnotations().stream() - .map(a -> a.getAnnotation(Repeatable.class)) - .filter(Objects::nonNull) - .map(Repeatable::value) - .forEach(repeatableAnnotationClasses::add); - } - @Override public void duringAnalysis(DuringAnalysisAccess access) { DuringAnalysisAccessImpl accessImpl = (DuringAnalysisAccessImpl) access; @@ -76,22 +53,5 @@ public void duringAnalysis(DuringAnalysisAccess access) { accessImpl.registerAsInHeap(annotationArray); access.requireAnalysisIteration(); }); - Stream allElements = Stream.concat(Stream.concat(universe.getFields().stream(), universe.getMethods().stream()), universe.getTypes().stream()); - Stream newElements = allElements.filter(visitedElements::add); - newElements.forEach(this::reportAnnotation); - } - - private void reportAnnotation(AnnotatedElement element) { - for (Annotation annotation : element.getDeclaredAnnotations()) { - if (repeatableAnnotationClasses.contains(annotation.annotationType())) { - ImageSingletons.lookup(AnnotationTypeSupport.class).createInstance(annotation.annotationType()); - } - } - } - - @Override - public void afterAnalysis(AfterAnalysisAccess access) { - repeatableAnnotationClasses.clear(); - visitedElements.clear(); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationValue.java new file mode 100644 index 0000000000000..ba7d9e4a7245d --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationValue.java @@ -0,0 +1,175 @@ +/* + * 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.hosted.annotation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; + +import jdk.internal.reflect.ConstantPool; +import jdk.vm.ci.meta.JavaConstant; +import sun.reflect.annotation.AnnotationParser; +import sun.reflect.annotation.AnnotationType; +import sun.reflect.annotation.TypeNotPresentExceptionProxy; + +public final class AnnotationValue implements AnnotationMemberValue { + private static final Map resolvedAnnotations = new ConcurrentHashMap<>(); + + final Class type; + final Map members; + + @SuppressWarnings("unchecked") + static AnnotationValue extract(ByteBuffer buf, ConstantPool cp, Class container, boolean exceptionOnMissingAnnotationClass, boolean skip) { + boolean skipMembers = skip; + Object typeOrException = AnnotationMetadata.extractType(buf, cp, container, skip); + if (typeOrException instanceof TypeNotPresentExceptionProxy) { + if (exceptionOnMissingAnnotationClass) { + TypeNotPresentExceptionProxy proxy = (TypeNotPresentExceptionProxy) typeOrException; + throw new TypeNotPresentException(proxy.typeName(), proxy.getCause()); + } + skipMembers = true; + } + + int numMembers = buf.getShort() & 0xFFFF; + Map memberValues = new LinkedHashMap<>(); + for (int i = 0; i < numMembers; i++) { + String memberName = AnnotationMetadata.extractString(buf, cp, skipMembers); + AnnotationMemberValue memberValue = AnnotationMemberValue.extract(buf, cp, container, skipMembers); + if (!skipMembers) { + memberValues.put(memberName, memberValue); + } + } + + if (skipMembers) { + return null; + } + Class type = (Class) typeOrException; + return new AnnotationValue(type, memberValues); + } + + AnnotationValue(Annotation annotation) { + this.type = annotation.annotationType(); + this.members = new LinkedHashMap<>(); + AnnotationType annotationType = AnnotationType.getInstance(type); + annotationType.members().forEach((memberName, memberAccessor) -> { + AnnotationMemberValue memberValue; + try { + memberValue = AnnotationMemberValue.from(annotationType.memberTypes().get(memberName), memberAccessor.invoke(annotation)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new AnnotationMetadata.AnnotationExtractionError(e); + } + Object memberDefault = annotationType.memberDefaults().get(memberName); + if (!memberValue.equals(memberDefault)) { + members.put(memberName, memberValue); + } + }); + } + + private AnnotationValue(Class type, Map members) { + this.type = type; + this.members = members; + } + + public Class getType() { + return type; + } + + public int getMemberCount() { + return members.size(); + } + + public void forEachMember(BiConsumer callback) { + members.forEach(callback); + } + + @Override + public List> getTypes() { + List> types = new ArrayList<>(); + types.add(type); + for (AnnotationMemberValue memberValue : members.values()) { + types.addAll(memberValue.getTypes()); + } + return types; + } + + @Override + public List getStrings() { + List strings = new ArrayList<>(); + members.forEach((memberName, memberValue) -> { + strings.add(memberName); + strings.addAll(memberValue.getStrings()); + }); + return strings; + } + + @Override + public List getExceptionProxies() { + List exceptionProxies = new ArrayList<>(); + for (AnnotationMemberValue memberValue : members.values()) { + exceptionProxies.addAll(memberValue.getExceptionProxies()); + } + return exceptionProxies; + } + + @Override + public char getTag() { + return '@'; + } + + @Override + public Object get(Class memberType) { + Annotation value = resolvedAnnotations.computeIfAbsent(this, annotationValue -> { + AnnotationType annotationType = AnnotationType.getInstance(annotationValue.type); + Map memberValues = new LinkedHashMap<>(annotationType.memberDefaults()); + annotationValue.members.forEach((memberName, memberValue) -> memberValues.put(memberName, memberValue.get(annotationType.memberTypes().get(memberName)))); + return AnnotationParser.annotationForMap(type, memberValues); + }); + return AnnotationMetadata.checkResult(value, memberType); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnnotationValue that = (AnnotationValue) o; + return Objects.equals(type, that.type) && members.equals(that.members); + } + + @Override + public int hashCode() { + return Objects.hash(type, members); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java index f45f92dcc3419..3210eb32bd18a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java @@ -24,11 +24,12 @@ */ package com.oracle.svm.hosted.annotation; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.util.GraalAccess; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.Assumptions; import jdk.vm.ci.meta.JavaConstant; @@ -44,7 +45,7 @@ * simply forwards calls to the original ResolvedJavaType. See * AnnotationSupport#constantAnnotationMarkerSubstitutionType for more details. */ -public class ConstantAnnotationMarkerSubstitutionType implements ResolvedJavaType, OriginalClassProvider { +public class ConstantAnnotationMarkerSubstitutionType implements ResolvedJavaType, OriginalClassProvider, AnnotationWrapper { private final ResolvedJavaType original; private final SubstitutionProcessor substitutionProcessor; @@ -270,17 +271,7 @@ public boolean isCloneableWithAllocation() { } @Override - public T getAnnotation(Class annotationClass) { - return original.getAnnotation(annotationClass); - } - - @Override - public Annotation[] getAnnotations() { - return original.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return original.getDeclaredAnnotations(); + public AnnotatedElement getAnnotationRoot() { + return original; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionField.java index fc3f83866614e..9a9433be7ea8d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionField.java @@ -24,17 +24,18 @@ */ package com.oracle.svm.hosted.annotation; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; import com.oracle.svm.core.meta.ReadableJavaField; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.ResolvedJavaType; -public abstract class CustomSubstitutionField implements ReadableJavaField, OriginalFieldProvider { +public abstract class CustomSubstitutionField implements ReadableJavaField, OriginalFieldProvider, AnnotationWrapper { protected final ResolvedJavaType declaringClass; @@ -68,17 +69,7 @@ public ResolvedJavaType getDeclaringClass() { } @Override - public Annotation[] getAnnotations() { - return new Annotation[0]; - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return new Annotation[0]; - } - - @Override - public T getAnnotation(Class annotationClass) { + public AnnotatedElement getAnnotationRoot() { return null; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java index bc1594d278dd0..819f0fa936519 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java @@ -27,12 +27,14 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Executable; import java.lang.reflect.Type; import com.oracle.graal.pointsto.infrastructure.GraphProvider; import com.oracle.graal.pointsto.infrastructure.OriginalMethodProvider; import com.oracle.graal.pointsto.util.GraalAccess; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantPool; @@ -45,7 +47,7 @@ import jdk.vm.ci.meta.Signature; import jdk.vm.ci.meta.SpeculationLog; -public abstract class CustomSubstitutionMethod implements ResolvedJavaMethod, GraphProvider, OriginalMethodProvider { +public abstract class CustomSubstitutionMethod implements ResolvedJavaMethod, GraphProvider, OriginalMethodProvider, AnnotationWrapper { protected final ResolvedJavaMethod original; @@ -173,18 +175,8 @@ public ConstantPool getConstantPool() { } @Override - public Annotation[] getAnnotations() { - return original.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return original.getDeclaredAnnotations(); - } - - @Override - public T getAnnotation(Class annotationClass) { - return original.getAnnotation(annotationClass); + public AnnotatedElement getAnnotationRoot() { + return original; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java index fcff66ac0f29c..f64edd471de05 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java @@ -24,7 +24,7 @@ */ package com.oracle.svm.hosted.annotation; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -32,6 +32,7 @@ import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.util.GraalAccess; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.meta.Assumptions.AssumptionResult; @@ -47,7 +48,7 @@ * @param The type of fields in the substitution type * @param The type of methods in the substitution type */ -public abstract class CustomSubstitutionType implements ResolvedJavaType, OriginalClassProvider { +public abstract class CustomSubstitutionType implements ResolvedJavaType, OriginalClassProvider, AnnotationWrapper { protected final ResolvedJavaType original; protected final List fields; @@ -244,18 +245,8 @@ public ResolvedJavaField[] getStaticFields() { } @Override - public Annotation[] getAnnotations() { - return original.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return original.getDeclaredAnnotations(); - } - - @Override - public T getAnnotation(Class annotationClass) { - return original.getAnnotation(annotationClass); + public AnnotatedElement getAnnotationRoot() { + return original; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/SubstrateAnnotationExtracter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/SubstrateAnnotationExtracter.java new file mode 100644 index 0000000000000..e91881aa2bb2f --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/SubstrateAnnotationExtracter.java @@ -0,0 +1,454 @@ +/* + * 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.hosted.annotation; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; +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.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.graalvm.compiler.debug.GraalError; + +import com.oracle.svm.hosted.annotation.AnnotationMetadata.AnnotationExtractionError; +import com.oracle.svm.util.AnnotationExtracter; +import com.oracle.svm.util.AnnotationWrapper; +import com.oracle.svm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.ReflectionUtil; + +import jdk.internal.reflect.ConstantPool; +import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; +import jdk.vm.ci.hotspot.HotSpotResolvedJavaField; +import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; +import jdk.vm.ci.hotspot.HotSpotResolvedJavaType; +import jdk.vm.ci.hotspot.HotSpotResolvedObjectType; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import sun.reflect.annotation.AnnotationParser; + +/** + * This class wraps all annotation accesses during the Native Image build. This is necessary to + * avoid initializing classes that should be initialized at run-time, since looking up annotations + * through the JDK's {@link AnnotationParser} initializes the class of every annotation on the + * queried element. + * + * When queried, the extracter looks for the root of the provided element, which can be a + * {@link Field}, {@link Method}, {@link Constructor} or {@link Class} object, as well as a record + * component on JDK 17. It then looks into the byte arrays representing annotations in the root + * object and outputs wrapper classes containing all the information necessary to reconstruct the + * annotation on demand in an {@link AnnotationValue} or {@link TypeAnnotationValue} object or any + * subclass of {@link AnnotationMemberValue}. The actual annotation can then be created using the + * {@link AnnotationMemberValue#get(Class)} method. + * + * The {@link SubstrateAnnotationExtracter} is tightly coupled with {@link GuardedAnnotationAccess}, + * which provides implementations of {@link AnnotatedElement#isAnnotationPresent(Class)} and + * {@link AnnotatedElement#getAnnotation(Class)}. {@link AnnotatedElement#getAnnotations()} should + * in principle not be used during Native Image generation. + */ +public class SubstrateAnnotationExtracter implements AnnotationExtracter { + private final Map, AnnotationValue[]> annotationCache = new ConcurrentHashMap<>(); + private final Map declaredAnnotationCache = new ConcurrentHashMap<>(); + private final Map parameterAnnotationCache = new ConcurrentHashMap<>(); + private final Map typeAnnotationCache = new ConcurrentHashMap<>(); + private final Map annotationDefaultCache = new ConcurrentHashMap<>(); + + private static final AnnotationValue[] NO_ANNOTATIONS = new AnnotationValue[0]; + private static final AnnotationValue[][] NO_PARAMETER_ANNOTATIONS = new AnnotationValue[0][0]; + private static final TypeAnnotationValue[] NO_TYPE_ANNOTATIONS = new TypeAnnotationValue[0]; + + private static final Method classGetRawAnnotations = ReflectionUtil.lookupMethod(Class.class, "getRawAnnotations"); + private static final Method classGetRawTypeAnnotations = ReflectionUtil.lookupMethod(Class.class, "getRawTypeAnnotations"); + private static final Method classGetConstantPool = ReflectionUtil.lookupMethod(Class.class, "getConstantPool"); + private static final Field fieldAnnotations = ReflectionUtil.lookupField(Field.class, "annotations"); + private static final Method fieldGetTypeAnnotationBytes = ReflectionUtil.lookupMethod(Field.class, "getTypeAnnotationBytes0"); + private static final Method executableGetAnnotationBytes = ReflectionUtil.lookupMethod(Executable.class, "getAnnotationBytes"); + private static final Method executableGetTypeAnnotationBytes = ReflectionUtil.lookupMethod(Executable.class, "getTypeAnnotationBytes"); + private static final Field methodParameterAnnotations = ReflectionUtil.lookupField(Method.class, "parameterAnnotations"); + private static final Field methodAnnotationDefault = ReflectionUtil.lookupField(Method.class, "annotationDefault"); + private static final Field constructorParameterAnnotations = ReflectionUtil.lookupField(Constructor.class, "parameterAnnotations"); + private static final Class recordComponentClass; + private static final Field recordComponentAnnotations; + private static final Field recordComponentTypeAnnotations; + private static final Method recordComponentGetDeclaringRecord; + private static final Method packageGetPackageInfo = ReflectionUtil.lookupMethod(Package.class, "getPackageInfo"); + private static final Object hotSpotJVMCIRuntimeReflection; + private static final Method hotSpotJDKReflectionGetMirror; + private static final Method hotSpotJDKReflectionGetMethod; + private static final Method hotSpotJDKReflectionGetField; + + static { + try { + recordComponentClass = ReflectionUtil.lookupClass(true, "java.lang.reflect.RecordComponent"); + recordComponentAnnotations = recordComponentClass != null ? ReflectionUtil.lookupField(recordComponentClass, "annotations") : null; + recordComponentTypeAnnotations = recordComponentClass != null ? ReflectionUtil.lookupField(recordComponentClass, "typeAnnotations") : null; + recordComponentGetDeclaringRecord = recordComponentClass != null ? ReflectionUtil.lookupMethod(recordComponentClass, "getDeclaringRecord") : null; + Object hotSpotJVMCIRuntime = ReflectionUtil.lookupMethod(HotSpotJVMCIRuntime.class, "runtime").invoke(null); + hotSpotJVMCIRuntimeReflection = ReflectionUtil.lookupMethod(HotSpotJVMCIRuntime.class, "getReflection").invoke(hotSpotJVMCIRuntime); + hotSpotJDKReflectionGetMirror = ReflectionUtil.lookupMethod(Class.forName("jdk.vm.ci.hotspot.HotSpotJDKReflection"), "getMirror", HotSpotResolvedJavaType.class); + hotSpotJDKReflectionGetMethod = ReflectionUtil.lookupMethod(Class.forName("jdk.vm.ci.hotspot.HotSpotJDKReflection"), "getMethod", + Class.forName("jdk.vm.ci.hotspot.HotSpotResolvedJavaMethodImpl")); + hotSpotJDKReflectionGetField = ReflectionUtil.lookupMethod(Class.forName("jdk.vm.ci.hotspot.HotSpotJDKReflection"), "getField", + Class.forName("jdk.vm.ci.hotspot.HotSpotResolvedJavaFieldImpl")); + } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(e); + } + } + + @SuppressWarnings("unchecked") + @Override + public T getAnnotation(AnnotatedElement element, Class annotationType, boolean declaredOnly) { + for (AnnotationValue annotation : getAnnotationData(element, declaredOnly)) { + if (annotation.type != null && annotation.type.equals(annotationType)) { + return (T) annotation.get(annotationType); + } + } + return null; + } + + @Override + public boolean isAnnotationPresent(AnnotatedElement element, Class annotationType) { + for (AnnotationValue annotation : getAnnotationData(element, false)) { + if (annotation.type != null && annotation.type.equals(annotationType)) { + return true; + } + } + return false; + } + + public AnnotationValue[] getDeclaredAnnotationData(AnnotatedElement element) { + return getAnnotationData(element, true); + } + + private AnnotationValue[] getAnnotationData(AnnotatedElement element, boolean declaredOnly) { + AnnotatedElement root = getRoot(element); + AnnotatedElement secondaryRoot = getSecondaryRoot(element); + List injectedAnnotations = getInjectedAnnotations(element); + List> ignoredAnnotations = getIgnoredAnnotations(element); + + List data = new ArrayList<>(); + for (Annotation annotation : injectedAnnotations) { + data.add(new AnnotationValue(annotation)); + } + if (root != null) { + data.addAll(Arrays.asList(declaredOnly ? getDeclaredAnnotationDataFromRoot(root) : getAnnotationDataFromRoot(root))); + } + if (secondaryRoot != null) { + data.addAll(Arrays.asList(declaredOnly ? getDeclaredAnnotationDataFromRoot(secondaryRoot) : getAnnotationDataFromRoot(secondaryRoot))); + } + if (ignoredAnnotations.size() > 0) { + data.removeIf(annotation -> ignoredAnnotations.contains(annotation.type)); + } + return data.toArray(NO_ANNOTATIONS); + } + + private AnnotationValue[] getAnnotationDataFromRoot(AnnotatedElement rootElement) { + if (!(rootElement instanceof Class)) { + return getDeclaredAnnotationDataFromRoot(rootElement); + } + + Class clazz = (Class) rootElement; + List inheritedAnnotations = new ArrayList<>(); + if (!annotationCache.containsKey(clazz)) { + Class superClass = clazz.getSuperclass(); + if (superClass != null) { + for (AnnotationValue superclassAnnotation : getAnnotationDataFromRoot(superClass)) { + if (isAnnotationPresent(superclassAnnotation.type, Inherited.class)) { + inheritedAnnotations.add(superclassAnnotation); + } + } + } + } + + return annotationCache.computeIfAbsent(clazz, element -> { + AnnotationValue[] declaredAnnotations = getDeclaredAnnotationDataFromRoot(element); + Map, AnnotationValue> annotations = new LinkedHashMap<>(); + for (AnnotationValue declaredAnnotation : declaredAnnotations) { + annotations.put(declaredAnnotation.type, declaredAnnotation); + } + boolean modified = false; + for (AnnotationValue inheritedAnnotation : inheritedAnnotations) { + if (!annotations.containsKey(inheritedAnnotation.type)) { + annotations.put(inheritedAnnotation.type, inheritedAnnotation); + modified = true; + } + } + return modified ? annotations.values().toArray(NO_ANNOTATIONS) : declaredAnnotations; + }); + } + + private AnnotationValue[] getDeclaredAnnotationDataFromRoot(AnnotatedElement rootElement) { + return declaredAnnotationCache.computeIfAbsent(rootElement, element -> { + byte[] rawAnnotations = getRawAnnotations(element); + if (rawAnnotations == null) { + return NO_ANNOTATIONS; + } + ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); + List annotations = new ArrayList<>(); + int numAnnotations = buf.getShort() & 0xFFFF; + for (int i = 0; i < numAnnotations; i++) { + AnnotationValue annotation = AnnotationValue.extract(buf, getConstantPool(element), getContainer(element), false, false); + if (annotation != null) { + annotations.add(annotation); + } + } + return annotations.toArray(NO_ANNOTATIONS); + }); + } + + public AnnotationValue[][] getParameterAnnotationData(AnnotatedElement element) { + return getParameterAnnotationDataFromRoot((Executable) getRoot(element)); + } + + private AnnotationValue[][] getParameterAnnotationDataFromRoot(Executable rootElement) { + return parameterAnnotationCache.computeIfAbsent(rootElement, element -> { + byte[] rawParameterAnnotations = getRawParameterAnnotations(element); + if (rawParameterAnnotations == null) { + return NO_PARAMETER_ANNOTATIONS; + } + ByteBuffer buf = ByteBuffer.wrap(rawParameterAnnotations); + int numParameters = buf.get() & 0xFF; + AnnotationValue[][] parameterAnnotations = new AnnotationValue[numParameters][]; + for (int i = 0; i < numParameters; i++) { + List parameterAnnotationList = new ArrayList<>(); + int numAnnotations = buf.getShort() & 0xFFFF; + for (int j = 0; j < numAnnotations; j++) { + AnnotationValue parameterAnnotation = AnnotationValue.extract(buf, getConstantPool(element), getContainer(element), false, false); + if (parameterAnnotation != null) { + parameterAnnotationList.add(parameterAnnotation); + } + } + parameterAnnotations[i] = parameterAnnotationList.toArray(NO_ANNOTATIONS); + } + return parameterAnnotations; + }); + } + + public TypeAnnotationValue[] getTypeAnnotationData(AnnotatedElement element) { + return getTypeAnnotationDataFromRoot(getRoot(element)); + } + + private TypeAnnotationValue[] getTypeAnnotationDataFromRoot(AnnotatedElement rootElement) { + return typeAnnotationCache.computeIfAbsent(rootElement, element -> { + byte[] rawTypeAnnotations = getRawTypeAnnotations(element); + if (rawTypeAnnotations == null) { + return NO_TYPE_ANNOTATIONS; + } + ByteBuffer buf = ByteBuffer.wrap(rawTypeAnnotations); + int annotationCount = buf.getShort() & 0xFFFF; + TypeAnnotationValue[] typeAnnotationValues = new TypeAnnotationValue[annotationCount]; + for (int i = 0; i < annotationCount; i++) { + typeAnnotationValues[i] = TypeAnnotationValue.extract(buf, getConstantPool(element), getContainer(element)); + } + return typeAnnotationValues; + }); + } + + public AnnotationMemberValue getAnnotationDefaultData(AnnotatedElement element) { + return getAnnotationDefaultDataFromRoot((Method) getRoot(element)); + } + + private AnnotationMemberValue getAnnotationDefaultDataFromRoot(Method accessorMethod) { + return annotationDefaultCache.computeIfAbsent(accessorMethod, method -> { + byte[] rawAnnotationDefault = getRawAnnotationDefault(method); + if (rawAnnotationDefault == null) { + return null; + } + ByteBuffer buf = ByteBuffer.wrap(rawAnnotationDefault); + return AnnotationMemberValue.extract(buf, getConstantPool(method), getContainer(method), false); + }); + } + + private static byte[] getRawAnnotations(AnnotatedElement rootElement) { + try { + if (rootElement instanceof Class) { + return (byte[]) classGetRawAnnotations.invoke(rootElement); + } else if (rootElement instanceof Field) { + return (byte[]) fieldAnnotations.get(rootElement); + } else if (rootElement instanceof Executable) { + return (byte[]) executableGetAnnotationBytes.invoke(rootElement); + } else if (recordComponentClass != null && recordComponentClass.isInstance(rootElement)) { + return (byte[]) recordComponentAnnotations.get(rootElement); + } else { + throw new AnnotationExtractionError("Unexpected annotated element type: " + rootElement.getClass()); + } + } catch (InvocationTargetException | IllegalAccessException e) { + throw new AnnotationExtractionError(e); + } + } + + private static byte[] getRawParameterAnnotations(Executable rootElement) { + try { + if (rootElement instanceof Method) { + return (byte[]) methodParameterAnnotations.get(rootElement); + } else if (rootElement instanceof Constructor) { + return (byte[]) constructorParameterAnnotations.get(rootElement); + } else { + throw new AnnotationExtractionError("Unexpected annotated element type: " + rootElement.getClass()); + } + } catch (IllegalAccessException e) { + throw new AnnotationExtractionError(e); + } + } + + private static byte[] getRawTypeAnnotations(AnnotatedElement rootElement) { + try { + if (rootElement instanceof Class) { + return (byte[]) classGetRawTypeAnnotations.invoke(rootElement); + } else if (rootElement instanceof Field) { + return (byte[]) fieldGetTypeAnnotationBytes.invoke(rootElement); + } else if (rootElement instanceof Executable) { + return (byte[]) executableGetTypeAnnotationBytes.invoke(rootElement); + } else if (recordComponentClass != null && recordComponentClass.isInstance(rootElement)) { + return (byte[]) recordComponentTypeAnnotations.get(rootElement); + } else { + throw new AnnotationExtractionError("Unexpected annotated element type: " + rootElement.getClass()); + } + } catch (InvocationTargetException | IllegalAccessException e) { + throw new AnnotationExtractionError(e); + } + } + + private static byte[] getRawAnnotationDefault(Method method) { + try { + return (byte[]) methodAnnotationDefault.get(method); + } catch (IllegalAccessException e) { + throw new AnnotationExtractionError(e); + } + } + + private static ConstantPool getConstantPool(AnnotatedElement rootElement) { + Class container = getContainer(rootElement); + try { + return (ConstantPool) classGetConstantPool.invoke(container); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new AnnotationExtractionError(e); + } + } + + private static Class getContainer(AnnotatedElement rootElement) { + if (rootElement instanceof Class) { + return (Class) rootElement; + } else if (rootElement instanceof Field) { + return ((Field) rootElement).getDeclaringClass(); + } else if (rootElement instanceof Executable) { + return ((Executable) rootElement).getDeclaringClass(); + } else if (recordComponentClass != null && recordComponentClass.isInstance(rootElement)) { + try { + return (Class) recordComponentGetDeclaringRecord.invoke(rootElement); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new AnnotationExtractionError(e); + } + } else { + throw new AnnotationExtractionError("Unexpected annotated element type: " + rootElement.getClass()); + } + } + + private static AnnotatedElement getRoot(AnnotatedElement element) { + try { + if (element instanceof Package) { + return (Class) packageGetPackageInfo.invoke(element); + } else if (element instanceof HotSpotResolvedObjectType || element instanceof HotSpotResolvedJavaType) { + return (AnnotatedElement) hotSpotJDKReflectionGetMirror.invoke(hotSpotJVMCIRuntimeReflection, element); + } else if (element instanceof HotSpotResolvedJavaMethod) { + if (((ResolvedJavaMethod) element).isClassInitializer()) { + return null; + } + return (AnnotatedElement) hotSpotJDKReflectionGetMethod.invoke(hotSpotJVMCIRuntimeReflection, element); + } else if (element instanceof HotSpotResolvedJavaField) { + return (AnnotatedElement) hotSpotJDKReflectionGetField.invoke(hotSpotJVMCIRuntimeReflection, element); + } else if (element instanceof AnnotationWrapper) { + return getRoot(((AnnotationWrapper) element).getAnnotationRoot()); + } + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + if (targetException instanceof LinkageError) { + throw (LinkageError) targetException; + } else if (targetException instanceof IllegalArgumentException) { + return null; + } + throw new AnnotationExtractionError(e); + } catch (IllegalAccessException e) { + throw new AnnotationExtractionError(e); + } + return element; + } + + private static AnnotatedElement getSecondaryRoot(AnnotatedElement element) { + if (element instanceof AnnotationWrapper) { + return getRoot(((AnnotationWrapper) element).getSecondaryAnnotationRoot()); + } + return null; + } + + private static List getInjectedAnnotations(AnnotatedElement element) { + List injectedAnnotations = new ArrayList<>(); + if (element instanceof AnnotationWrapper) { + AnnotationWrapper wrapper = (AnnotationWrapper) element; + Annotation[] wrapperInjectedAnnotations = wrapper.getInjectedAnnotations(); + if (wrapperInjectedAnnotations != null) { + injectedAnnotations.addAll(Arrays.asList(wrapperInjectedAnnotations)); + } + AnnotatedElement root = wrapper.getAnnotationRoot(); + if (root != null) { + injectedAnnotations.addAll(getInjectedAnnotations(root)); + } + AnnotatedElement secondaryRoot = wrapper.getSecondaryAnnotationRoot(); + if (secondaryRoot != null) { + injectedAnnotations.addAll(getInjectedAnnotations(secondaryRoot)); + } + } + return injectedAnnotations; + } + + private static List> getIgnoredAnnotations(AnnotatedElement element) { + List> ignoredAnnotations = new ArrayList<>(); + if (element instanceof AnnotationWrapper) { + AnnotationWrapper wrapper = (AnnotationWrapper) element; + Class[] wrapperIgnoredAnnotations = wrapper.getIgnoredAnnotations(); + if (wrapperIgnoredAnnotations != null) { + ignoredAnnotations.addAll(Arrays.asList(wrapperIgnoredAnnotations)); + } + AnnotatedElement root = wrapper.getAnnotationRoot(); + if (root != null) { + ignoredAnnotations.addAll(getIgnoredAnnotations(root)); + } + AnnotatedElement secondaryRoot = wrapper.getSecondaryAnnotationRoot(); + if (secondaryRoot != null) { + ignoredAnnotations.addAll(getIgnoredAnnotations(secondaryRoot)); + } + } + return ignoredAnnotations; + } + +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/TypeAnnotationValue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/TypeAnnotationValue.java new file mode 100644 index 0000000000000..8beb887c3d37c --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/TypeAnnotationValue.java @@ -0,0 +1,177 @@ +/* + * 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.hosted.annotation; + +import java.lang.annotation.AnnotationFormatError; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Objects; + +import jdk.internal.reflect.ConstantPool; + +public final class TypeAnnotationValue { + private final byte[] targetInfo; + private final byte[] locationInfo; + private final AnnotationValue annotation; + + // Values copied from TypeAnnotationParser + // Regular type parameter annotations + private static final byte CLASS_TYPE_PARAMETER = 0x00; + private static final byte METHOD_TYPE_PARAMETER = 0x01; + // Type Annotations outside method bodies + 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; + // Type Annotations inside method bodies + private static final byte LOCAL_VARIABLE = (byte) 0x40; + private static final byte RESOURCE_VARIABLE = (byte) 0x41; + private static final byte EXCEPTION_PARAMETER = (byte) 0x42; + private static final byte INSTANCEOF = (byte) 0x43; + private static final byte NEW = (byte) 0x44; + private static final byte CONSTRUCTOR_REFERENCE = (byte) 0x45; + private static final byte METHOD_REFERENCE = (byte) 0x46; + private static final byte CAST = (byte) 0x47; + private static final byte CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = (byte) 0x48; + private static final byte METHOD_INVOCATION_TYPE_ARGUMENT = (byte) 0x49; + private static final byte CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = (byte) 0x4A; + private static final byte METHOD_REFERENCE_TYPE_ARGUMENT = (byte) 0x4B; + + private static byte[] extractTargetInfo(ByteBuffer buf) { + int startPos = buf.position(); + int posCode = buf.get() & 0xFF; + switch (posCode) { + case CLASS_TYPE_PARAMETER: + case METHOD_TYPE_PARAMETER: + case METHOD_FORMAL_PARAMETER: + case EXCEPTION_PARAMETER: + buf.get(); + break; + case CLASS_EXTENDS: + case THROWS: + case INSTANCEOF: + case NEW: + case CONSTRUCTOR_REFERENCE: + case METHOD_REFERENCE: + buf.getShort(); + break; + case CLASS_TYPE_PARAMETER_BOUND: + case METHOD_TYPE_PARAMETER_BOUND: + buf.get(); + buf.get(); + break; + case FIELD: + case METHOD_RETURN: + case METHOD_RECEIVER: + break; + case LOCAL_VARIABLE: + case RESOURCE_VARIABLE: + short length = buf.getShort(); + for (int i = 0; i < length; ++i) { + buf.getShort(); + buf.getShort(); + buf.getShort(); + } + break; + case CAST: + case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case METHOD_INVOCATION_TYPE_ARGUMENT: + case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case METHOD_REFERENCE_TYPE_ARGUMENT: + buf.getShort(); + buf.get(); + break; + default: + throw new AnnotationFormatError("Could not parse bytes for type annotations"); + } + int endPos = buf.position(); + byte[] targetInfo = new byte[endPos - startPos]; + buf.position(startPos).get(targetInfo).position(endPos); + return targetInfo; + } + + private static byte[] extractLocationInfo(ByteBuffer buf) { + int startPos = buf.position(); + int depth = buf.get() & 0xFF; + for (int i = 0; i < depth; i++) { + buf.get(); + buf.get(); + } + int endPos = buf.position(); + byte[] locationInfo = new byte[endPos - startPos]; + buf.position(startPos).get(locationInfo).position(endPos); + return locationInfo; + } + + static TypeAnnotationValue extract(ByteBuffer buf, ConstantPool cp, Class container) { + byte[] targetInfo = extractTargetInfo(buf); + byte[] locationInfo = extractLocationInfo(buf); + AnnotationValue annotation = AnnotationValue.extract(buf, cp, container, false, false); + + return new TypeAnnotationValue(targetInfo, locationInfo, annotation); + } + + private TypeAnnotationValue(byte[] targetInfo, byte[] locationInfo, AnnotationValue annotation) { + this.targetInfo = targetInfo; + this.locationInfo = locationInfo; + this.annotation = annotation; + } + + public byte[] getTargetInfo() { + return targetInfo; + } + + public byte[] getLocationInfo() { + return locationInfo; + } + + public AnnotationValue getAnnotationData() { + return annotation; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TypeAnnotationValue that = (TypeAnnotationValue) o; + return Arrays.equals(targetInfo, that.targetInfo) && Arrays.equals(locationInfo, that.locationInfo) && Objects.equals(annotation, that.annotation); + } + + @Override + public int hashCode() { + int result = Objects.hash(annotation); + result = 31 * result + Arrays.hashCode(targetInfo); + result = 31 * result + Arrays.hashCode(locationInfo); + return result; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperSubstitutionProcessor.java index b39351e464fc4..5672592c24ef7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperSubstitutionProcessor.java @@ -29,7 +29,7 @@ import org.graalvm.nativeimage.c.constant.CEnumLookup; import org.graalvm.nativeimage.c.constant.CEnumValue; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.svm.hosted.c.NativeLibraries; 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 93187ceb71714..8e0d541afb785 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 @@ -27,7 +27,6 @@ import static com.oracle.svm.core.SubstrateOptions.TraceClassInitialization; import static com.oracle.svm.core.SubstrateOptions.TraceObjectInstantiation; -import java.lang.reflect.Proxy; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -567,9 +566,6 @@ private static void checkEagerInitialization(Class clazz) { throw UserError.abort("Primitive types and array classes are initialized at build time because initialization is side-effect free. " + "It is not possible (and also not useful) to register them for run time initialization. Culprit: %s", clazz.getTypeName()); } - if (clazz.isAnnotation()) { - throw UserError.abort("Class initialization of annotation classes cannot be delayed to runtime. Culprit: %s", clazz.getTypeName()); - } } /** @@ -585,20 +581,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; @@ -609,11 +591,6 @@ InitKind computeInitKindAndMaybeInitializeClass(Class clazz, boolean memoize, return InitKind.BUILD_TIME; } - if (Proxy.isProxyClass(clazz) && isProxyFromAnnotation(clazz)) { - forceInitializeHosted(clazz, "proxy classes are initialized at build time", false); - return InitKind.BUILD_TIME; - } - if (clazz.getTypeName().contains("$$StringConcat")) { forceInitializeHosted(clazz, "string concatenation classes are initialized at build time", false); return InitKind.BUILD_TIME; @@ -700,15 +677,6 @@ private InitKind processInterfaces(Class clazz, boolean memoizeEager, Set clazz) { - for (Class interfaces : clazz.getInterfaces()) { - if (interfaces.isAnnotation()) { - return true; - } - } - return false; - } - void addProvenEarly(Class clazz) { provenSafeEarly.add(clazz); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointJavaCallStubMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointJavaCallStubMethod.java index 7f40c268204a3..0d3bd8fe704f5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointJavaCallStubMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointJavaCallStubMethod.java @@ -24,7 +24,7 @@ */ package com.oracle.svm.hosted.code; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.util.List; import org.graalvm.compiler.nodes.ConstantNode; @@ -87,17 +87,7 @@ protected ValueNode createTargetAddressNode(HostedGraphKit kit, HostedProviders } @Override - public Annotation[] getAnnotations() { - return getDeclaredAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return new Annotation[0]; - } - - @Override - public T getAnnotation(Class annotationClass) { + public AnnotatedElement getAnnotationRoot() { return null; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/EntryPointCallStubMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/EntryPointCallStubMethod.java index dc5f35bcea4fb..4721baa07310c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/EntryPointCallStubMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/EntryPointCallStubMethod.java @@ -55,20 +55,7 @@ private static void uninterruptibleAnnotationHolder() { ReflectionUtil.lookupMethod(EntryPointCallStubMethod.class, "uninterruptibleAnnotationHolder").getAnnotation(Uninterruptible.class)); @Override - public final T getAnnotation(Class annotationClass) { - if (annotationClass == Uninterruptible.class) { - return annotationClass.cast(UNINTERRUPTIBLE_ANNOTATION); - } - return null; - } - - @Override - public final Annotation[] getAnnotations() { - return new Annotation[]{UNINTERRUPTIBLE_ANNOTATION}; - } - - @Override - public final Annotation[] getDeclaredAnnotations() { + public Annotation[] getInjectedAnnotations() { return new Annotation[]{UNINTERRUPTIBLE_ANNOTATION}; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java index 856482964c706..099be0654dbf3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java @@ -79,20 +79,7 @@ private static void annotationHolder() { ReflectionUtil.lookupMethod(FactoryMethod.class, "annotationHolder").getAnnotation(NeverInlineTrivial.class)); @Override - public T getAnnotation(Class annotationClass) { - if (annotationClass.isInstance(INLINE_ANNOTATION)) { - return annotationClass.cast(INLINE_ANNOTATION); - } - return null; - } - - @Override - public Annotation[] getAnnotations() { - return new Annotation[]{INLINE_ANNOTATION}; - } - - @Override - public Annotation[] getDeclaredAnnotations() { + public Annotation[] getInjectedAnnotations() { return new Annotation[]{INLINE_ANNOTATION}; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/NonBytecodeStaticMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/NonBytecodeStaticMethod.java index 0559851bf26f0..2bd8496b1016d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/NonBytecodeStaticMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/NonBytecodeStaticMethod.java @@ -25,11 +25,13 @@ package com.oracle.svm.hosted.code; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import com.oracle.graal.pointsto.infrastructure.GraphProvider; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantPool; @@ -46,8 +48,7 @@ * Abstract base class for methods with generated Graal IR, i.e., methods that do not originate from * bytecode. */ -public abstract class NonBytecodeStaticMethod implements GraphProvider, ResolvedJavaMethod { - private static final Annotation[] NO_ANNOTATIONS = new Annotation[0]; +public abstract class NonBytecodeStaticMethod implements GraphProvider, ResolvedJavaMethod, AnnotationWrapper { /** * Line numbers are bogus because this is generated code, but we need to include them in our @@ -229,26 +230,10 @@ public SpeculationLog getSpeculationLog() { } @Override - @SuppressWarnings("unchecked") - public T getAnnotation(Class annotationClass) { - for (Annotation annotation : getAnnotations()) { - if (annotationClass.isInstance(annotation)) { - return (T) annotation; - } - } + public AnnotatedElement getAnnotationRoot() { return null; } - @Override - public Annotation[] getAnnotations() { - return NO_ANNOTATIONS; - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return NO_ANNOTATIONS; - } - @Override public int getModifiers() { return Modifier.PUBLIC | Modifier.STATIC; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodFactorySubstitutionMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodFactorySubstitutionMethod.java index 13190c4638689..a75e278dac158 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodFactorySubstitutionMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodFactorySubstitutionMethod.java @@ -45,6 +45,7 @@ import org.graalvm.compiler.nodes.UnwindNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.ExceptionObjectNode; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.meta.HostedProviders; @@ -71,7 +72,7 @@ final class PodFactorySubstitutionProcessor extends SubstitutionProcessor { @Override public ResolvedJavaMethod lookup(ResolvedJavaMethod method) { - if (method.isSynthetic() && method.getDeclaringClass().isAnnotationPresent(PodFactory.class) && !method.isConstructor()) { + if (method.isSynthetic() && GuardedAnnotationAccess.isAnnotationPresent(method.getDeclaringClass(), PodFactory.class) && !method.isConstructor()) { assert !(method instanceof CustomSubstitutionMethod); return substitutions.computeIfAbsent(method, PodFactorySubstitutionMethod::new); } 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 d4602c7751289..33a412c2d3a75 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 @@ -57,6 +57,7 @@ import org.graalvm.word.WordFactory; import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.infrastructure.WrappedElement; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.objectfile.ObjectFile; @@ -273,13 +274,13 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code if (object instanceof Field) { HostedField hostedField = hMetaAccess.lookupJavaField((Field) object); if (!includedFields.contains(hostedField)) { - reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, object, configurationFields.contains(object)); + reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, hostedField, object, configurationFields.contains(object)); includedFields.add(hostedField); } } else if (object instanceof Executable) { HostedMethod hostedMethod = hMetaAccess.lookupJavaMethod((Executable) object); if (!includedMethods.contains(hostedMethod)) { - reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, object, configurationExecutables.contains(object)); + reflectionMetadataEncoder.addHeapAccessibleObjectMetadata(hMetaAccess, hostedMethod, object, configurationExecutables.contains(object)); includedMethods.add(hostedMethod); } } @@ -654,7 +655,7 @@ public interface ReflectionMetadataEncoder { void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, HostedMethod sharedMethod, Executable reflectMethod, Object accessor); - void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object, boolean registered); + void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, WrappedElement hostedElement, AccessibleObject object, boolean registered); void addHidingFieldMetadata(AnalysisField analysisField, HostedType declType, String name, HostedType type, int modifiers); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java index dfccce7b4c34a..535c969a5d175 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java @@ -25,10 +25,12 @@ package com.oracle.svm.hosted.lambda; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.core.jdk.LambdaFormHiddenMethod; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.Assumptions.AssumptionResult; import jdk.vm.ci.meta.JavaConstant; @@ -43,7 +45,7 @@ /** * Simply changes the name of Lambdas from a random ID into a stable name. */ -public class LambdaSubstitutionType implements ResolvedJavaType, OriginalClassProvider { +public class LambdaSubstitutionType implements ResolvedJavaType, OriginalClassProvider, AnnotationWrapper { private final ResolvedJavaType original; private final String stableName; @@ -59,21 +61,13 @@ public String getName() { } @Override - public Annotation[] getAnnotations() { - return LambdaFormHiddenMethod.Holder.ARRAY; - } - - @Override - public boolean isAnnotationPresent(Class annotationClass) { - return annotationClass == LambdaFormHiddenMethod.class; + public AnnotatedElement getAnnotationRoot() { + return null; } @Override - public T getAnnotation(Class annotationClass) { - if (annotationClass == LambdaFormHiddenMethod.class) { - return annotationClass.cast(LambdaFormHiddenMethod.Holder.INSTANCE); - } - return null; + public Annotation[] getInjectedAnnotations() { + return LambdaFormHiddenMethod.Holder.ARRAY; } @Override @@ -397,24 +391,11 @@ public T[] getAnnotationsByType(Class annotationClass) return original.getAnnotationsByType(annotationClass); } - @Override - public T getDeclaredAnnotation(Class annotationClass) { - if (annotationClass == LambdaFormHiddenMethod.class) { - return annotationClass.cast(LambdaFormHiddenMethod.Holder.INSTANCE); - } - return null; - } - @Override public T[] getDeclaredAnnotationsByType(Class annotationClass) { return original.getDeclaredAnnotationsByType(annotationClass); } - @Override - public Annotation[] getDeclaredAnnotations() { - return LambdaFormHiddenMethod.Holder.ARRAY; - } - public ResolvedJavaType getOriginal() { return original; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java index a8174004187c6..702dc883a152f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java @@ -24,13 +24,15 @@ */ package com.oracle.svm.hosted.meta; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; +import com.oracle.graal.pointsto.infrastructure.WrappedJavaField; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.meta.SubstrateObjectConstant; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -39,7 +41,7 @@ /** * Store the compile-time information for a field in the Substrate VM, such as the field offset. */ -public class HostedField implements OriginalFieldProvider, SharedField, Comparable { +public class HostedField implements OriginalFieldProvider, SharedField, Comparable, WrappedJavaField, AnnotationWrapper { private final HostedUniverse universe; private final HostedMetaAccess metaAccess; @@ -64,6 +66,11 @@ public HostedField(HostedUniverse universe, HostedMetaAccess metaAccess, Analysi this.location = LOC_UNINITIALIZED; } + @Override + public AnalysisField getWrapped() { + return wrapped; + } + public JavaTypeProfile getFieldTypeProfile() { return typeProfile; } @@ -177,18 +184,8 @@ public boolean isSynthetic() { } @Override - public Annotation[] getAnnotations() { - return wrapped.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return wrapped.getDeclaredAnnotations(); - } - - @Override - public T getAnnotation(Class annotationClass) { - return wrapped.getAnnotation(annotationClass); + public AnnotatedElement getAnnotationRoot() { + return wrapped; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index 0b4b54d674b4f..18eef09a9d6f5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -366,21 +366,6 @@ public ConstantPool getConstantPool() { return constantPool; } - @Override - public Annotation[] getAnnotations() { - return wrapped.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return wrapped.getDeclaredAnnotations(); - } - - @Override - public T getAnnotation(Class annotationClass) { - return wrapped.getAnnotation(annotationClass); - } - @Override public Annotation[][] getParameterAnnotations() { return wrapped.getParameterAnnotations(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java index 3c079e52d2fd9..77cc90f9bcb93 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java @@ -26,8 +26,6 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; -import java.lang.annotation.Annotation; - import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; @@ -349,21 +347,6 @@ public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expec return null; } - @Override - public Annotation[] getAnnotations() { - return wrapped.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return wrapped.getDeclaredAnnotations(); - } - - @Override - public final T getAnnotation(Class annotationClass) { - return wrapped.getAnnotation(annotationClass); - } - @Override public String getSourceFileName() { return wrapped.getSourceFileName(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/ImplicitAssertionsPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/ImplicitAssertionsPhase.java index c8944c5c8bb4d..27d8ad73d4076 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/ImplicitAssertionsPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/ImplicitAssertionsPhase.java @@ -48,7 +48,7 @@ import org.graalvm.compiler.nodes.java.NewInstanceNode; import org.graalvm.compiler.nodes.spi.CoreProviders; import org.graalvm.compiler.phases.BasePhase; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.core.code.FactoryMethodMarker; import com.oracle.svm.core.snippets.ImplicitExceptions; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyImpl.java index f31116e598ae6..791646b328aa7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyImpl.java @@ -50,7 +50,7 @@ import org.graalvm.compiler.nodes.virtual.VirtualArrayNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.compiler.options.Option; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index ef07efb4aec2a..7e1178ca7fb4e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -90,7 +90,7 @@ import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.hosted.RuntimeReflection; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; import org.graalvm.word.LocationIdentity; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedField.java index 37a1af797f1bb..f8d4cb59fa2ba 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedField.java @@ -25,6 +25,7 @@ package com.oracle.svm.hosted.substitute; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.util.Arrays; @@ -33,6 +34,7 @@ import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.InjectAccessors; import com.oracle.svm.core.meta.ReadableJavaField; +import com.oracle.svm.util.AnnotationWrapper; import com.oracle.svm.util.ClassUtil; import jdk.vm.ci.meta.JavaConstant; @@ -41,7 +43,7 @@ import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaType; -public class AnnotatedField implements ReadableJavaField, OriginalFieldProvider { +public class AnnotatedField implements ReadableJavaField, OriginalFieldProvider, AnnotationWrapper { static Annotation[] appendAnnotationTo(Annotation[] array, Annotation element) { Annotation[] result = Arrays.copyOf(array, array.length + 1); @@ -59,21 +61,13 @@ public AnnotatedField(ResolvedJavaField original, Annotation injectedAnnotation) } @Override - public Annotation[] getAnnotations() { - return appendAnnotationTo(original.getAnnotations(), injectedAnnotation); + public AnnotatedElement getAnnotationRoot() { + return original; } @Override - public Annotation[] getDeclaredAnnotations() { - return appendAnnotationTo(original.getDeclaredAnnotations(), injectedAnnotation); - } - - @Override - public T getAnnotation(Class annotationClass) { - if (annotationClass.isInstance(injectedAnnotation)) { - return annotationClass.cast(injectedAnnotation); - } - return original.getAnnotation(annotationClass); + public Annotation[] getInjectedAnnotations() { + return new Annotation[]{injectedAnnotation}; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedMethod.java index 450b0934a4d0e..8752599b25749 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedMethod.java @@ -25,12 +25,10 @@ package com.oracle.svm.hosted.substitute; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Executable; import java.lang.reflect.Type; import java.util.Arrays; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.nodes.StructuredGraph; @@ -41,6 +39,7 @@ import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.core.annotate.AnnotateOriginal; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantPool; @@ -53,7 +52,7 @@ import jdk.vm.ci.meta.Signature; import jdk.vm.ci.meta.SpeculationLog; -public class AnnotatedMethod implements ResolvedJavaMethod, GraphProvider, OriginalMethodProvider { +public class AnnotatedMethod implements ResolvedJavaMethod, GraphProvider, OriginalMethodProvider, AnnotationWrapper { private final ResolvedJavaMethod original; private final ResolvedJavaMethod annotated; @@ -177,40 +176,20 @@ public ConstantPool getConstantPool() { return original.getConstantPool(); } - private Annotation[] getAnnotationsImpl(Function src) { - // Collect all but @AnnotateOriginal from annotated - Map result = Arrays.stream(src.apply(annotated))// - .filter(annotation -> !annotation.getClass().equals(AnnotateOriginal.class))// - .collect(Collectors.toMap(annotation -> annotation.getClass(), Function.identity())); - // Add remaining missing ones from original - for (Annotation annotation : src.apply(original)) { - result.putIfAbsent(annotation.getClass(), annotation); - } - return result.values().toArray(new Annotation[result.size()]); - } - @Override - public Annotation[] getAnnotations() { - return getAnnotationsImpl(ResolvedJavaMethod::getAnnotations); + public AnnotatedElement getAnnotationRoot() { + return annotated; } @Override - public Annotation[] getDeclaredAnnotations() { - return getAnnotationsImpl(ResolvedJavaMethod::getDeclaredAnnotations); + public AnnotatedElement getSecondaryAnnotationRoot() { + return original; } @Override - public T getAnnotation(Class annotationClass) { - if (annotationClass.equals(AnnotateOriginal.class)) { - return null; - } - // First look for the Annotation in annotated - T result = annotated.getAnnotation(annotationClass); - if (result != null) { - return result; - } - // Consider original if not found in annotated - return original.getAnnotation(annotationClass); + @SuppressWarnings({"unchecked", "rawtypes"}) + public Class[] getIgnoredAnnotations() { + return new Class[]{AnnotateOriginal.class}; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index 046205799af2e..8a3aae68a486a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -47,7 +47,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; @@ -1064,7 +1064,7 @@ private Class[] interceptParameterTypes(Class[] types) { protected T lookupAnnotation(AnnotatedElement element, Class annotationClass) { assert element instanceof Class || element instanceof Executable || element instanceof Field : element.getClass(); - return element.getAnnotation(annotationClass); + return GuardedAnnotationAccess.getAnnotation(element, annotationClass); } protected static String deleteErrorMessage(AnnotatedElement element, Delete deleteAnnotation, boolean hosted) { 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 340f52cefc566..11832cb737a5d 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 @@ -30,7 +30,7 @@ import static com.oracle.svm.core.util.VMError.guarantee; import static com.oracle.svm.core.util.VMError.shouldNotReachHere; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -63,6 +63,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedMetaAccess; +import com.oracle.svm.util.AnnotationWrapper; import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; @@ -81,7 +82,7 @@ * @see RecomputeFieldValue * @see NativeImageReinitialize */ -public class ComputedValueField implements ReadableJavaField, OriginalFieldProvider, ComputedValue { +public class ComputedValueField implements ReadableJavaField, OriginalFieldProvider, ComputedValue, AnnotationWrapper { private static final EnumSet offsetComputationKinds = EnumSet.of(FieldOffset, TranslateFieldOffset, AtomicFieldUpdaterOffset); private final ResolvedJavaField original; @@ -560,18 +561,8 @@ public ResolvedJavaType getDeclaringClass() { } @Override - public Annotation[] getAnnotations() { - return original.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return original.getDeclaredAnnotations(); - } - - @Override - public T getAnnotation(Class annotationClass) { - return original.getAnnotation(annotationClass); + public AnnotatedElement getAnnotationRoot() { + return original; } public boolean isCompatible(ResolvedJavaField o) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/DeletedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/DeletedMethod.java index ca604de1ecfda..7e7000f256015 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/DeletedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/DeletedMethod.java @@ -61,21 +61,8 @@ public DeletedMethod(ResolvedJavaMethod original, Delete deleteAnnotation) { } @Override - public Annotation[] getAnnotations() { - return AnnotatedField.appendAnnotationTo(original.getAnnotations(), deleteAnnotation); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return AnnotatedField.appendAnnotationTo(original.getDeclaredAnnotations(), deleteAnnotation); - } - - @Override - public T getAnnotation(Class annotationClass) { - if (annotationClass.isInstance(deleteAnnotation)) { - return annotationClass.cast(deleteAnnotation); - } - return original.getAnnotation(annotationClass); + public Annotation[] getInjectedAnnotations() { + return new Annotation[]{deleteAnnotation}; } public static final Method reportErrorMethod = ReflectionUtil.lookupMethod(VMError.class, "unsupportedFeature", String.class); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/InjectedFieldsType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/InjectedFieldsType.java index 4620cb2bf8a4c..8c0ff3780236c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/InjectedFieldsType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/InjectedFieldsType.java @@ -24,11 +24,12 @@ */ package com.oracle.svm.hosted.substitute; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.util.Arrays; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.util.GraalAccess; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.meta.Assumptions.AssumptionResult; @@ -38,7 +39,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -public class InjectedFieldsType implements ResolvedJavaType, OriginalClassProvider { +public class InjectedFieldsType implements ResolvedJavaType, OriginalClassProvider, AnnotationWrapper { private final ResolvedJavaType original; @@ -198,18 +199,8 @@ public ResolvedJavaField[] getStaticFields() { } @Override - public Annotation[] getAnnotations() { - return original.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return original.getDeclaredAnnotations(); - } - - @Override - public T getAnnotation(Class annotationClass) { - return original.getAnnotation(annotationClass); + public AnnotatedElement getAnnotationRoot() { + return original; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/PolymorphicSignatureWrapperMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/PolymorphicSignatureWrapperMethod.java index a86be4b795ed5..24a46295b47c1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/PolymorphicSignatureWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/PolymorphicSignatureWrapperMethod.java @@ -28,6 +28,7 @@ import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandle; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Type; import java.util.List; @@ -47,6 +48,7 @@ import com.oracle.svm.core.invoke.Target_java_lang_invoke_MemberName; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.phases.HostedGraphKit; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantPool; @@ -65,7 +67,7 @@ * assembles the arguments into an array, performing necessary boxing operations. The wrapper then * transfers execution to the underlying varargs method. */ -public class PolymorphicSignatureWrapperMethod implements ResolvedJavaMethod, GraphProvider { +public class PolymorphicSignatureWrapperMethod implements ResolvedJavaMethod, GraphProvider, AnnotationWrapper { private final SubstitutionMethod substitutionBaseMethod; private final ResolvedJavaMethod originalMethod; @@ -336,18 +338,8 @@ public SpeculationLog getSpeculationLog() { } @Override - public T getAnnotation(Class annotationClass) { - return substitutionBaseMethod.getAnnotation(annotationClass); - } - - @Override - public Annotation[] getAnnotations() { - return substitutionBaseMethod.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return substitutionBaseMethod.getDeclaredAnnotations(); + public AnnotatedElement getAnnotationRoot() { + return substitutionBaseMethod; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionMethod.java index 96551f86e4419..c6fd988921fab 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionMethod.java @@ -28,6 +28,7 @@ import static com.oracle.svm.core.util.VMError.unimplemented; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Executable; import java.lang.reflect.Type; @@ -38,6 +39,7 @@ import com.oracle.graal.pointsto.infrastructure.OriginalMethodProvider; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.util.GraalAccess; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantPool; @@ -51,7 +53,7 @@ import jdk.vm.ci.meta.Signature; import jdk.vm.ci.meta.SpeculationLog; -public class SubstitutionMethod implements ResolvedJavaMethod, GraphProvider, OriginalMethodProvider { +public class SubstitutionMethod implements ResolvedJavaMethod, GraphProvider, OriginalMethodProvider, AnnotationWrapper { private final ResolvedJavaMethod original; private final ResolvedJavaMethod annotated; @@ -208,18 +210,8 @@ public ConstantPool getConstantPool() { } @Override - public Annotation[] getAnnotations() { - return annotated.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return annotated.getDeclaredAnnotations(); - } - - @Override - public T getAnnotation(Class annotationClass) { - return annotated.getAnnotation(annotationClass); + public AnnotatedElement getAnnotationRoot() { + return annotated; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionType.java index a3f3d336b96e9..bdd975f4c353f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionType.java @@ -24,12 +24,13 @@ */ package com.oracle.svm.hosted.substitute; -import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Modifier; import java.util.Arrays; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.util.GraalAccess; +import com.oracle.svm.util.AnnotationWrapper; import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.meta.Assumptions.AssumptionResult; @@ -39,7 +40,7 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -public class SubstitutionType implements ResolvedJavaType, OriginalClassProvider { +public class SubstitutionType implements ResolvedJavaType, OriginalClassProvider, AnnotationWrapper { private final ResolvedJavaType original; private final ResolvedJavaType annotated; @@ -237,18 +238,8 @@ public ResolvedJavaField[] getStaticFields() { } @Override - public Annotation[] getAnnotations() { - return annotated.getAnnotations(); - } - - @Override - public Annotation[] getDeclaredAnnotations() { - return annotated.getDeclaredAnnotations(); - } - - @Override - public T getAnnotation(Class annotationClass) { - return annotated.getAnnotation(annotationClass); + public AnnotatedElement getAnnotationRoot() { + return annotated; } @Override diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/functions/JNIFunctionTablesFeature.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/functions/JNIFunctionTablesFeature.java index 30b0f16fb5cb3..8c49fb8694723 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/functions/JNIFunctionTablesFeature.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/functions/JNIFunctionTablesFeature.java @@ -34,7 +34,7 @@ import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/AnnotationEncoder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/AnnotationEncoder.java new file mode 100644 index 0000000000000..f15336da38f22 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/AnnotationEncoder.java @@ -0,0 +1,120 @@ +/* + * 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.hosted; + +import java.util.function.Consumer; + +import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter; +import org.graalvm.compiler.debug.GraalError; + +import com.oracle.svm.core.code.CodeInfoEncoder.Encoders; +import com.oracle.svm.hosted.annotation.AnnotationMemberValue; +import com.oracle.svm.hosted.annotation.AnnotationValue; +import com.oracle.svm.hosted.annotation.AnnotationArrayValue; +import com.oracle.svm.hosted.annotation.AnnotationClassValue; +import com.oracle.svm.hosted.annotation.AnnotationEnumValue; +import com.oracle.svm.hosted.annotation.AnnotationExceptionProxyValue; +import com.oracle.svm.hosted.annotation.AnnotationPrimitiveValue; +import com.oracle.svm.hosted.annotation.AnnotationStringValue; +import com.oracle.svm.hosted.annotation.TypeAnnotationValue; + +public class AnnotationEncoder { + static void encodeAnnotation(UnsafeArrayTypeWriter buf, AnnotationValue value, Encoders encoders) { + buf.putS4(encoders.sourceClasses.getIndex(value.getType())); + buf.putU2(value.getMemberCount()); + value.forEachMember((memberName, memberValue) -> { + buf.putS4(encoders.sourceMethodNames.getIndex(memberName)); + encodeAnnotationMember(buf, memberValue, encoders); + }); + } + + static void encodeAnnotationMember(UnsafeArrayTypeWriter buf, AnnotationMemberValue memberValue, Encoders encoders) { + buf.putU1(memberValue.getTag()); + if (memberValue instanceof AnnotationValue) { + encodeAnnotation(buf, (AnnotationValue) memberValue, encoders); + } else if (memberValue instanceof AnnotationClassValue) { + buf.putS4(encoders.sourceClasses.getIndex(((AnnotationClassValue) memberValue).getValue())); + } else if (memberValue instanceof AnnotationEnumValue) { + buf.putS4(encoders.sourceClasses.getIndex(((AnnotationEnumValue) memberValue).getType())); + buf.putS4(encoders.sourceMethodNames.getIndex(((AnnotationEnumValue) memberValue).getName())); + } else if (memberValue instanceof AnnotationStringValue) { + buf.putS4(encoders.sourceMethodNames.getIndex(((AnnotationStringValue) memberValue).getValue())); + } else if (memberValue instanceof AnnotationArrayValue) { + buf.putU2(((AnnotationArrayValue) memberValue).getElementCount()); + ((AnnotationArrayValue) memberValue).forEachElement(element -> encodeAnnotationMember(buf, element, encoders)); + } else if (memberValue instanceof AnnotationPrimitiveValue) { + Object value = ((AnnotationPrimitiveValue) memberValue).getValue(); + switch (memberValue.getTag()) { + case 'B': + buf.putS1((byte) value); + return; + case 'C': + buf.putU2((char) value); + return; + case 'D': + buf.putS8(Double.doubleToRawLongBits((double) value)); + return; + case 'F': + buf.putS4(Float.floatToRawIntBits((float) value)); + return; + case 'I': + buf.putS4((int) value); + return; + case 'J': + buf.putS8((long) value); + return; + case 'S': + buf.putS2((short) value); + return; + case 'Z': + buf.putU1((boolean) value ? 1 : 0); + return; + default: + throw GraalError.shouldNotReachHere("Invalid annotation encoding"); + } + } else if (memberValue instanceof AnnotationExceptionProxyValue) { + buf.putS4(encoders.objectConstants.getIndex(((AnnotationExceptionProxyValue) memberValue).getObjectConstant())); + } else { + throw GraalError.shouldNotReachHere("Invalid annotation member type: " + memberValue.getClass()); + } + } + + static void encodeTypeAnnotation(UnsafeArrayTypeWriter buf, TypeAnnotationValue value, Encoders encoders) { + for (byte b : value.getTargetInfo()) { + buf.putU1(b); + } + for (byte b : value.getLocationInfo()) { + buf.putU1(b); + } + encodeAnnotation(buf, value.getAnnotationData(), encoders); + } + + static void encodeArray(UnsafeArrayTypeWriter buf, T[] elements, Consumer encoder) { + buf.putU2(elements.length); + for (T element : elements) { + encoder.accept(element); + } + } +} 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 bcf59e2618c09..386096c667d38 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,18 +24,12 @@ */ package com.oracle.svm.reflect.hosted; -import static com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl.getAnnotationEncodingType; -import static com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl.getTypeAnnotations; - -import java.lang.annotation.Annotation; 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.MalformedParameterizedTypeException; import java.lang.reflect.Member; import java.lang.reflect.Method; @@ -44,6 +38,7 @@ import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -58,7 +53,6 @@ import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.ConfigurationCondition; -import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -66,7 +60,6 @@ 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; @@ -76,19 +69,20 @@ 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.annotation.AnnotationMemberValue; import com.oracle.svm.hosted.annotation.AnnotationSubstitutionType; +import com.oracle.svm.hosted.annotation.AnnotationValue; +import com.oracle.svm.hosted.annotation.SubstrateAnnotationExtracter; +import com.oracle.svm.hosted.annotation.TypeAnnotationValue; import com.oracle.svm.hosted.meta.InternalRuntimeReflectionSupport; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; -import com.oracle.svm.util.ModuleSupport; +import com.oracle.svm.util.GuardedAnnotationAccess; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -import sun.reflect.annotation.AnnotationType; -import sun.reflect.annotation.EnumConstantNotPresentExceptionProxy; -import sun.reflect.annotation.TypeAnnotation; -import sun.reflect.annotation.TypeNotPresentExceptionProxy; +import sun.reflect.annotation.ExceptionProxy; public class ReflectionDataBuilder extends ConditionalConfigurationRegistry implements InternalRuntimeReflectionSupport { @@ -119,7 +113,14 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl /* Keep track of annotation interface members to include in proxy classes */ private final Map, Set> annotationMembers = new HashMap<>(); - public ReflectionDataBuilder() { + private final Map filteredAnnotations = new ConcurrentHashMap<>(); + private final Map filteredParameterAnnotations = new ConcurrentHashMap<>(); + private final Map filteredTypeAnnotations = new ConcurrentHashMap<>(); + + private final SubstrateAnnotationExtracter annotationExtracter; + + ReflectionDataBuilder(SubstrateAnnotationExtracter annotationExtracter) { + this.annotationExtracter = annotationExtracter; } @Override @@ -434,14 +435,8 @@ private void registerTypesForClass(DuringAnalysisAccessImpl access, AnalysisType } 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 - } + registerTypesForAnnotations(access, analysisType); + registerTypesForTypeAnnotations(access, analysisType); } private void registerTypesForField(DuringAnalysisAccessImpl access, AnalysisField analysisField, Field reflectField) { @@ -462,14 +457,8 @@ private void registerTypesForField(DuringAnalysisAccessImpl access, AnalysisFiel /* * 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 - } + registerTypesForAnnotations(access, analysisField); + registerTypesForTypeAnnotations(access, analysisField); } private void registerTypesForMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod, Executable reflectMethod) { @@ -491,24 +480,11 @@ private void registerTypesForMethod(DuringAnalysisAccessImpl access, AnalysisMet /* * Enable runtime instantiation of annotations */ - for (Annotation annotation : GuardedAnnotationAccess.getDeclaredAnnotations(analysisMethod)) { - registerTypesForAnnotation(access, annotation); - } - for (Annotation[] parameterAnnotations : reflectMethod.getParameterAnnotations()) { - for (Annotation parameterAnnotation : parameterAnnotations) { - registerTypesForAnnotation(access, parameterAnnotation); - } - } - for (TypeAnnotation typeAnnotation : getTypeAnnotations(reflectMethod)) { - // Checkstyle: allow direct annotation access - registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); - // Checkstyle: disallow direct annotation access - } - if (reflectMethod instanceof Method) { - Object defaultValue = ((Method) reflectMethod).getDefaultValue(); - if (defaultValue != null) { - registerTypesForAnnotationValue(access, getAnnotationEncodingType(defaultValue), defaultValue); - } + registerTypesForAnnotations(access, analysisMethod); + registerTypesForParameterAnnotations(access, analysisMethod); + registerTypesForTypeAnnotations(access, analysisMethod); + if (!analysisMethod.isConstructor()) { + registerTypesForAnnotationDefault(access, analysisMethod); } } @@ -568,73 +544,92 @@ private void makeTypeReachable(DuringAnalysisAccessImpl access, Type type) { } } - private static void registerTypesForRecordComponent(DuringAnalysisAccessImpl access, Object recordComponent) { - for (Annotation annotation : GuardedAnnotationAccess.getAnnotations((AnnotatedElement) recordComponent)) { - registerTypesForAnnotation(access, annotation); + private void registerTypesForRecordComponent(DuringAnalysisAccessImpl access, Object recordComponent) { + registerTypesForAnnotations(access, (AnnotatedElement) recordComponent); + registerTypesForTypeAnnotations(access, (AnnotatedElement) recordComponent); + } + + private void registerTypesForAnnotations(DuringAnalysisAccessImpl access, AnnotatedElement annotatedElement) { + if (annotatedElement != null) { + filteredAnnotations.computeIfAbsent(annotatedElement, element -> { + List includedAnnotations = new ArrayList<>(); + for (AnnotationValue annotation : annotationExtracter.getDeclaredAnnotationData(element)) { + if (includeAnnotation(access, annotation)) { + includedAnnotations.add(annotation); + registerTypes(access, annotation.getTypes()); + } + } + return includedAnnotations.toArray(new AnnotationValue[0]); + }); + } + } + + private void registerTypesForParameterAnnotations(DuringAnalysisAccessImpl access, AnalysisMethod executable) { + if (executable != null) { + filteredParameterAnnotations.computeIfAbsent(executable, element -> { + AnnotationValue[][] parameterAnnotations = annotationExtracter.getParameterAnnotationData(element); + AnnotationValue[][] includedParameterAnnotations = new AnnotationValue[parameterAnnotations.length][]; + for (int i = 0; i < includedParameterAnnotations.length; ++i) { + AnnotationValue[] annotations = parameterAnnotations[i]; + List includedAnnotations = new ArrayList<>(); + for (AnnotationValue annotation : annotations) { + if (includeAnnotation(access, annotation)) { + includedAnnotations.add(annotation); + registerTypes(access, annotation.getTypes()); + } + } + includedParameterAnnotations[i] = includedAnnotations.toArray(new AnnotationValue[0]); + } + return includedParameterAnnotations; + }); } - for (TypeAnnotation typeAnnotation : getTypeAnnotations((AnnotatedElement) recordComponent)) { - // Checkstyle: allow direct annotation access - registerTypesForAnnotation(access, typeAnnotation.getAnnotation()); - // Checkstyle: disallow direct annotation access + } + + private void registerTypesForTypeAnnotations(DuringAnalysisAccessImpl access, AnnotatedElement annotatedElement) { + if (annotatedElement != null) { + filteredTypeAnnotations.computeIfAbsent(annotatedElement, element -> { + List includedTypeAnnotations = new ArrayList<>(); + for (TypeAnnotationValue typeAnnotation : annotationExtracter.getTypeAnnotationData(element)) { + if (includeAnnotation(access, typeAnnotation.getAnnotationData())) { + includedTypeAnnotations.add(typeAnnotation); + registerTypes(access, typeAnnotation.getAnnotationData().getTypes()); + } + } + return includedTypeAnnotations.toArray(new TypeAnnotationValue[0]); + }); } } - private static void registerTypesForAnnotation(DuringAnalysisAccessImpl access, Annotation annotation) { - if (annotation != null) { - registerTypesForAnnotationValue(access, annotation.annotationType(), annotation); + private void registerTypesForAnnotationDefault(DuringAnalysisAccessImpl access, AnalysisMethod method) { + AnnotationMemberValue annotationDefault = annotationExtracter.getAnnotationDefaultData(method); + if (annotationDefault != null) { + registerTypes(access, annotationDefault.getTypes()); } } - @SuppressWarnings("unchecked") - private static void registerTypesForAnnotationValue(DuringAnalysisAccessImpl access, Class type, Object value) { - if (type.isAnnotation() && !SubstitutionReflectivityFilter.shouldExclude(type, access.getMetaAccess(), access.getUniverse())) { - makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(type)); + private static boolean includeAnnotation(DuringAnalysisAccessImpl access, AnnotationValue annotationValue) { + for (Class type : annotationValue.getTypes()) { + if (type == null || SubstitutionReflectivityFilter.shouldExclude(type, access.getMetaAccess(), access.getUniverse())) { + return false; + } + } + return true; + } + + @SuppressWarnings("cast") + private static void registerTypes(DuringAnalysisAccessImpl access, Collection> types) { + for (Class type : types) { + AnalysisType analysisType = access.getMetaAccess().lookupJavaType(type); + makeAnalysisTypeReachable(access, analysisType); + if (type.isAnnotation()) { + ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(type); + } /* - * Parsing annotation data in reflection classes requires being able to instantiate all - * annotation types at runtime. + * Exception proxies are stored as-is in the image heap */ - ImageSingletons.lookup(AnnotationTypeSupport.class).createInstance((Class) type); - ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ReflectionDataBuilder.class, type); - ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(type); - - Annotation annotation = (Annotation) value; - AnnotationType annotationType = AnnotationType.getInstance((Class) type); - for (Map.Entry> entry : annotationType.memberTypes().entrySet()) { - String valueName = entry.getKey(); - Class valueType = entry.getValue(); - try { - Method getAnnotationValue = annotationType.members().get(valueName); - getAnnotationValue.setAccessible(true); - Object annotationValue = getAnnotationValue.invoke(annotation); - registerTypesForAnnotationValue(access, valueType, annotationValue); - } 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()) { - Class componentType = type.getComponentType(); - if (!componentType.isPrimitive()) { - for (Object val : (Object[]) value) { - registerTypesForAnnotationValue(access, componentType, val); - } + if (ExceptionProxy.class.isAssignableFrom(type)) { + analysisType.registerAsInHeap(); } - } else if (type == Class.class) { - makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType((Class) value)); - } else if (type.isEnum()) { - makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(type)); } } @@ -865,6 +860,31 @@ public Set getHeapReflectionObjects() { return Collections.unmodifiableSet(heapReflectionObjects); } + private static final AnnotationValue[] NO_ANNOTATIONS = new AnnotationValue[0]; + + public AnnotationValue[] getAnnotationData(AnnotatedElement element) { + assert sealed; + return filteredAnnotations.getOrDefault(element, NO_ANNOTATIONS); + } + + private static final AnnotationValue[][] NO_PARAMETER_ANNOTATIONS = new AnnotationValue[0][0]; + + public AnnotationValue[][] getParameterAnnotationData(AnalysisMethod element) { + assert sealed; + return filteredParameterAnnotations.getOrDefault(element, NO_PARAMETER_ANNOTATIONS); + } + + private static final TypeAnnotationValue[] NO_TYPE_ANNOTATIONS = new TypeAnnotationValue[0]; + + public TypeAnnotationValue[] getTypeAnnotationData(AnnotatedElement element) { + assert sealed; + return filteredTypeAnnotations.getOrDefault(element, NO_TYPE_ANNOTATIONS); + } + + public AnnotationMemberValue getAnnotationDefaultData(AnnotatedElement element) { + return annotationExtracter.getAnnotationDefaultData(element); + } + @Override public int getReflectionClassesCount() { return reflectionClasses.size(); 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 f787a8a47b6a8..ac382e3d15860 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 @@ -34,6 +34,8 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import com.oracle.svm.hosted.annotation.SubstrateAnnotationExtracter; +import com.oracle.svm.util.AnnotationExtracter; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; import org.graalvm.compiler.phases.util.Providers; @@ -202,7 +204,7 @@ private MethodPointer register(ResolvedJavaMethod method) { public void afterRegistration(AfterRegistrationAccess access) { ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, null, false, "java.base", "jdk.internal.reflect"); - reflectionData = new ReflectionDataBuilder(); + reflectionData = new ReflectionDataBuilder((SubstrateAnnotationExtracter) ImageSingletons.lookup(AnnotationExtracter.class)); ImageSingletons.add(RuntimeReflectionSupport.class, reflectionData); ImageSingletons.add(InternalRuntimeReflectionSupport.class, reflectionData); } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java index 87ca425ff00cd..112c9a4c841f9 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadata.java @@ -26,20 +26,20 @@ 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.annotation.AnnotationMemberValue; +import com.oracle.svm.hosted.annotation.AnnotationValue; +import com.oracle.svm.hosted.annotation.TypeAnnotationValue; 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 { @@ -54,10 +54,10 @@ public void afterRegistration(AfterRegistrationAccess access) { public class ReflectionMetadata { static class AnnotatedElementMetadata { - final Annotation[] annotations; - final TypeAnnotation[] typeAnnotations; + final AnnotationValue[] annotations; + final TypeAnnotationValue[] typeAnnotations; - AnnotatedElementMetadata(Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + AnnotatedElementMetadata(AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations) { this.annotations = annotations; this.typeAnnotations = typeAnnotations; } @@ -70,8 +70,8 @@ static class ClassMetadata extends AnnotatedElementMetadata { final HostedType[] permittedSubclasses; final int classAccessFlags; - ClassMetadata(HostedType[] classes, Object[] enclosingMethodInfo, RecordComponentMetadata[] recordComponents, HostedType[] permittedSubclasses, int classAccessFlags, Annotation[] annotations, - TypeAnnotation[] typeAnnotations) { + ClassMetadata(HostedType[] classes, Object[] enclosingMethodInfo, RecordComponentMetadata[] recordComponents, HostedType[] permittedSubclasses, int classAccessFlags, + AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations) { super(annotations, typeAnnotations); this.classes = classes; this.enclosingMethodInfo = enclosingMethodInfo; @@ -88,7 +88,8 @@ static class AccessibleObjectMetadata extends AnnotatedElementMetadata { final int modifiers; final String signature; - AccessibleObjectMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, int modifiers, String signature, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + AccessibleObjectMetadata(boolean complete, JavaConstant heapObject, HostedType declaringType, int modifiers, String signature, AnnotationValue[] annotations, + TypeAnnotationValue[] typeAnnotations) { super(annotations, typeAnnotations); this.complete = complete; this.heapObject = heapObject; @@ -107,7 +108,7 @@ static class FieldMetadata extends AccessibleObjectMetadata { final String deletedReason; private FieldMetadata(boolean complete, boolean hiding, JavaConstant heapObject, HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, - Annotation[] annotations, TypeAnnotation[] typeAnnotations, int offset, String deletedReason) { + AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations, int offset, String deletedReason) { super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations); this.hiding = hiding; this.name = name; @@ -118,13 +119,13 @@ private FieldMetadata(boolean complete, boolean hiding, JavaConstant heapObject, } /* Field registered for reflection */ - FieldMetadata(HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, Annotation[] annotations, TypeAnnotation[] typeAnnotations, - int offset, String deletedReason) { + FieldMetadata(HostedType declaringType, String name, HostedType type, int modifiers, boolean trustedFinal, String signature, AnnotationValue[] annotations, + TypeAnnotationValue[] typeAnnotations, int offset, String deletedReason) { this(true, false, null, declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason); } /* Field in heap */ - FieldMetadata(boolean registered, JavaConstant heapObject, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + FieldMetadata(boolean registered, JavaConstant heapObject, AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations) { this(registered, false, heapObject, null, null, null, 0, false, null, annotations, typeAnnotations, LOC_UNINITIALIZED, null); } @@ -142,12 +143,13 @@ private FieldMetadata(boolean complete, boolean hiding, JavaConstant heapObject, static class ExecutableMetadata extends AccessibleObjectMetadata { final HostedType[] parameterTypes; final HostedType[] exceptionTypes; - final Annotation[][] parameterAnnotations; + final AnnotationValue[][] 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) { + AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, + JavaConstant accessor) { super(complete, heapObject, declaringType, modifiers, signature, annotations, typeAnnotations); this.parameterTypes = parameterTypes; this.exceptionTypes = exceptionTypes; @@ -161,11 +163,11 @@ static class MethodMetadata extends ExecutableMetadata { final boolean hiding; final String name; final HostedType returnType; - final Object annotationDefault; + final AnnotationMemberValue annotationDefault; private MethodMetadata(boolean complete, boolean hiding, JavaConstant heapObject, HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, - HostedType[] exceptionTypes, String signature, Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, - ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { + HostedType[] exceptionTypes, String signature, AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, AnnotationMemberValue annotationDefault, + TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { super(complete, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor); this.hiding = hiding; this.name = name; @@ -175,16 +177,15 @@ private MethodMetadata(boolean complete, boolean hiding, JavaConstant heapObject /* Method registered for reflection */ MethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, HostedType[] exceptionTypes, String signature, - Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, - JavaConstant accessor) { + AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, AnnotationMemberValue annotationDefault, TypeAnnotationValue[] typeAnnotations, + ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { this(true, false, null, declaringClass, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, parameterAnnotations, annotationDefault, typeAnnotations, - reflectParameters, - accessor); + reflectParameters, accessor); } /* Method in heap */ - MethodMetadata(boolean registered, JavaConstant heapObject, Annotation[] annotations, Annotation[][] parameterAnnotations, Object annotationDefault, TypeAnnotation[] typeAnnotations, - ReflectParameterMetadata[] reflectParameters) { + MethodMetadata(boolean registered, JavaConstant heapObject, AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, AnnotationMemberValue annotationDefault, + TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters) { this(registered, false, heapObject, null, null, null, 0, null, null, null, annotations, parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, null); } @@ -202,18 +203,19 @@ private MethodMetadata(boolean complete, boolean hiding, JavaConstant heapObject static class ConstructorMetadata extends ExecutableMetadata { private ConstructorMetadata(boolean complete, JavaConstant heapObject, HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, - Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { + AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, + JavaConstant accessor) { super(complete, heapObject, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor); } /* Constructor registered for reflection */ - ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, Annotation[] annotations, - Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { + ConstructorMetadata(HostedType declaringClass, HostedType[] parameterTypes, int modifiers, HostedType[] exceptionTypes, String signature, AnnotationValue[] annotations, + AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters, JavaConstant accessor) { this(true, null, declaringClass, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, typeAnnotations, reflectParameters, accessor); } /* Constructor in heap */ - ConstructorMetadata(boolean registered, JavaConstant heapObject, Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, + ConstructorMetadata(boolean registered, JavaConstant heapObject, AnnotationValue[] annotations, AnnotationValue[][] parameterAnnotations, TypeAnnotationValue[] typeAnnotations, ReflectParameterMetadata[] reflectParameters) { this(registered, heapObject, null, null, 0, null, null, annotations, parameterAnnotations, typeAnnotations, reflectParameters, null); } @@ -231,7 +233,7 @@ static class RecordComponentMetadata extends AnnotatedElementMetadata { final String signature; final JavaConstant accessor; - RecordComponentMetadata(HostedType declaringType, String name, HostedType type, String signature, JavaConstant accessor, Annotation[] annotations, TypeAnnotation[] typeAnnotations) { + RecordComponentMetadata(HostedType declaringType, String name, HostedType type, String signature, JavaConstant accessor, AnnotationValue[] annotations, TypeAnnotationValue[] typeAnnotations) { super(annotations, typeAnnotations); this.declaringType = declaringType; this.name = name; diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java index b291b008d8f60..95cd5f5cf185a 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionMetadataEncoderImpl.java @@ -31,7 +31,6 @@ 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; @@ -40,7 +39,6 @@ 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; @@ -52,18 +50,18 @@ 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.util.GuardedAnnotationAccess; +import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; +import com.oracle.graal.pointsto.infrastructure.WrappedElement; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.code.CodeInfoEncoder; import com.oracle.svm.core.hub.DynamicHub; @@ -72,6 +70,9 @@ 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.annotation.AnnotationMemberValue; +import com.oracle.svm.hosted.annotation.AnnotationValue; +import com.oracle.svm.hosted.annotation.TypeAnnotationValue; import com.oracle.svm.hosted.image.NativeImageCodeCache.ReflectionMetadataEncoder; import com.oracle.svm.hosted.image.NativeImageCodeCache.ReflectionMetadataEncoderFactory; import com.oracle.svm.hosted.meta.HostedField; @@ -91,18 +92,12 @@ 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.GuardedAnnotationAccess; import com.oracle.svm.util.ReflectionUtil; import jdk.internal.reflect.Reflection; 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 @@ -136,7 +131,7 @@ public ReflectionMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { private final CodeInfoEncoder.Encoders encoders; private final ReflectionDataAccessors accessors; - private final Map, JavaConstant> annotationExceptionProxies = new HashMap<>(); + private final ReflectionDataBuilder dataBuilder; private final TreeSet sortedTypes = new TreeSet<>(Comparator.comparingLong(t -> t.getHub().getTypeID())); private final Map classData = new HashMap<>(); private final Map> fieldData = new HashMap<>(); @@ -154,6 +149,7 @@ public ReflectionMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { public ReflectionMetadataEncoderImpl(CodeInfoEncoder.Encoders encoders) { this.encoders = encoders; this.accessors = new ReflectionDataAccessors(); + this.dataBuilder = (ReflectionDataBuilder) ImageSingletons.lookup(RuntimeReflectionSupport.class); } private void registerClass(HostedType type, ClassMetadata metadata) { @@ -252,8 +248,6 @@ public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Cla RecordComponentMetadata[] recordComponents = getRecordComponents(metaAccess, type, javaClass); Class[] permittedSubclasses = getPermittedSubclasses(metaAccess, javaClass); int classAccessFlags = Reflection.getClassAccessFlags(javaClass); - Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(type); - TypeAnnotation[] typeAnnotations = getTypeAnnotations(javaClass); /* Register string and class values in annotations */ encoders.sourceClasses.addObject(javaClass); @@ -264,8 +258,9 @@ public void addClassMetadata(MetaAccessProvider metaAccess, HostedType type, Cla } HostedType[] innerTypes = registerClassValues(metaAccess, innerClasses); HostedType[] permittedSubtypes = (permittedSubclasses != null) ? registerClassValues(metaAccess, permittedSubclasses) : null; - annotations = registerAnnotationValues(metaAccess, annotations); - typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + AnalysisType analysisType = type.getWrapped(); + AnnotationValue[] annotations = registerAnnotationValues(analysisType); + TypeAnnotationValue[] typeAnnotations = registerTypeAnnotationValues(analysisType); registerClass(type, new ClassMetadata(innerTypes, enclosingMethodInfo, recordComponents, permittedSubtypes, classAccessFlags, annotations, typeAnnotations)); } @@ -313,8 +308,6 @@ public void addReflectionFieldMetadata(MetaAccessProvider metaAccess, HostedFiel 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; @@ -325,8 +318,9 @@ public void addReflectionFieldMetadata(MetaAccessProvider metaAccess, HostedFiel encoders.sourceMethodNames.addObject(signature); encoders.sourceMethodNames.addObject(deletedReason); /* Register string and class values in annotations */ - annotations = registerAnnotationValues(metaAccess, annotations); - typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); + AnalysisField analysisField = hostedField.getWrapped(); + AnnotationValue[] annotations = registerAnnotationValues(analysisField); + TypeAnnotationValue[] typeAnnotations = registerTypeAnnotationValues(analysisField); registerField(declaringType, reflectField, new FieldMetadata(declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason)); } @@ -342,11 +336,6 @@ public void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, Hoste 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) { @@ -361,19 +350,12 @@ public void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, Hoste } 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(getAnnotationEncodingType(annotationDefault), annotationDefault); - } - typeAnnotations = registerTypeAnnotationValues(metaAccess, typeAnnotations); - if (reflectParameters != null) { - for (ReflectParameterMetadata parameter : reflectParameters) { - encoders.sourceMethodNames.addObject(parameter.name); - } - } + AnalysisMethod analysisMethod = hostedMethod.getWrapped(); + AnnotationValue[] annotations = registerAnnotationValues(analysisMethod); + AnnotationValue[][] parameterAnnotations = registerParameterAnnotationValues(analysisMethod); + AnnotationMemberValue annotationDefault = isMethod ? registerAnnotationDefaultValues(analysisMethod) : null; + TypeAnnotationValue[] typeAnnotations = registerTypeAnnotationValues(analysisMethod); + ReflectParameterMetadata[] reflectParameters = registerReflectParameters(reflectMethod); JavaConstant accessorConstant = null; if (accessor != null) { accessorConstant = SubstrateObjectConstant.forObject(accessor); @@ -381,11 +363,11 @@ public void addReflectionExecutableMetadata(MetaAccessProvider metaAccess, Hoste } if (isMethod) { - registerMethod(declaringType, reflectMethod, new MethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, - parameterAnnotations, annotationDefault, typeAnnotations, reflectParameters, accessorConstant)); + registerMethod(declaringType, reflectMethod, new MethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, annotations, parameterAnnotations, + annotationDefault, typeAnnotations, reflectParameters, accessorConstant)); } else { - registerConstructor(declaringType, reflectMethod, new ConstructorMetadata(declaringType, parameterTypes, modifiers, exceptionTypes, signature, annotations, - parameterAnnotations, typeAnnotations, reflectParameters, accessorConstant)); + registerConstructor(declaringType, reflectMethod, new ConstructorMetadata(declaringType, parameterTypes, modifiers, exceptionTypes, signature, annotations, parameterAnnotations, + typeAnnotations, reflectParameters, accessorConstant)); } } @@ -403,31 +385,17 @@ private static boolean isTrustedFinal(Field field) { } @Override - public void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, AccessibleObject object, boolean registered) { + public void addHeapAccessibleObjectMetadata(MetaAccessProvider metaAccess, WrappedElement hostedObject, AccessibleObject object, boolean registered) { 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(getAnnotationEncodingType(annotationDefault), annotationDefault); - } - if (reflectParameters != null) { - for (ReflectParameterMetadata parameter : reflectParameters) { - encoders.sourceMethodNames.addObject(parameter.name); - } - } - } + AnnotatedElement analysisObject = (AnnotatedElement) hostedObject.getWrapped(); + AnnotationValue[] annotations = registerAnnotationValues(analysisObject); + AnnotationValue[][] parameterAnnotations = isExecutable ? registerParameterAnnotationValues((AnalysisMethod) analysisObject) : null; + TypeAnnotationValue[] typeAnnotations = registerTypeAnnotationValues(analysisObject); + AnnotationMemberValue annotationDefault = isMethod ? registerAnnotationDefaultValues((AnalysisMethod) analysisObject) : null; + ReflectParameterMetadata[] reflectParameters = isExecutable ? registerReflectParameters((Executable) object) : null; AccessibleObject holder = getHolder(object); JavaConstant heapObjectConstant = SubstrateObjectConstant.forObject(holder); encoders.objectConstants.addObject(heapObjectConstant); @@ -457,16 +425,6 @@ private static AccessibleObject getHolder(AccessibleObject accessibleObject) { } } - 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) { @@ -484,90 +442,60 @@ private HostedType[] registerClassValues(MetaAccessProvider metaAccess, Class return includedClasses.toArray(new HostedType[0]); } - private Annotation[] registerAnnotationValues(MetaAccessProvider metaAccess, Annotation... annotations) { - Set includedAnnotations = new HashSet<>(); - for (Annotation annotation : annotations) { - if (annotation != null && registerAnnotation(metaAccess, annotation)) { - includedAnnotations.add(annotation); - } + private AnnotationValue[] registerAnnotationValues(AnnotatedElement element) { + AnnotationValue[] annotations = dataBuilder.getAnnotationData(element); + for (AnnotationValue annotation : annotations) { + registerValues(annotation); } - return includedAnnotations.toArray(new Annotation[0]); + return annotations; } - 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 (annotation != null && registerAnnotation(metaAccess, annotation)) { - includedTypeAnnotations.add(typeAnnotation); + private AnnotationValue[][] registerParameterAnnotationValues(AnalysisMethod element) { + AnnotationValue[][] parameterAnnotations = dataBuilder.getParameterAnnotationData(element); + for (AnnotationValue[] annotations : parameterAnnotations) { + for (AnnotationValue annotation : annotations) { + registerValues(annotation); } } - return includedTypeAnnotations.toArray(new TypeAnnotation[0]); + return parameterAnnotations; } - 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; + private AnnotationMemberValue registerAnnotationDefaultValues(AnalysisMethod method) { + AnnotationMemberValue annotationDefault = dataBuilder.getAnnotationDefaultData(method); + if (annotationDefault != null) { + registerValues(annotationDefault); } - return false; + return annotationDefault; + } + + private TypeAnnotationValue[] registerTypeAnnotationValues(AnnotatedElement element) { + TypeAnnotationValue[] typeAnnotations = dataBuilder.getTypeAnnotationData(element); + for (TypeAnnotationValue typeAnnotation : typeAnnotations) { + registerValues(typeAnnotation.getAnnotationData()); + } + return typeAnnotations; } - @SuppressWarnings("unchecked") - private void registerAnnotationValue(Class type, Object value) { - if (type.isAnnotation()) { - Annotation annotation = (Annotation) value; - AnnotationType annotationType = AnnotationType.getInstance((Class) type); + private void registerValues(AnnotationMemberValue annotationValue) { + for (Class type : annotationValue.getTypes()) { 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); - } + } + for (String string : annotationValue.getStrings()) { + encoders.sourceMethodNames.addObject(string); + } + for (JavaConstant proxy : annotationValue.getExceptionProxies()) { + encoders.objectConstants.addObject(proxy); + } + } + + private ReflectParameterMetadata[] registerReflectParameters(Executable executable) { + ReflectParameterMetadata[] reflectParameters = getReflectParameters(executable); + if (reflectParameters != null) { + for (ReflectParameterMetadata parameter : reflectParameters) { + encoders.sourceMethodNames.addObject(parameter.name); } - } 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()); } + return reflectParameters; } @Override @@ -662,21 +590,19 @@ private RecordComponentMetadata[] getRecordComponents(MetaAccessProvider metaAcc } RecordComponentMetadata[] metadata = new RecordComponentMetadata[recordComponents.length]; for (int i = 0; i < recordComponents.length; ++i) { - Object recordComponent = recordComponents[i]; + AnnotatedElement recordComponent = (AnnotatedElement) recordComponents[i]; 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); + AnnotationValue[] annotations = registerAnnotationValues(recordComponent); + TypeAnnotationValue[] typeAnnotations = registerTypeAnnotationValues(recordComponent); JavaConstant accessorConstant = null; if (accessor != null) { accessorConstant = SubstrateObjectConstant.forObject(accessor); @@ -747,9 +673,9 @@ public void encodeAllAndInstall() { 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 enclosingMethodInfoIndex = addElement(buf, encodeEnclosingMethod(classMetadata.enclosingMethodInfo)); + int annotationsIndex = addEncodedElement(buf, encodeAnnotations(classMetadata.annotations)); + int typeAnnotationsIndex = addEncodedElement(buf, encodeTypeAnnotations(classMetadata.typeAnnotations)); int classesEncodingIndex = encodeAndAddCollection(buf, classMetadata.classes, this::encodeType, false); int permittedSubclassesIndex = JavaVersionUtil.JAVA_SPEC >= 17 ? encodeAndAddCollection(buf, classMetadata.permittedSubclasses, this::encodeType, true) : NO_DATA; if (anySet(enclosingMethodInfoIndex, annotationsIndex, typeAnnotationsIndex, classesEncodingIndex, permittedSubclassesIndex)) { @@ -774,9 +700,8 @@ public void encodeAllAndInstall() { 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)); + if (metadata instanceof MethodMetadata) { + annotationDefaultEncodings.put((Method) heapObject, encodeAnnotationDefault(((MethodMetadata) metadata).annotationDefault)); } } } @@ -798,8 +723,7 @@ private static int encodeAndAddCollection(UnsafeArrayTypeWriter buf, T[] dat return offset; } - private static int encodeAndAddElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback) { - byte[] encoding = encodeCallback.apply(data); + private static int addElement(UnsafeArrayTypeWriter buf, byte[] encoding) { if (encoding == null) { return NO_DATA; } @@ -808,8 +732,7 @@ private static int encodeAndAddElement(UnsafeArrayTypeWriter buf, T data, Fu return offset; } - private static int encodeAndAddEncodedElement(UnsafeArrayTypeWriter buf, T data, Function encodeCallback) { - byte[] encoding = encodeCallback.apply(data); + private static int addEncodedElement(UnsafeArrayTypeWriter buf, byte[] encoding) { if (encoding == null) { return NO_DATA; } @@ -887,9 +810,7 @@ private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata exec 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, encodeAnnotationDefault(((MethodMetadata) executable).annotationDefault)); } encodeByteArray(buf, encodeTypeAnnotations(executable.typeAnnotations)); encodeByteArray(buf, encodeReflectParameters(executable.reflectParameters)); @@ -981,333 +902,46 @@ private static Parameter[] getRawParameters(Executable executable) { * {@link Target_jdk_internal_reflect_ConstantPool} and * {@link Target_sun_reflect_annotation_AnnotationParser}) */ - public byte[] encodeAnnotations(Annotation[] annotations) { + public byte[] encodeAnnotations(AnnotationValue[] 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); - } + AnnotationEncoder.encodeArray(buf, annotations, annotation -> AnnotationEncoder.encodeAnnotation(buf, annotation, encoders)); return buf.toArray(); } - private byte[] encodeParameterAnnotations(Annotation[][] annotations) { - if (!hasAnnotation(annotations)) { + private byte[] encodeParameterAnnotations(AnnotationValue[][] parameterAnnotations) { + if (parameterAnnotations.length == 0) { 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); - } + buf.putU1(parameterAnnotations.length); + for (AnnotationValue[] annotations : parameterAnnotations) { + AnnotationEncoder.encodeArray(buf, annotations, annotation -> AnnotationEncoder.encodeAnnotation(buf, annotation, encoders)); } 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 (String memberName : orderedAnnotationMemberNames(annotation)) { - Method valueAccessor = type.members().get(memberName); - buf.putS4(encoders.sourceMethodNames.getIndex(memberName)); - try { - encodeValue(buf, valueAccessor.invoke(annotation), type.memberTypes().get(memberName)); - } catch (InvocationTargetException e) { - encodeValue(buf, annotationExceptionProxies.get(Pair.create(annotation, memberName)), Throwable.class); - } catch (IllegalAccessException e) { - throw GraalError.shouldNotReachHere(e); - } - } - } - - private static final Field annotationInvocationHandlerMemberValues; - - static { - try { - annotationInvocationHandlerMemberValues = ReflectionUtil.lookupField(Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"), "memberValues"); - } catch (ClassNotFoundException e) { - throw GraalError.shouldNotReachHere(); - } - } - - /* - * The order of annotation values returned by AnnotationType.members() is random, so we use the - * LinkedHashMap stored in the invocation handler to ensure the annotation values are encoded in - * the right order. - */ - @SuppressWarnings("unchecked") - private static Set orderedAnnotationMemberNames(Annotation annotation) { - try { - Map memberValues = (Map) annotationInvocationHandlerMemberValues.get(Proxy.getInvocationHandler(annotation)); - return memberValues.keySet(); - } catch (IllegalAccessException e) { - throw GraalError.shouldNotReachHere(); - } - } - - private byte[] encodeMemberValue(Object value) { - if (value == null) { + private byte[] encodeAnnotationDefault(AnnotationMemberValue annotationDefault) { + if (annotationDefault == null) { return null; } UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); - encodeValue(buf, value, getAnnotationEncodingType(value)); + AnnotationEncoder.encodeAnnotationMember(buf, annotationDefault, encoders); 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()); - } - } - - static Class getAnnotationEncodingType(Object value) { - Class type = value.getClass(); - if (Proxy.isProxyClass(type)) { - assert type.getInterfaces().length == 1; - type = type.getInterfaces()[0]; - } else if (value instanceof Enum) { - type = ((Enum) value).getDeclaringClass(); - } - return type; - } - - private byte[] encodeTypeAnnotations(TypeAnnotation[] annotations) { - if (annotations.length == 0) { + public byte[] encodeTypeAnnotations(TypeAnnotationValue[] typeAnnotations) { + if (typeAnnotations.length == 0) { return null; } UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess(), true); - buf.putU2(annotations.length); - for (TypeAnnotation typeAnnotation : annotations) { - encodeTypeAnnotation(buf, typeAnnotation); - } + AnnotationEncoder.encodeArray(buf, typeAnnotations, + typeAnnotation -> AnnotationEncoder.encodeTypeAnnotation(buf, typeAnnotation, encoders)); 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; 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 a3c2160b438f3..91b3114d11bce 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 @@ -32,7 +32,7 @@ import java.util.Map; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.util.GuardedAnnotationAccess; +import com.oracle.svm.util.GuardedAnnotationAccess; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; 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 b7253aef0c8c4..e8a44343fea84 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java @@ -40,6 +40,7 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.annotation.AnnotationValue; import com.oracle.svm.reflect.hosted.ReflectionMetadataEncoderImpl; import sun.reflect.annotation.AnnotationParser; @@ -49,8 +50,9 @@ /** * Substitutions in this class are required to adapt the JDK encoding for annotations to our - * modified version of it. See {@link ReflectionMetadataEncoderImpl#encodeAnnotations(Annotation[])} - * for a description of the changes and the rationale behind them. + * modified version of it. See + * {@link ReflectionMetadataEncoderImpl#encodeAnnotations(AnnotationValue[])} for a description of + * the changes and the rationale behind them. */ @TargetClass(AnnotationParser.class) public final class Target_sun_reflect_annotation_AnnotationParser { diff --git a/substratevm/src/com.oracle.svm.test/.checkstyle_checks.xml b/substratevm/src/com.oracle.svm.test/.checkstyle_checks.xml index 6500f2dc06615..7523497dbf427 100644 --- a/substratevm/src/com.oracle.svm.test/.checkstyle_checks.xml +++ b/substratevm/src/com.oracle.svm.test/.checkstyle_checks.xml @@ -14,7 +14,7 @@ - + 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 a02475447a06b..d42f170874267 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 @@ -65,7 +65,7 @@ import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.ConfigurationCondition; -import org.graalvm.util.DirectAnnotationAccess; +import com.oracle.svm.util.DirectAnnotationAccess; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/AnnotationExtracter.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/AnnotationExtracter.java new file mode 100644 index 0000000000000..cc14779676fee --- /dev/null +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/AnnotationExtracter.java @@ -0,0 +1,38 @@ +/* + * 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.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +@Platforms(Platform.HOSTED_ONLY.class) +public interface AnnotationExtracter { + boolean isAnnotationPresent(AnnotatedElement element, Class annotationType); + + T getAnnotation(AnnotatedElement element, Class annotationType, boolean declaredOnly); +} diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/AnnotationWrapper.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/AnnotationWrapper.java new file mode 100644 index 0000000000000..06b3a7c607cda --- /dev/null +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/AnnotationWrapper.java @@ -0,0 +1,151 @@ +/* + * 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.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public interface AnnotationWrapper extends AnnotatedElement { + AnnotatedElement getAnnotationRoot(); + + default AnnotatedElement getSecondaryAnnotationRoot() { + return null; + } + + default Annotation[] getInjectedAnnotations() { + return null; + } + + default Class[] getIgnoredAnnotations() { + return null; + } + + @Override + default boolean isAnnotationPresent(Class annotationClass) { + if (getIgnoredAnnotations() != null) { + for (Class ignoredAnnotation : getIgnoredAnnotations()) { + if (ignoredAnnotation == annotationClass) { + return false; + } + } + } + if (getInjectedAnnotations() != null) { + for (Annotation injectedAnnotation : getInjectedAnnotations()) { + if (injectedAnnotation.annotationType() == annotationClass) { + return true; + } + } + } + if (getAnnotationRoot() != null) { + if (GuardedAnnotationAccess.isAnnotationPresent(getAnnotationRoot(), annotationClass)) { + return true; + } + } + if (getSecondaryAnnotationRoot() != null) { + return GuardedAnnotationAccess.isAnnotationPresent(getSecondaryAnnotationRoot(), annotationClass); + } + return false; + } + + @Override + default Annotation[] getAnnotations() { + return getAnnotations(this, false); + } + + @Override + default Annotation[] getDeclaredAnnotations() { + return getAnnotations(this, true); + } + + private static Annotation[] getAnnotations(AnnotationWrapper element, boolean declaredOnly) { + List annotations = new ArrayList<>(); + List> ignoredAnnotations = element.getIgnoredAnnotations() == null ? Collections.emptyList() : Arrays.asList(element.getIgnoredAnnotations()); + if (element.getInjectedAnnotations() != null) { + annotations.addAll(Arrays.asList(element.getInjectedAnnotations())); + } + if (element.getAnnotationRoot() != null) { + Annotation[] rootAnnotations = declaredOnly ? GuardedAnnotationAccess.getDeclaredAnnotations(element.getAnnotationRoot()) + : GuardedAnnotationAccess.getAnnotations(element.getAnnotationRoot()); + for (Annotation rootAnnotation : rootAnnotations) { + if (!ignoredAnnotations.contains(rootAnnotation.annotationType())) { + annotations.add(rootAnnotation); + } + } + } + if (element.getSecondaryAnnotationRoot() != null) { + Annotation[] secondaryRootAnnotations = declaredOnly ? GuardedAnnotationAccess.getDeclaredAnnotations(element.getSecondaryAnnotationRoot()) + : GuardedAnnotationAccess.getAnnotations(element.getSecondaryAnnotationRoot()); + for (Annotation secondaryRootAnnotation : secondaryRootAnnotations) { + if (!ignoredAnnotations.contains(secondaryRootAnnotation.annotationType())) { + annotations.add(secondaryRootAnnotation); + } + } + } + return annotations.toArray(new Annotation[0]); + } + + @Override + default T getAnnotation(Class annotationClass) { + return getAnnotation(this, annotationClass, false); + } + + @Override + default T getDeclaredAnnotation(Class annotationClass) { + return getAnnotation(this, annotationClass, true); + } + + private static T getAnnotation(AnnotationWrapper element, Class annotationClass, boolean declaredOnly) { + if (element.getIgnoredAnnotations() != null) { + for (Class ignoredAnnotation : element.getIgnoredAnnotations()) { + if (ignoredAnnotation == annotationClass) { + return null; + } + } + } + if (element.getInjectedAnnotations() != null) { + for (Annotation injectedAnnotation : element.getInjectedAnnotations()) { + if (injectedAnnotation.annotationType() == annotationClass) { + return annotationClass.cast(injectedAnnotation); + } + } + } + if (element.getAnnotationRoot() != null) { + T rootAnnotation = declaredOnly ? GuardedAnnotationAccess.getDeclaredAnnotation(element.getAnnotationRoot(), annotationClass) + : GuardedAnnotationAccess.getAnnotation(element.getAnnotationRoot(), annotationClass); + if (rootAnnotation != null) { + return rootAnnotation; + } + } + if (element.getSecondaryAnnotationRoot() != null) { + return declaredOnly ? GuardedAnnotationAccess.getDeclaredAnnotation(element.getSecondaryAnnotationRoot(), annotationClass) + : GuardedAnnotationAccess.getAnnotation(element.getSecondaryAnnotationRoot(), annotationClass); + } + return null; + } +} diff --git a/compiler/src/org.graalvm.util/src/org/graalvm/util/DirectAnnotationAccess.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/DirectAnnotationAccess.java similarity index 98% rename from compiler/src/org.graalvm.util/src/org/graalvm/util/DirectAnnotationAccess.java rename to substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/DirectAnnotationAccess.java index aa265db511f73..4a4a74dafcc40 100644 --- a/compiler/src/org.graalvm.util/src/org/graalvm/util/DirectAnnotationAccess.java +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/DirectAnnotationAccess.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.util; +package com.oracle.svm.util; //Checkstyle: allow reflection import java.lang.annotation.Annotation; diff --git a/compiler/src/org.graalvm.util/src/org/graalvm/util/GuardedAnnotationAccess.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GuardedAnnotationAccess.java similarity index 71% rename from compiler/src/org.graalvm.util/src/org/graalvm/util/GuardedAnnotationAccess.java rename to substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GuardedAnnotationAccess.java index 5244089a91410..375a179ac345a 100644 --- a/compiler/src/org.graalvm.util/src/org/graalvm/util/GuardedAnnotationAccess.java +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GuardedAnnotationAccess.java @@ -22,11 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.util; +package com.oracle.svm.util; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.clinit.ClassInitializationTracking; + /** * Wrapper class for annotation access that defends against * https://bugs.openjdk.java.net/browse/JDK-7183985: when an annotation declares a Class array @@ -44,12 +47,38 @@ public final class GuardedAnnotationAccess { public static boolean isAnnotationPresent(AnnotatedElement element, Class annotationClass) { - return getAnnotation(element, annotationClass) != null; + if (ClassInitializationTracking.IS_IMAGE_BUILD_TIME && ImageSingletons.contains(AnnotationExtracter.class)) { + return isAnnotationPresent(ImageSingletons.lookup(AnnotationExtracter.class), element, annotationClass); + } else { + return element.isAnnotationPresent(annotationClass); + } } - public static T getAnnotation(AnnotatedElement element, Class annotationType) { + public static boolean isAnnotationPresent(AnnotationExtracter extracter, AnnotatedElement element, Class annotationClass) { try { + return extracter.isAnnotationPresent(element, annotationClass); + } catch (ArrayStoreException | LinkageError e) { + /* + * Returning null essentially means that the element doesn't declare the annotationType, + * but we cannot know that since the annotation parsing failed. However, this allows us + * to defend against crashing the image builder if the above JDK bug is encountered in + * user code or if the user code references types missing from the classpath. + */ + return false; + } + } + + public static T getAnnotation(AnnotatedElement element, Class annotationType) { + if (ClassInitializationTracking.IS_IMAGE_BUILD_TIME && ImageSingletons.contains(AnnotationExtracter.class)) { + return getAnnotation(ImageSingletons.lookup(AnnotationExtracter.class), element, annotationType); + } else { return element.getAnnotation(annotationType); + } + } + + public static T getAnnotation(AnnotationExtracter extracter, AnnotatedElement element, Class annotationType) { + try { + return extracter.getAnnotation(element, annotationType, false); } catch (ArrayStoreException | LinkageError e) { /* * Returning null essentially means that the element doesn't declare the annotationType, @@ -77,8 +106,16 @@ public static Annotation[] getAnnotations(AnnotatedElement element) { } public static T getDeclaredAnnotation(AnnotatedElement element, Class annotationType) { - try { + if (ClassInitializationTracking.IS_IMAGE_BUILD_TIME && ImageSingletons.contains(AnnotationExtracter.class)) { + return getDeclaredAnnotation(ImageSingletons.lookup(AnnotationExtracter.class), element, annotationType); + } else { return element.getDeclaredAnnotation(annotationType); + } + } + + public static T getDeclaredAnnotation(AnnotationExtracter extracter, AnnotatedElement element, Class annotationType) { + try { + return extracter.getAnnotation(element, annotationType, true); } catch (ArrayStoreException | LinkageError e) { /* * Returning null essentially means that the element doesn't declare the annotationType, diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java index 5bb73141430ee..fc5e7b5cea1d4 100644 --- a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java @@ -54,6 +54,17 @@ private static void openModule(Class declaringClass) { ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ReflectionUtil.class, declaringClass); } + public static Class lookupClass(boolean optional, String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException ex) { + if (optional) { + return null; + } + throw new ReflectionUtilError(ex); + } + } + public static Method lookupMethod(Class declaringClass, String methodName, Class... parameterTypes) { return lookupMethod(false, declaringClass, methodName, parameterTypes); } @@ -102,6 +113,8 @@ public static Field lookupField(Class declaringClass, String fieldName) { return lookupField(false, declaringClass, fieldName); } + private static final Method fieldGetDeclaredFields0 = ReflectionUtil.lookupMethod(Class.class, "getDeclaredFields0", boolean.class); + public static Field lookupField(boolean optional, Class declaringClass, String fieldName) { try { Field result = declaringClass.getDeclaredField(fieldName); @@ -109,6 +122,19 @@ public static Field lookupField(boolean optional, Class declaringClass, Strin result.setAccessible(true); return result; } catch (ReflectiveOperationException ex) { + /* Try to get hidden field */ + try { + Field[] allFields = (Field[]) fieldGetDeclaredFields0.invoke(declaringClass, false); + for (Field field : allFields) { + if (field.getName().equals(fieldName)) { + openModule(declaringClass); + field.setAccessible(true); + return field; + } + } + } catch (ReflectiveOperationException e) { + // ignore + } if (optional) { return null; }