From 5ff3f1a778dc5a7a94ffc794fc4a3621c7f299bf Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Fri, 16 Sep 2022 17:27:53 +0200 Subject: [PATCH] Introduce methods to query annotation information from with Features. --- .../org.graalvm.nativeimage/snapshot.sigtest | 13 ++ .../graalvm/nativeimage/AnnotationAccess.java | 118 ++++++++++++++++++ ...eBuildtimeCodeAnnotationAccessSupport.java | 54 ++++++++ .../hosted/annotation/AnnotationSupport.java | 33 ++++- 4 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/AnnotationAccess.java create mode 100644 sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ImageBuildtimeCodeAnnotationAccessSupport.java diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest index db5dc36fdd3d..df65580419d0 100644 --- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest +++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest @@ -119,11 +119,24 @@ CLSS public abstract interface java.util.function.BooleanSupplier anno 0 java.lang.FunctionalInterface() meth public abstract boolean getAsBoolean() +CLSS public final org.graalvm.nativeimage.AnnotationAccess +meth public static <%0 extends java.lang.annotation.Annotation> {%%0} getAnnotation(java.lang.reflect.AnnotatedElement,java.lang.Class<{%%0}>) +meth public static boolean isAnnotationPresent(java.lang.reflect.AnnotatedElement,java.lang.Class) +meth public static java.lang.Class[] getAnnotationTypes(java.lang.reflect.AnnotatedElement) +supr java.lang.Object + CLSS public final org.graalvm.nativeimage.CurrentIsolate meth public static org.graalvm.nativeimage.Isolate getIsolate() meth public static org.graalvm.nativeimage.IsolateThread getCurrentThread() supr java.lang.Object +CLSS public org.graalvm.nativeimage.AnnotationAccess +cons public init() +meth public static <%0 extends java.lang.annotation.Annotation> {%%0} getAnnotation(java.lang.reflect.AnnotatedElement,java.lang.Class<{%%0}>) +meth public static boolean isAnnotationPresent(java.lang.reflect.AnnotatedElement,java.lang.Class) +meth public static java.lang.Class[] getAnnotationTypes(java.lang.reflect.AnnotatedElement) +supr java.lang.Object + CLSS public final org.graalvm.nativeimage.ImageInfo fld public final static java.lang.String PROPERTY_IMAGE_CODE_KEY = "org.graalvm.nativeimage.imagecode" fld public final static java.lang.String PROPERTY_IMAGE_CODE_VALUE_BUILDTIME = "buildtime" diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/AnnotationAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/AnnotationAccess.java new file mode 100644 index 000000000000..43e3a6d34422 --- /dev/null +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/AnnotationAccess.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.nativeimage; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Arrays; + +import org.graalvm.nativeimage.impl.ImageBuildtimeCodeAnnotationAccessSupport; + +/** + * This class provides methods to query annotation information on {@link AnnotatedElement}s while + * trying to prevent, at image build time, side-effecting changes that impact the analysis results. + * + * The getAnnotation implementation in the JDK for Class, Field, and Method initializes the classes + * of all annotations present on that element, not just the class of the queried annotation. This + * leads to problems when not all annotation classes are present on the classpath/modulepath: + * querying an annotation whose class is present can fail with an exception because not all + * annotation classes are present. When this class's methods are called at image build time, then + * the minimal amount of class initialization is done: {@link #getAnnotation} only initializes the + * queried annotation class (when the annotation is actually present and therefore instantiated); + * {@link #isAnnotationPresent} and {@link #getAnnotationTypes} do not initialize any classes. + * + * When methods of this class are called not at image build time, i.e., at image run time or during + * the execution of a Java application not involving native image, then the JDK method to query the + * annotation is invoked. In these cases, there is no difference to the class initialization + * behavior of the JDK. + * + * Note that there is intentionally no `getAnnotations` method to query all annotations: all + * annotation classes must be initialized anyways by this method, so the JDK method can be invoke + * directly. In the image generator it should be generally avoided to use `getAnnotations`. + * + * @since 22.3 + */ +public final class AnnotationAccess { + + /** + * Implementation of {@link AnnotatedElement#isAnnotationPresent(Class)}. + * + * @since 22.3 + */ + public static boolean isAnnotationPresent(AnnotatedElement element, Class annotationClass) { + if (ImageInfo.inImageBuildtimeCode()) { + return ImageSingletons.lookup(ImageBuildtimeCodeAnnotationAccessSupport.class).isAnnotationPresent(element, annotationClass); + } else { + return element.isAnnotationPresent(annotationClass); + } + } + + /** + * Implementation of {@link AnnotatedElement#getAnnotation(Class)} . + * + * @since 22.3 + */ + @SuppressWarnings("unchecked") + public static T getAnnotation(AnnotatedElement element, Class annotationType) { + if (ImageInfo.inImageBuildtimeCode()) { + return (T) ImageSingletons.lookup(ImageBuildtimeCodeAnnotationAccessSupport.class).getAnnotation(element, annotationType); + } else { + return element.getAnnotation(annotationType); + } + } + + /** + * Implementation for retrieving all {@link Annotation#annotationType()}s for a {@code element}. + * + * @since 22.3 + */ + @SuppressWarnings("unchecked") + public static Class[] getAnnotationTypes(AnnotatedElement element) { + if (ImageInfo.inImageBuildtimeCode()) { + return ImageSingletons.lookup(ImageBuildtimeCodeAnnotationAccessSupport.class).getAnnotationTypes(element); + } else { + return Arrays.stream(element.getAnnotations()).map(Annotation::annotationType).toArray(Class[]::new); + } + } + + private AnnotationAccess() { + } +} diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ImageBuildtimeCodeAnnotationAccessSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ImageBuildtimeCodeAnnotationAccessSupport.java new file mode 100644 index 000000000000..6a5cca760324 --- /dev/null +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ImageBuildtimeCodeAnnotationAccessSupport.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.nativeimage.impl; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; + +public interface ImageBuildtimeCodeAnnotationAccessSupport { + // needed as Annotation Access-specific ImageSingletons key + + boolean isAnnotationPresent(AnnotatedElement element, Class annotationClass); + + Annotation getAnnotation(AnnotatedElement element, Class annotationType); + + Class[] getAnnotationTypes(AnnotatedElement element); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java index 1968b516e84b..046d2dd96404 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.hosted.annotation; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; @@ -60,20 +62,24 @@ import org.graalvm.compiler.nodes.java.LoadFieldNode; import org.graalvm.compiler.replacements.nodes.MacroNode.MacroParams; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.ImageInfo; +import org.graalvm.nativeimage.impl.ImageBuildtimeCodeAnnotationAccessSupport; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.core.SubstrateAnnotationInvocationHandler; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.jdk.SubstrateObjectCloneWithExceptionNode; import com.oracle.svm.core.jdk.AnnotationSupportConfig; import com.oracle.svm.core.meta.SubstrateObjectConstant; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.analysis.NativeImagePointsToAnalysis; import com.oracle.svm.hosted.phases.HostedGraphKit; +import com.oracle.svm.util.GuardedAnnotationAccess; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; @@ -734,3 +740,28 @@ private static Object replacementComputer(Object original) { return Proxy.newProxyInstance(original.getClass().getClassLoader(), extendedInterfaces, new SubstrateAnnotationInvocationHandler(Proxy.getInvocationHandler(original))); } } + +@AutomaticallyRegisteredImageSingleton(ImageBuildtimeCodeAnnotationAccessSupport.class) +class ImageBuildtimeCodeAnnotationAccessSupportSingleton implements ImageBuildtimeCodeAnnotationAccessSupport { + + @Override + public boolean isAnnotationPresent(AnnotatedElement element, Class annotationClass) { + assert ImageInfo.inImageBuildtimeCode() : "This method should only be called from within image buildtime code"; + + return GuardedAnnotationAccess.isAnnotationPresent(element, annotationClass); + } + + @Override + public Annotation getAnnotation(AnnotatedElement element, Class annotationType) { + assert ImageInfo.inImageBuildtimeCode() : "This method should only be called from within image buildtime code"; + + return GuardedAnnotationAccess.getAnnotation(element, annotationType); + } + + @Override + public Class[] getAnnotationTypes(AnnotatedElement element) { + assert ImageInfo.inImageBuildtimeCode() : "This method should only be called from within image buildtime code"; + + return GuardedAnnotationAccess.getAnnotationTypes(element); + } +}