From 155ba6e7f089ed53d95beaf61c87db296cee4de5 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Wed, 12 Apr 2023 16:08:39 -0700 Subject: [PATCH] Remove obsolete annotation support --- .../SubstrateAnnotationInvocationHandler.java | 57 -- .../svm/hosted/NativeImageGenerator.java | 9 +- .../hosted/annotation/AnnotationFeature.java | 86 ++ .../AnnotationSubstitutionField.java | 161 ---- .../AnnotationSubstitutionMethod.java | 40 - .../AnnotationSubstitutionType.java | 74 -- .../hosted/annotation/AnnotationSupport.java | 735 ------------------ .../annotation/AnnotationTypeFeature.java | 56 -- ...stantAnnotationMarkerSubstitutionType.java | 275 ------- .../hosted/annotation/CustomSubstitution.java | 129 --- .../annotation/CustomSubstitutionField.java | 79 -- .../annotation/CustomSubstitutionType.java | 294 ------- .../image/NativeImageDebugInfoProvider.java | 3 - .../AnnotationSubstitutionProcessor.java | 3 - 14 files changed, 89 insertions(+), 1912 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateAnnotationInvocationHandler.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationFeature.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionField.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionMethod.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionType.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitution.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionField.java delete mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateAnnotationInvocationHandler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateAnnotationInvocationHandler.java deleted file mode 100644 index 704e97144019..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateAnnotationInvocationHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2019, 2019, 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; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; - -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - -/** - * Wrapper for the {@link InvocationHandler} used by the JDK for annotations. During image - * generation, it makes the annotation properties available by delegating to the JDK invocation - * handler. At run time, the properties are stored directly in the annotation objects. See the - * hosted class AnnotationSupport for details. - */ -public final class SubstrateAnnotationInvocationHandler implements InvocationHandler { - - @Platforms(Platform.HOSTED_ONLY.class) // - private final InvocationHandler hostedInvocationHandler; - - @Platforms(Platform.HOSTED_ONLY.class) - public SubstrateAnnotationInvocationHandler(InvocationHandler hostedInvocationHandler) { - this.hostedInvocationHandler = hostedInvocationHandler; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (SubstrateUtil.HOSTED) { - return hostedInvocationHandler.invoke(proxy, method, args); - } else { - throw new IllegalArgumentException("InvocationHandler for annotations must not be used. Method: " + method.toString()); - } - } -} 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 d836d1ef5423..488d99031163 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 @@ -245,7 +245,6 @@ import com.oracle.svm.hosted.analysis.NativeImageReachabilityAnalysisEngine; import com.oracle.svm.hosted.analysis.SVMAnalysisMetaAccess; import com.oracle.svm.hosted.analysis.SubstrateUnsupportedFeatures; -import com.oracle.svm.hosted.annotation.AnnotationSupport; import com.oracle.svm.hosted.annotation.SubstrateAnnotationExtractor; import com.oracle.svm.hosted.c.CAnnotationProcessorCache; import com.oracle.svm.hosted.c.CConstantValueSupportImpl; @@ -980,8 +979,7 @@ public static AnalysisUniverse createAnalysisUniverse(OptionValues options, Targ SnippetReflectionProvider originalSnippetReflection, AnnotationSubstitutionProcessor annotationSubstitutions, SubstitutionProcessor cEnumProcessor, ClassInitializationSupport classInitializationSupport, List additionalSubstitutions) { UnsafeAutomaticSubstitutionProcessor automaticSubstitutions = createAutomaticUnsafeSubstitutions(options, originalSnippetReflection, annotationSubstitutions); - SubstitutionProcessor aSubstitutions = createAnalysisSubstitutionProcessor(originalMetaAccess, originalSnippetReflection, cEnumProcessor, automaticSubstitutions, - annotationSubstitutions, additionalSubstitutions); + SubstitutionProcessor aSubstitutions = createAnalysisSubstitutionProcessor(cEnumProcessor, automaticSubstitutions, annotationSubstitutions, additionalSubstitutions); SVMHost hostVM = HostedConfiguration.instance().createHostVM(options, loader.getClassLoader(), classInitializationSupport, automaticSubstitutions, loader.platform); @@ -1012,13 +1010,12 @@ public static UnsafeAutomaticSubstitutionProcessor createAutomaticUnsafeSubstitu return new UnsafeAutomaticSubstitutionProcessor(options, annotationSubstitutions, originalSnippetReflection); } - public static SubstitutionProcessor createAnalysisSubstitutionProcessor(MetaAccessProvider originalMetaAccess, SnippetReflectionProvider originalSnippetReflection, + public static SubstitutionProcessor createAnalysisSubstitutionProcessor( SubstitutionProcessor cEnumProcessor, SubstitutionProcessor automaticSubstitutions, SubstitutionProcessor annotationSubstitutions, List additionalSubstitutionProcessors) { List allProcessors = new ArrayList<>(); SubstitutionProcessor cFunctionSubstitutions = new CFunctionSubstitutionProcessor(); - allProcessors.addAll(Arrays.asList(new AnnotationSupport(originalMetaAccess, originalSnippetReflection), - annotationSubstitutions, cFunctionSubstitutions, automaticSubstitutions, cEnumProcessor)); + allProcessors.addAll(Arrays.asList(annotationSubstitutions, cFunctionSubstitutions, automaticSubstitutions, cEnumProcessor)); allProcessors.addAll(additionalSubstitutionProcessors); return SubstitutionProcessor.chainUpInOrder(allProcessors.toArray(new SubstitutionProcessor[0])); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationFeature.java new file mode 100644 index 000000000000..9bc751363555 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationFeature.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023, 2023, 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.Array; +import java.lang.reflect.Proxy; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.ConfigurationCondition; +import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; + +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.hosted.reflect.ReflectionDataBuilder; + +@AutomaticallyRegisteredFeature +public class AnnotationFeature implements InternalFeature { + + private RuntimeReflectionSupport runtimeReflectionSupport; + private final Set> processedTypes = ConcurrentHashMap.newKeySet(); + + @Override + public void duringSetup(DuringSetupAccess access) { + runtimeReflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class); + access.registerObjectReplacer(this::registerDeclaredMethods); + } + + /** + * For annotations that are materialized at image run time, all necessary methods are registered + * for reflection in {@link ReflectionDataBuilder#registerTypesForAnnotation}. But if an + * annotation type is only used by an annotation that is already in the image heap, then we need + * to also register its methods for reflection. This is done here by inspecting every image heap + * object and checking if it is an annotation that was materialized by the JDK, i.e., implements + * the {@link Annotation} interface and is a {@link Proxy}. + */ + private Object registerDeclaredMethods(Object obj) { + if (obj instanceof Annotation annotation && Proxy.isProxyClass(annotation.getClass())) { + Class annotationType = annotation.annotationType(); + if (processedTypes.add(annotationType)) { + runtimeReflectionSupport.registerAllDeclaredMethodsQuery(ConfigurationCondition.alwaysTrue(), false, annotationType); + } + } + return obj; + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + access.registerSubtypeReachabilityHandler(this::registerArrayClass, Annotation.class); + } + + /* + * The JDK implementation of repeatable annotations always instantiates an array of a requested + * annotation. We need to mark arrays of all reachable annotations as in heap. + */ + private void registerArrayClass(DuringAnalysisAccess access, Class subclass) { + if (subclass.isAnnotation()) { + Class arrayClass = Array.newInstance(subclass, 0).getClass(); + access.registerAsInHeap(arrayClass); + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionField.java deleted file mode 100644 index be99abd583de..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionField.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2014, 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.hosted.annotation; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Proxy; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; - -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; -import com.oracle.svm.util.ReflectionUtil; - -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaType; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import sun.reflect.annotation.TypeNotPresentExceptionProxy; - -public class AnnotationSubstitutionField extends CustomSubstitutionField { - - private final ResolvedJavaMethod accessorMethod; - private final Map valueCache; - private final SnippetReflectionProvider snippetReflection; - private final MetaAccessProvider metaAccess; - - public AnnotationSubstitutionField(AnnotationSubstitutionType declaringClass, ResolvedJavaMethod accessorMethod, - SnippetReflectionProvider snippetReflection, - MetaAccessProvider metaAccess) { - super(declaringClass); - this.accessorMethod = accessorMethod; - this.snippetReflection = snippetReflection; - this.valueCache = Collections.synchronizedMap(new HashMap<>()); - this.metaAccess = metaAccess; - } - - @Override - public String getName() { - return accessorMethod.getName(); - } - - @Override - public JavaType getType() { - /* - * The type of an annotation element can be one of: primitive, String, Class, an enum type, - * an annotation type, or an array type whose component type is one of the preceding types, - * according to https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6.1. - */ - JavaType actualType = accessorMethod.getSignature().getReturnType(accessorMethod.getDeclaringClass()); - if (AnnotationSupport.isClassType(actualType, metaAccess)) { - /* - * Annotation elements that have a Class type can reference classes that are missing at - * runtime. We declare the corresponding fields with the Object type to be able to store - * a TypeNotPresentExceptionProxy which we then use to generate the - * TypeNotPresentException at runtime (see below). - */ - return metaAccess.lookupJavaType(Object.class); - } - return actualType; - } - - @Override - public JavaConstant readValue(MetaAccessProvider suppliedMetaAccess, ClassInitializationSupport classInitializationSupport, JavaConstant receiver) { - JavaConstant result = valueCache.get(receiver); - if (result == null) { - Object annotationFieldValue; - /* - * Invoke the accessor method of the annotation object. Since array attributes return a - * different, newly allocated, array at every invocation, we cache the result value. - */ - try { - /* - * The code below assumes that the annotations have already been parsed and the - * result cached in the AnnotationInvocationHandler.memberValues field. The parsing - * is triggered, at the least, during object graph checking in - * Inflation.checkType(), or earlier when the type annotations are accessed for the - * first time, e.g., ImageClassLoader.includedInPlatform() due to the call to - * Class.getAnnotation(Platforms.class). - */ - Proxy proxy = snippetReflection.asObject(Proxy.class, receiver); - - /* - * Reflect on the proxy interface, i.e., the annotation class, instead of the - * generated proxy class to avoid module access issues with dynamically generated - * modules. The dynamically generated module that the generated proxies belong to, - * i.e., `jdk.proxy1`, cannot be open to all-unnamed-modules like we do with other - * modules. - */ - Class annotationInterface = AnnotationSupport.findAnnotationInterfaceTypeForMarkedAnnotationType(proxy.getClass()); - annotationFieldValue = ReflectionUtil.lookupMethod(annotationInterface, accessorMethod.getName()).invoke(proxy); - } catch (IllegalAccessException | IllegalArgumentException ex) { - throw VMError.shouldNotReachHere(ex); - } catch (InvocationTargetException ex) { - Throwable cause = ex.getCause(); - if (cause instanceof TypeNotPresentException) { - /* - * When an annotation has a Class parameter but is referencing a missing - * class a TypeNotPresentException is thrown. The TypeNotPresentException is - * usually created when the annotation is first parsed, i.e., one some other - * parameter is queried, and cached as an TypeNotPresentExceptionProxy. We catch - * and repackage it here, then rely on the runtime mechanism to unpack and - * rethrow it. - */ - TypeNotPresentException tnpe = (TypeNotPresentException) cause; - annotationFieldValue = new TypeNotPresentExceptionProxy(tnpe.typeName(), new NoClassDefFoundError(tnpe.typeName())); - } else { - throw VMError.shouldNotReachHere(ex); - } - } - - result = snippetReflection.forBoxed(getJavaKind(), annotationFieldValue); - - valueCache.put(receiver, result); - } - return result; - } - - @Override - public boolean isValueAvailableBeforeAnalysis() { - return true; - } - - @Override - public boolean injectFinalForRuntimeCompilation() { - /* - * Value of annotations never change at run time, so we can treat the field as final for - * runtime compilations. - */ - return true; - } - - @Override - public String toString() { - return "AnnotationField<" + format("%h.%n") + ">"; - } -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionMethod.java deleted file mode 100644 index d7f0467a8257..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionMethod.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2014, 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.hosted.annotation; - -import jdk.vm.ci.meta.ResolvedJavaMethod; - -public abstract class AnnotationSubstitutionMethod extends CustomSubstitutionMethod { - - public AnnotationSubstitutionMethod(ResolvedJavaMethod original) { - super(original); - } - - @Override - public String toString() { - return "AnnotationMethod<" + format("%h.%n") + " -> " + original + ">"; - } - -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionType.java deleted file mode 100644 index 8a78affe55ea..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSubstitutionType.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2014, 2023, 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.Proxy; -import java.util.Arrays; - -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaType; - -public class AnnotationSubstitutionType extends CustomSubstitutionType { - - private final String name; - private final MetaAccessProvider metaAccess; - private final ResolvedJavaType annotationInterfaceType; - - public AnnotationSubstitutionType(MetaAccessProvider metaAccess, ResolvedJavaType original) { - super(original); - this.metaAccess = metaAccess; - - assert original.getSuperclass().equals(metaAccess.lookupJavaType(Proxy.class)); - assert metaAccess.lookupJavaType(Annotation.class).isAssignableFrom(original); - - annotationInterfaceType = AnnotationSupport.findAnnotationInterfaceTypeForMarkedAnnotationType(original, metaAccess); - assert annotationInterfaceType.isAssignableFrom(original); - assert metaAccess.lookupJavaType(Annotation.class).isAssignableFrom(annotationInterfaceType); - - String n = annotationInterfaceType.getName(); - assert n.endsWith(";"); - name = n.substring(0, n.length() - 1) + "$$ProxyImpl;"; - } - - @Override - public ResolvedJavaType[] getInterfaces() { - /* Filter out the ConstantAnnotationMarker interface. */ - ResolvedJavaType[] interfaces = super.getInterfaces(); - return Arrays.stream(interfaces) - .filter((t) -> !AnnotationSupport.isAnnotationMarkerInterface(t, metaAccess)) - .toArray(ResolvedJavaType[]::new); - } - - @Override - public String getName() { - return name; - } - - @Override - public String toString() { - return "AnnotationType<" + toJavaName(true) + " -> " + original + ">"; - } -} 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 deleted file mode 100644 index 28bcd4c97c80..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java +++ /dev/null @@ -1,735 +0,0 @@ -/* - * Copyright (c) 2014, 2020, 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.InvocationHandler; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -import org.graalvm.collections.Pair; -import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; -import org.graalvm.compiler.core.common.type.StampFactory; -import org.graalvm.compiler.core.common.type.StampPair; -import org.graalvm.compiler.core.common.type.TypeReference; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.java.FrameStateBuilder; -import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; -import org.graalvm.compiler.nodes.ConstantNode; -import org.graalvm.compiler.nodes.DeoptimizeNode; -import org.graalvm.compiler.nodes.FixedNode; -import org.graalvm.compiler.nodes.LogicNode; -import org.graalvm.compiler.nodes.PiNode; -import org.graalvm.compiler.nodes.ReturnNode; -import org.graalvm.compiler.nodes.StateSplit; -import org.graalvm.compiler.nodes.StructuredGraph; -import org.graalvm.compiler.nodes.UnwindNode; -import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.calc.AddNode; -import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; -import org.graalvm.compiler.nodes.calc.ObjectEqualsNode; -import org.graalvm.compiler.nodes.calc.XorNode; -import org.graalvm.compiler.nodes.extended.BoxNode; -import org.graalvm.compiler.nodes.extended.BranchProbabilityNode; -import org.graalvm.compiler.nodes.java.ArrayLengthNode; -import org.graalvm.compiler.nodes.java.InstanceOfNode; -import org.graalvm.compiler.nodes.java.LoadFieldNode; -import org.graalvm.compiler.replacements.nodes.MacroNode.MacroParams; -import org.graalvm.compiler.serviceprovider.JavaVersionUtil; - -import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; -import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; -import com.oracle.graal.pointsto.meta.HostedProviders; -import com.oracle.svm.core.SubstrateAnnotationInvocationHandler; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; -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.util.VMError; -import com.oracle.svm.hosted.analysis.NativeImagePointsToAnalysis; -import com.oracle.svm.hosted.phases.HostedGraphKit; - -import jdk.vm.ci.meta.DeoptimizationAction; -import jdk.vm.ci.meta.DeoptimizationReason; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.JavaType; -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; -import sun.reflect.annotation.TypeNotPresentExceptionProxy; - -public class AnnotationSupport extends CustomSubstitution { - - /** - * The constant-annotation-marker interface is used to mark the ahead-of-time allocated - * annotation proxy objects. We re-use the {@link Override java.lang.Override} interface for - * this purpose. Although this may seem like a strange choice at first it is necessary to avoid - * the restrictions around the creation of proxy objects. We need an interface that can be used - * to mark existing proxy objects by extending the list of interfaces they implement. We do this - * in AnnotationObjectReplacer#replacementComputer(Object). We call - * {@link Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)} passing in the same - * class loader that loaded the class of the original proxy object, the extended interface list - * and the original proxy object invocation handler. If the original proxy object is a proxy to - * an annotation loaded by the boot class loader then the marker interface needs to be loaded by - * the boot class loader too. This is imposed by {@link Proxy} API. Therefore, the - * {@link Override} interface gives us access to an interface that is already loaded by the boot - * class loader. It also has the advantage that it declares SOURCE retention policy, so this - * interface should not be otherwise present in the bytecode and no other uses should interfere - * with our mechanism. - * - * Note: Ideally we would use a custom marker interface. However, this is impossible as of JDK9 - * since the set of boot modules is fixed at JDK build time and cannot be extended at runtime. - * - * This allows us to create an optimized type for the ahead-of-time allocated annotation proxy - * objects which removes the overhead of storing the annotation values in a HashMap. See - * {@link AnnotationSupport#getSubstitution(ResolvedJavaType)} for the logic where this - * substitution is implemented. This is possible since the ahead-of-time allocated annotation - * proxy objects are effectively constant. The constant-annotation-marker interface is removed - * before runtime. See {@link AnnotationSubstitutionType#getInterfaces()}. Therefore the - * annotation proxy objects only implement the annotation interface, together with - * {@link java.lang.reflect.Proxy} and {@link java.lang.annotation.Annotation}, as expected. - * - * The run-time allocated annotations use the default JDK implementation. - * - * The downside of having a separate (more efficient) implementation for ahead-of-time allocated - * annotations is that at run time there can be two proxy types for the same annotation type and - * an equality check between them would fail. - */ - public static final Class constantAnnotationMarkerInterface = java.lang.Override.class; - - private final SnippetReflectionProvider snippetReflection; - - private final ResolvedJavaType javaLangReflectProxy; - private final ResolvedJavaType constantAnnotationMarkerOriginalType; - - /** - * Because {@link #constantAnnotationMarkerInterface} is injected into - * {@link AnnotationSubstitutionType}s, a substitution type is needed to correct the behavior of - * calls to {@link ResolvedJavaType#isAssignableFrom(ResolvedJavaType)}. Otherwise, all - * AnnotationSubstitutionTypes will be considered assignable from the - * constantAnnotationMarkerInterface. {@link ConstantAnnotationMarkerSubstitutionType} catches - * and corrects this issue. - */ - private final ResolvedJavaType constantAnnotationMarkerSubstitutionType; - - @SuppressWarnings("this-escape") - public AnnotationSupport(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection) { - super(metaAccess); - this.snippetReflection = snippetReflection; - - javaLangReflectProxy = metaAccess.lookupJavaType(java.lang.reflect.Proxy.class); - constantAnnotationMarkerOriginalType = metaAccess.lookupJavaType(constantAnnotationMarkerInterface); - constantAnnotationMarkerSubstitutionType = new ConstantAnnotationMarkerSubstitutionType(constantAnnotationMarkerOriginalType, this); - - AnnotationSupportConfig.initialize(); - } - - private boolean isConstantAnnotationType(ResolvedJavaType type) { - /* - * Check if the type implements all of: Annotation, Proxy and the constant-annotation-marker - * interface. If so, then it is the type of an annotation proxy object encountered during - * heap scanning. Only those types are substituted with a more efficient annotation proxy - * type implementation. - * - * If a type implements only Annotation and Proxy but not the constant-annotation-marker - * interface then it is a proxy type registered via the dynamic proxy API. Such type is used - * to allocate annotation instances at run time and must not be replaced. - * - * If the type implements more than two interfaces then it could be a non-standard - * annotation implementations like - * com.sun.xml.internal.bind.v2.model.annotation.LocatableAnnotation, i.e., an annotation - * wrapper that also implements com.sun.xml.internal.bind.v2.model.annotation.Locatable. We - * don't optimize these types; the implementation would be too complicated for a marginal - * benefit. Therefore, the type must implement two and only two interfaces: the annotation - * interface and the marker interface. - */ - return type.getInterfaces().length == 2 && - isAnnotation(type.getInterfaces()[0]) && - type.getInterfaces()[1].equals(constantAnnotationMarkerOriginalType) && - javaLangReflectProxy.isAssignableFrom(type); - } - - /* Value copied from java.lang.Class. */ - private static final int ANNOTATION = 0x00002000; - - /* Method copied from java.lang.Class. */ - private static boolean isAnnotation(ResolvedJavaType type) { - return (type.getModifiers() & ANNOTATION) != 0; - } - - @Override - public ResolvedJavaType lookup(ResolvedJavaType type) { - if (isConstantAnnotationType(type)) { - return getSubstitution(type); - } else if (type.equals(constantAnnotationMarkerOriginalType)) { - return constantAnnotationMarkerSubstitutionType; - } - return type; - } - - @Override - public ResolvedJavaType resolve(ResolvedJavaType type) { - if (type instanceof AnnotationSubstitutionType) { - return ((AnnotationSubstitutionType) type).original; - } else if (type.equals(constantAnnotationMarkerSubstitutionType)) { - return constantAnnotationMarkerOriginalType; - } - return type; - } - - @Override - public ResolvedJavaField lookup(ResolvedJavaField field) { - if (isConstantAnnotationType(field.getDeclaringClass())) { - throw new UnsupportedFeatureException("Field of annotation proxy is not accessible: " + field); - } - return field; - } - - @Override - public ResolvedJavaMethod lookup(ResolvedJavaMethod method) { - if (isConstantAnnotationType(method.getDeclaringClass()) && !method.getName().equals("proxyClassLookup")) { - AnnotationSubstitutionType declaringClass = getSubstitution(method.getDeclaringClass()); - AnnotationSubstitutionMethod result = declaringClass.getSubstitutionMethod(method); - assert result != null && result.original.equals(method); - return result; - } - return method; - } - - @Override - public ResolvedJavaMethod resolve(ResolvedJavaMethod method) { - if (method instanceof AnnotationSubstitutionMethod) { - return ((AnnotationSubstitutionMethod) method).original; - } - return method; - } - - private synchronized AnnotationSubstitutionType getSubstitution(ResolvedJavaType type) { - AnnotationSubstitutionType result = getSubstitutionType(type); - if (result == null) { - result = new AnnotationSubstitutionType(metaAccess, type); - - for (ResolvedJavaMethod originalMethod : type.getDeclaredMethods()) { - AnnotationSubstitutionMethod substitutionMethod; - String methodName = canonicalMethodName(originalMethod); - /* Our annotation implementation doesn't use the proxyClassLookup method. */ - if (methodName.equals("proxyClassLookup")) { - continue; - } - if (methodName.equals("equals")) { - substitutionMethod = new AnnotationEqualsMethod(originalMethod); - } else if (methodName.equals("hashCode")) { - substitutionMethod = new AnnotationHashCodeMethod(originalMethod); - } else if (methodName.equals("toString")) { - substitutionMethod = new AnnotationToStringMethod(originalMethod); - } else if (methodName.equals("annotationType")) { - substitutionMethod = new AnnotationAnnotationTypeMethod(originalMethod); - } else { - substitutionMethod = new AnnotationAccessorMethod(originalMethod); - result.addSubstitutionField(new AnnotationSubstitutionField(result, originalMethod, snippetReflection, metaAccess)); - } - result.addSubstitutionMethod(originalMethod, substitutionMethod); - } - - for (ResolvedJavaMethod originalMethod : type.getDeclaredConstructors()) { - AnnotationSubstitutionMethod substitutionMethod = new AnnotationConstructorMethod(originalMethod); - result.addSubstitutionMethod(originalMethod, substitutionMethod); - } - - typeSubstitutions.put(type, result); - } - return result; - } - - static class AnnotationConstructorMethod extends AnnotationSubstitutionMethod { - AnnotationConstructorMethod(ResolvedJavaMethod original) { - super(original); - } - - @Override - public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) { - HostedGraphKit kit = new HostedGraphKit(debug, providers, method, purpose); - StructuredGraph graph = kit.getGraph(); - graph.addAfterFixed(graph.start(), graph.add(new DeoptimizeNode(DeoptimizationAction.None, DeoptimizationReason.UnreachedCode))); - return graph; - } - } - - /* Used to check the type of fields that need special guarding against missing types. */ - static boolean isClassType(JavaType type, MetaAccessProvider metaAccess) { - return type.getJavaKind() == JavaKind.Object && - (type.equals(metaAccess.lookupJavaType(Class.class)) || type.equals(metaAccess.lookupJavaType(Class[].class))); - } - - static class AnnotationAccessorMethod extends AnnotationSubstitutionMethod { - AnnotationAccessorMethod(ResolvedJavaMethod original) { - super(original); - } - - @Override - public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) { - ResolvedJavaType annotationType = method.getDeclaringClass(); - assert !Modifier.isStatic(method.getModifiers()) && method.getSignature().getParameterCount(false) == 0; - - HostedGraphKit kit = new HostedGraphKit(debug, providers, method, purpose); - StructuredGraph graph = kit.getGraph(); - FrameStateBuilder state = new FrameStateBuilder(null, method, graph); - state.initializeForMethodStart(null, true, providers.getGraphBuilderPlugins()); - - /* - * A random, but unique and consistent, number for every invoke. This is necessary - * because we, e.g., look up static analysis results by bci. - */ - int bci = 0; - graph.start().setStateAfter(state.create(bci++, graph.start())); - - ValueNode receiver = state.loadLocal(0, JavaKind.Object); - ResolvedJavaField field = findField(annotationType, canonicalMethodName(method)); - - ValueNode loadField = kit.append(LoadFieldNode.create(null, receiver, field)); - - ResolvedJavaType resultType = method.getSignature().getReturnType(null).resolve(null); - - loadField = unpackAttribute(providers, kit, loadField, resultType); - - if (resultType.isArray()) { - loadField = kit.maybeCreateExplicitNullCheck(loadField); - - /* From the specification: Arrays with length > 0 need to be cloned. */ - ValueNode arrayLength = kit.append(new ArrayLengthNode(loadField)); - kit.startIf(graph.unique(new IntegerEqualsNode(arrayLength, ConstantNode.forInt(0, graph))), BranchProbabilityNode.NOT_LIKELY_PROFILE); - kit.elsePart(); - - ResolvedJavaMethod cloneMethod = kit.findMethod(Object.class, "clone"); - JavaType returnType = cloneMethod.getSignature().getReturnType(null); - StampPair returnStampPair = StampFactory.forDeclaredType(null, returnType, false); - - FixedNode cloned = kit.appendWithUnwind(new SubstrateObjectCloneWithExceptionNode(MacroParams.of(InvokeKind.Virtual, method, cloneMethod, bci++, returnStampPair, loadField))); - state.push(returnType.getJavaKind(), cloned); - ((StateSplit) cloned).setStateAfter(state.create(bci, (StateSplit) cloned)); - state.pop(returnType.getJavaKind()); - - ValueNode casted = kit.unique(new PiNode(cloned, resultType, false, false)); - kit.append(new ReturnNode(casted)); - kit.endIf(); - } - kit.append(new ReturnNode(loadField)); - - return kit.finalizeGraph(); - } - } - - static class AnnotationAnnotationTypeMethod extends AnnotationSubstitutionMethod { - AnnotationAnnotationTypeMethod(ResolvedJavaMethod original) { - super(original); - } - - @Override - public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) { - ResolvedJavaType annotationType = method.getDeclaringClass(); - ResolvedJavaType annotationInterfaceType = findAnnotationInterfaceType(annotationType); - JavaConstant returnValue = providers.getConstantReflection().asJavaClass(annotationInterfaceType); - - HostedGraphKit kit = new HostedGraphKit(debug, providers, method, purpose); - ValueNode returnConstant = kit.unique(ConstantNode.forConstant(returnValue, providers.getMetaAccess())); - kit.append(new ReturnNode(returnConstant)); - - return kit.finalizeGraph(); - } - } - - static class AnnotationEqualsMethod extends AnnotationSubstitutionMethod { - AnnotationEqualsMethod(ResolvedJavaMethod original) { - super(original); - } - - @Override - public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) { - assert !Modifier.isStatic(method.getModifiers()) && method.getSignature().getParameterCount(false) == 1; - ResolvedJavaType annotationType = method.getDeclaringClass(); - ResolvedJavaType annotationInterfaceType = findAnnotationInterfaceType(annotationType); - - HostedGraphKit kit = new HostedGraphKit(debug, providers, method, purpose); - StructuredGraph graph = kit.getGraph(); - FrameStateBuilder state = new FrameStateBuilder(null, method, graph); - state.initializeForMethodStart(null, true, providers.getGraphBuilderPlugins()); - - /* - * A random, but unique and consistent, number for every invoke. This is necessary - * because we, e.g., look up static analysis results by bci. - */ - int bci = 0; - graph.start().setStateAfter(state.create(bci++, graph.start())); - - ValueNode receiver = state.loadLocal(0, JavaKind.Object); - ValueNode other = state.loadLocal(1, JavaKind.Object); - ValueNode trueValue = ConstantNode.forBoolean(true, graph); - ValueNode falseValue = ConstantNode.forBoolean(false, graph); - - kit.startIf(graph.unique(new ObjectEqualsNode(receiver, other)), BranchProbabilityNode.LIKELY_PROFILE); - kit.thenPart(); - kit.append(new ReturnNode(trueValue)); - kit.endIf(); - - TypeReference otherTypeRef = TypeReference.createTrustedWithoutAssumptions(annotationInterfaceType); - kit.startIf(graph.unique(InstanceOfNode.create(otherTypeRef, other)), BranchProbabilityNode.NOT_LIKELY_PROFILE); - kit.elsePart(); - kit.append(new ReturnNode(falseValue)); - kit.endIf(); - - other = kit.append(new PiNode(other, StampFactory.objectNonNull(otherTypeRef))); - - for (Pair attributePair : findAttributes(annotationType)) { - String attribute = attributePair.getLeft(); - ResolvedJavaField ourField = findField(annotationType, attribute); - ResolvedJavaMethod otherMethod = findMethod(annotationInterfaceType, attribute); - ResolvedJavaType attributeType = attributePair.getRight(); - - /* - * Access other value. The other object can be any implementation of the annotation - * interface, so we need to invoke the accessor method. - */ - ValueNode otherAttribute = kit.createInvokeWithExceptionAndUnwind(otherMethod, InvokeKind.Interface, state, bci++, other); - - /* Access our value. We know that it is in a field. */ - ValueNode ourAttribute = kit.append(LoadFieldNode.create(null, receiver, ourField)); - - if (attributeType.isPrimitive()) { - /* - * Box primitive types. The equality on attributes is defined on boxed values - * (which matters, e.g., for floating point values), and it is easier to call - * equals() on the boxed type anyway (and rely on method inlining and boxing - * elimination to clean things up). - */ - ResolvedJavaType boxedAttributeType = providers.getMetaAccess().lookupJavaType(attributeType.getJavaKind().toBoxedJavaClass()); - ourAttribute = kit.append(BoxNode.create(ourAttribute, boxedAttributeType, attributeType.getJavaKind())); - otherAttribute = kit.append(BoxNode.create(otherAttribute, boxedAttributeType, attributeType.getJavaKind())); - } - - ourAttribute = unpackAttribute(providers, kit, ourAttribute, attributeType); - /* - * The otherAttribute doesn't need any unpacking as it is accessed by invoking the - * accessor method. If it is our special annotation implementation the accessor - * method already does the unpacking. If it is another implementation of the - * annotation interface no unpacking is necessary. - */ - - ValueNode attributeEqual; - if (attributeType.isArray()) { - /* Call the appropriate Arrays.equals() method for our attribute type. */ - ResolvedJavaMethod m = findMethod(providers.getMetaAccess().lookupJavaType(Arrays.class), "equals", attributeType, attributeType); - attributeEqual = kit.createInvokeWithExceptionAndUnwind(m, InvokeKind.Static, state, bci++, ourAttribute, otherAttribute); - } else { - /* Just call Object.equals(). Primitive values are already boxed. */ - ResolvedJavaMethod m = kit.findMethod(Object.class, "equals", Object.class); - ValueNode ourAttributeNonNull = kit.maybeCreateExplicitNullCheck(ourAttribute); - attributeEqual = kit.createInvokeWithExceptionAndUnwind(m, InvokeKind.Virtual, state, bci++, ourAttributeNonNull, otherAttribute); - } - - kit.startIf(graph.unique(new IntegerEqualsNode(attributeEqual, trueValue)), BranchProbabilityNode.LIKELY_PROFILE); - kit.elsePart(); - kit.append(new ReturnNode(falseValue)); - kit.endIf(); - } - kit.append(new ReturnNode(trueValue)); - - return kit.finalizeGraph(); - } - - } - - static class AnnotationHashCodeMethod extends AnnotationSubstitutionMethod { - AnnotationHashCodeMethod(ResolvedJavaMethod original) { - super(original); - } - - @Override - public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) { - assert !Modifier.isStatic(method.getModifiers()) && method.getSignature().getParameterCount(false) == 0; - ResolvedJavaType annotationType = method.getDeclaringClass(); - - HostedGraphKit kit = new HostedGraphKit(debug, providers, method, purpose); - StructuredGraph graph = kit.getGraph(); - FrameStateBuilder state = new FrameStateBuilder(null, method, graph); - state.initializeForMethodStart(null, true, providers.getGraphBuilderPlugins()); - - /* - * A random, but unique and consistent, number for every invoke. This is necessary - * because we, e.g., look up static analysis results by bci. - */ - int bci = 0; - graph.start().setStateAfter(state.create(bci++, graph.start())); - - ValueNode receiver = state.loadLocal(0, JavaKind.Object); - ValueNode result = ConstantNode.forInt(0, graph); - - for (Pair attributePair : findAttributes(annotationType)) { - String attribute = attributePair.getLeft(); - ResolvedJavaField ourField = findField(annotationType, attribute); - ResolvedJavaType attributeType = attributePair.getRight(); - - /* Access our value. We know that it is in a field. */ - ValueNode ourAttribute = kit.append(LoadFieldNode.create(null, receiver, ourField)); - - if (attributeType.isPrimitive()) { - /* Box primitive types. */ - ResolvedJavaType boxedAttributeType = providers.getMetaAccess().lookupJavaType(attributeType.getJavaKind().toBoxedJavaClass()); - ourAttribute = kit.append(BoxNode.create(ourAttribute, boxedAttributeType, attributeType.getJavaKind())); - } - - ourAttribute = unpackAttribute(providers, kit, ourAttribute, attributeType); - - ValueNode attributeHashCode; - if (attributeType.isArray()) { - /* Call the appropriate Arrays.hashCode() method for our attribute type. */ - ResolvedJavaMethod m = findMethod(providers.getMetaAccess().lookupJavaType(Arrays.class), "hashCode", attributeType); - attributeHashCode = kit.createInvokeWithExceptionAndUnwind(m, InvokeKind.Static, state, bci++, ourAttribute); - } else { - /* Just call Object.hashCode(). Primitive values are already boxed. */ - ourAttribute = kit.maybeCreateExplicitNullCheck(ourAttribute); - ResolvedJavaMethod m = kit.findMethod(Object.class, "hashCode"); - attributeHashCode = kit.createInvokeWithExceptionAndUnwind(m, InvokeKind.Virtual, state, bci++, ourAttribute); - } - - /* From the specification: sum up "name.hashCode() * 127 ^ value.hashCode()" */ - attributeHashCode = kit.unique(new XorNode(attributeHashCode, ConstantNode.forInt(127 * attribute.hashCode(), graph))); - result = kit.unique(new AddNode(result, attributeHashCode)); - } - kit.append(new ReturnNode(result)); - - return kit.finalizeGraph(); - } - } - - private static ValueNode unpackAttribute(HostedProviders providers, HostedGraphKit kit, ValueNode attribute, ResolvedJavaType attributeType) { - if (isClassType(attributeType, providers.getMetaAccess())) { - /* An annotation element that has a Class or Class[] type. */ - return unpackClassAttribute(providers, kit, attributeType, attribute); - } - return attribute; - } - - /** - * Attributes of type Class or Class[] are stored as Object since they can also encode a - * TypeNotPresentExceptionProxy. Unpack the attribute, throw the TypeNotPresentExceptionProxy if - * present, otherwise cast the value to the concrete Class or Class[] type. - */ - private static ValueNode unpackClassAttribute(HostedProviders providers, HostedGraphKit kit, ResolvedJavaType attributeType, ValueNode inputAttribute) { - ValueNode attribute = inputAttribute; - - /* Check if it stores a TypeNotPresentExceptionProxy. */ - ResolvedJavaType exceptionProxyType = providers.getMetaAccess().lookupJavaType(TypeNotPresentExceptionProxy.class); - TypeReference exceptionProxyTypeRef = TypeReference.createTrusted(kit.getAssumptions(), exceptionProxyType); - - LogicNode condition = kit.append(InstanceOfNode.create(exceptionProxyTypeRef, attribute)); - kit.startIf(condition, BranchProbabilityNode.SLOW_PATH_PROFILE); - kit.thenPart(); - - /* Generate the TypeNotPresentException exception and throw it. */ - PiNode casted = kit.createPiNode(attribute, StampFactory.object(exceptionProxyTypeRef, true)); - ResolvedJavaMethod generateExceptionMethod = kit.findMethod(TypeNotPresentExceptionProxy.class, "generateException", false); - ValueNode exception = kit.createJavaCallWithExceptionAndUnwind(InvokeKind.Virtual, generateExceptionMethod, casted); - kit.append(new UnwindNode(exception)); - - kit.elsePart(); - - /* Cast the value to the original type. */ - TypeReference resultTypeRef = TypeReference.createTrusted(kit.getAssumptions(), attributeType); - attribute = kit.createPiNode(attribute, StampFactory.object(resultTypeRef, true)); - - kit.endIf(); - - return attribute; - } - - static class AnnotationToStringMethod extends AnnotationSubstitutionMethod { - - AnnotationToStringMethod(ResolvedJavaMethod original) { - super(original); - } - - @Override - public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) { - assert !Modifier.isStatic(method.getModifiers()) && method.getSignature().getParameterCount(false) == 0; - ResolvedJavaType annotationType = method.getDeclaringClass(); - ResolvedJavaType annotationInterfaceType = findAnnotationInterfaceType(annotationType); - - HostedGraphKit kit = new HostedGraphKit(debug, providers, method, purpose); - StructuredGraph graph = kit.getGraph(); - - FrameStateBuilder state = new FrameStateBuilder(null, method, graph); - state.initializeForMethodStart(null, true, providers.getGraphBuilderPlugins()); - graph.start().setStateAfter(state.create(0, graph.start())); - - String returnValue; - if (JavaVersionUtil.JAVA_SPEC >= 19) { - /* - * In JDK 19, annotations use Class.getCanonicalName() instead of Class.getName(). - * See JDK-8281462. - */ - returnValue = "@" + getJavaClass(annotationInterfaceType).getCanonicalName(); - } else { - returnValue = "@" + annotationInterfaceType.toJavaName(true); - } - ValueNode returnConstant = kit.unique(ConstantNode.forConstant(providers.getSnippetReflection().forObject(returnValue), providers.getMetaAccess())); - kit.append(new ReturnNode(returnConstant)); - - return kit.finalizeGraph(); - } - - private static Class getJavaClass(ResolvedJavaType type) { - return OriginalClassProvider.getJavaClass(type); - } - } - - /* - * This method retrieves the annotation interface type from an annotation proxy type represented - * as an AnnotationSubstitutionType (or a type that wraps an AnnotationSubstitutionType). The - * constant-annotation-marker interface is already filtered out when - * AnnotationSubstitutionType.getInterfaces() is called. - */ - private static ResolvedJavaType findAnnotationInterfaceType(ResolvedJavaType annotationType) { - VMError.guarantee(NativeImagePointsToAnalysis.toWrappedType(annotationType) instanceof AnnotationSubstitutionType); - ResolvedJavaType[] interfaces = annotationType.getInterfaces(); - VMError.guarantee(interfaces.length == 1, "Unexpected number of interfaces for annotation proxy class."); - return interfaces[0]; - } - - /** - * This method retrieves the annotation interface type from a marked annotation proxy type. - * Annotation proxy types implement only the annotation interface by default. However, since we - * inject the constant-annotation-marker interface the Annotation proxy types for ahead-of-time - * allocated annotations implement two interfaces. We make sure we return the right one here. - */ - static ResolvedJavaType findAnnotationInterfaceTypeForMarkedAnnotationType(ResolvedJavaType annotationType, MetaAccessProvider metaAccess) { - ResolvedJavaType[] interfaces = annotationType.getInterfaces(); - VMError.guarantee(interfaces.length == 2, "Unexpected number of interfaces for annotation proxy class."); - VMError.guarantee(interfaces[1].equals(metaAccess.lookupJavaType(constantAnnotationMarkerInterface))); - return interfaces[0]; - } - - /* - * This method is similar to the above one, with the difference that it takes a Class instead - * of an ResolvedJavaType as an argument. - */ - static Class findAnnotationInterfaceTypeForMarkedAnnotationType(Class clazz) { - Class[] interfaces = clazz.getInterfaces(); - VMError.guarantee(interfaces.length == 2, "Unexpected number of interfaces for annotation proxy class."); - VMError.guarantee(interfaces[1].equals(constantAnnotationMarkerInterface)); - return interfaces[0]; - } - - static boolean isAnnotationMarkerInterface(ResolvedJavaType type, MetaAccessProvider metaAccess) { - return type.equals(metaAccess.lookupJavaType(constantAnnotationMarkerInterface)); - } - -} - -@AutomaticallyRegisteredFeature -class AnnotationSupportFeature implements InternalFeature { - - @Override - public void duringSetup(DuringSetupAccess access) { - access.registerObjectReplacer(new AnnotationObjectReplacer()); - } -} - -/** - * This replacer replaces the annotation proxy instances with a clone that additionally implements - * the constant-annotation-marker interface. - */ -class AnnotationObjectReplacer implements Function { - - /** - * Cache the replaced objects to ensure that they are only replaced once. We are using a - * concurrent hash map because replace() may be called from BigBang.finish(), which is - * multi-threaded. - * - * A side effect of this caching is de-duplication of annotation instances. When running as a - * native image two equal annotation instances are also identical. On HotSpot that is not true, - * the two annotation instances, although equal, are actually two distinct objects. Although - * this is a small deviation from HotSpot semantics it can improve the native image size. - * - * If de-duplication is not desired that can be achieved by replacing the ConcurrentHashMap with - * an IdentityHashMap (and additional access synchronisation). - */ - private ConcurrentHashMap objectCache = new ConcurrentHashMap<>(); - - /** - * During image generation, individual {@link SubstrateAnnotationInvocationHandler} instances - * are necessary because the invocation handler still stores the actual properties of the - * annotation. At run time, all individual objects can be canonicalized to a singleton. - */ - private static final SubstrateAnnotationInvocationHandler SINGLETON_HANDLER = new SubstrateAnnotationInvocationHandler(null); - - private static final Class HOSTED_INVOCATION_HANDLER_CLASS; - static { - try { - HOSTED_INVOCATION_HANDLER_CLASS = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); - } catch (ClassNotFoundException ex) { - throw VMError.shouldNotReachHere(ex); - } - } - - @Override - public Object apply(Object original) { - Class clazz = original.getClass(); - /* - * We only optimize standard implementation annotation types, i.e., the type must implement - * one and only one interface: the annotation interface, which in turn must implement - * java.lang.Annotation. Non-standard annotation implementations use their default - * implementation at run time. - */ - if (clazz.getInterfaces().length == 1 && clazz.getInterfaces()[0].isAnnotation() && - Proxy.class.isAssignableFrom(clazz)) { - return objectCache.computeIfAbsent(original, AnnotationObjectReplacer::replacementComputer); - } else if (original instanceof SubstrateAnnotationInvocationHandler) { - return SINGLETON_HANDLER; - } else if (HOSTED_INVOCATION_HANDLER_CLASS.isInstance(original)) { - /* - * If we see an instance of the AnnotationInvocationHandler used by the JDK as reachable - * in the image heap, something is wrong in our handling of annotations. - */ - throw VMError.shouldNotReachHere("Instance of the hosted AnnotationInvocationHandler is reachable at run time"); - } - - return original; - } - - /** - * Effectively clones the original proxy object and it adds the constant-annotation-marker - * interface. - */ - private static Object replacementComputer(Object original) { - Class[] interfaces = original.getClass().getInterfaces(); - Class[] extendedInterfaces = Arrays.copyOf(interfaces, interfaces.length + 1); - extendedInterfaces[extendedInterfaces.length - 1] = AnnotationSupport.constantAnnotationMarkerInterface; - return Proxy.newProxyInstance(original.getClass().getClassLoader(), extendedInterfaces, new SubstrateAnnotationInvocationHandler(Proxy.getInvocationHandler(original))); - } -} 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 deleted file mode 100644 index 72fac676c22e..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java +++ /dev/null @@ -1,56 +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.hosted.annotation; - -import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.AnalysisUniverse; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; -import com.oracle.svm.core.feature.InternalFeature; -import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; - -@AutomaticallyRegisteredFeature -public class AnnotationTypeFeature implements InternalFeature { - - @Override - public void duringAnalysis(DuringAnalysisAccess access) { - DuringAnalysisAccessImpl accessImpl = (DuringAnalysisAccessImpl) access; - AnalysisUniverse universe = accessImpl.getUniverse(); - - universe.getTypes().stream() - .filter(AnalysisType::isAnnotation) - .filter(AnalysisType::isReachable) - .map(type -> universe.lookup(type.getWrapped()).getArrayClass()) - .filter(annotationArray -> !annotationArray.isInstantiated()) - .forEach(annotationArray -> { - /* - * JDK implementation of repeatable annotations always instantiates an - * array of a requested annotation. We need to mark arrays of all - * reachable annotations as in heap. - */ - accessImpl.registerAsInHeap(annotationArray, "Is the array type of a reachable annotation."); - access.requireAnalysisIteration(); - }); - } -} 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 deleted file mode 100644 index 6c6c00b0a3fe..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/ConstantAnnotationMarkerSubstitutionType.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2020, 2020, 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.AnnotatedElement; - -import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; -import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; - -import jdk.vm.ci.meta.Assumptions; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; - -/** - * This class is used to correct the behavior of - * {@link ResolvedJavaType#isAssignableFrom(ResolvedJavaType)} for - * {@link AnnotationSupport#constantAnnotationMarkerInterface}. For all other methods, this class - * simply forwards calls to the original ResolvedJavaType. See - * AnnotationSupport#constantAnnotationMarkerSubstitutionType for more details. - */ -public class ConstantAnnotationMarkerSubstitutionType implements ResolvedJavaType, OriginalClassProvider, AnnotationWrapper { - private final ResolvedJavaType original; - private final SubstitutionProcessor substitutionProcessor; - - public ConstantAnnotationMarkerSubstitutionType(ResolvedJavaType original, SubstitutionProcessor substitutionProcessor) { - this.original = original; - this.substitutionProcessor = substitutionProcessor; - } - - /** - * Since AnnotationSubstitutionTypes do not naturally implement the constant marker interface, - * but are artificially forced to implement it via - * AnnotationObjectReplacer#replacementComputer(Object), they should not be considered - * assignable from the marker interface. - */ - @Override - public boolean isAssignableFrom(ResolvedJavaType other) { - ResolvedJavaType substitution = substitutionProcessor.lookup(other); - if (substitution instanceof AnnotationSubstitutionType) { - return false; - } - return original.isAssignableFrom(other); - } - - @Override - public Class getJavaClass() { - return OriginalClassProvider.getJavaClass(original); - } - - @Override - public boolean hasFinalizer() { - return original.hasFinalizer(); - } - - @Override - public Assumptions.AssumptionResult hasFinalizableSubclass() { - return original.hasFinalizableSubclass(); - } - - @Override - public int getModifiers() { - return original.getModifiers(); - } - - @Override - public boolean isInterface() { - return original.isInterface(); - } - - @Override - public boolean isInstanceClass() { - return original.isInstanceClass(); - } - - @Override - public boolean isPrimitive() { - return original.isPrimitive(); - } - - @Override - public boolean isEnum() { - return original.isEnum(); - } - - @Override - public boolean isInitialized() { - return original.isInitialized(); - } - - @Override - public void initialize() { - original.initialize(); - } - - @Override - public boolean isLinked() { - return original.isLinked(); - } - - @SuppressWarnings("deprecation") - @Override - public ResolvedJavaType getHostClass() { - return original.getHostClass(); - } - - @Override - public boolean isInstance(JavaConstant obj) { - return original.isInstance(obj); - } - - @Override - public ResolvedJavaType getSuperclass() { - return original.getSuperclass(); - } - - @Override - public ResolvedJavaType[] getInterfaces() { - return original.getInterfaces(); - } - - @Override - public ResolvedJavaType getSingleImplementor() { - return original.getSingleImplementor(); - } - - @Override - public ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType) { - return original.findLeastCommonAncestor(otherType); - } - - @Override - public Assumptions.AssumptionResult findLeafConcreteSubtype() { - return original.findLeafConcreteSubtype(); - } - - @Override - public String getName() { - return original.getName(); - } - - @Override - public ResolvedJavaType getComponentType() { - return original.getComponentType(); - } - - @Override - public ResolvedJavaType getArrayClass() { - return original.getArrayClass(); - } - - @Override - public JavaKind getJavaKind() { - return original.getJavaKind(); - } - - @Override - public ResolvedJavaType resolve(ResolvedJavaType accessingClass) { - return original.resolve(accessingClass); - } - - @Override - public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { - return original.resolveMethod(method, callerType); - } - - @Override - public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { - return original.resolveConcreteMethod(method, callerType); - } - - @Override - public Assumptions.AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) { - return original.findUniqueConcreteMethod(method); - } - - @Override - public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) { - return original.getInstanceFields(includeSuperclasses); - } - - @Override - public ResolvedJavaField[] getStaticFields() { - return original.getStaticFields(); - } - - @Override - public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) { - return original.findInstanceFieldWithOffset(offset, expectedKind); - } - - @Override - public String getSourceFileName() { - return original.getSourceFileName(); - } - - @Override - public boolean isLocal() { - return original.isLocal(); - } - - @Override - public boolean isMember() { - return original.isMember(); - } - - @Override - public ResolvedJavaType getEnclosingType() { - return original.getEnclosingType(); - } - - @Override - public ResolvedJavaMethod[] getDeclaredConstructors() { - return original.getDeclaredConstructors(); - } - - @Override - public ResolvedJavaMethod[] getDeclaredMethods() { - return original.getDeclaredMethods(); - } - - @Override - public ResolvedJavaMethod getClassInitializer() { - return original.getClassInitializer(); - } - - @Override - public void link() { - original.link(); - } - - @Override - public boolean hasDefaultMethods() { - return original.hasDefaultMethods(); - } - - @Override - public boolean declaresDefaultMethods() { - return original.declaresDefaultMethods(); - } - - @Override - public boolean isCloneableWithAllocation() { - return original.isCloneableWithAllocation(); - } - - @Override - public AnnotatedElement getAnnotationRoot() { - return original; - } -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitution.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitution.java deleted file mode 100644 index 294b87730a9d..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitution.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2014, 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.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.graalvm.collections.Pair; - -import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; - -import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; -import jdk.vm.ci.meta.Signature; - -/** - * A mapping between original {@link ResolvedJavaType}s and {@link CustomSubstitutionType}s used as - * substitutions for the original type at image build time. - */ -public class CustomSubstitution> extends SubstitutionProcessor { - - protected final MetaAccessProvider metaAccess; - protected final Map typeSubstitutions; - - public CustomSubstitution(MetaAccessProvider metaAccess) { - this.metaAccess = metaAccess; - this.typeSubstitutions = new HashMap<>(); - } - - protected T getSubstitutionType(ResolvedJavaType original) { - return typeSubstitutions.get(original); - } - - /* - * For deoptimization, we add "*" to the method name to distinguish the different variants of - * the method (this is necessary to make the method names unique). Remove that suffix here. - */ - protected static String canonicalMethodName(ResolvedJavaMethod method) { - String result = method.getName(); - while (result.endsWith("*")) { - result = result.substring(0, result.length() - 1); - } - return result; - } - - /** - * Return the annotation attributes. For each attribute return both the name and the original - * type, as declared by the return type of the annotation method. The name is later used to find - * the corresponding synthetic field that holds the value. The declared type of the field can be - * different than that of the actual attribute type: when the attribute type is either - * {@link Class} or {@code Class[]} the field type is {@link Object} since it can also store a - * {@link sun.reflect.annotation.TypeNotPresentExceptionProxy}. - */ - protected static List> findAttributes(ResolvedJavaType annotationType) { - List> attributes = new ArrayList<>(); - for (ResolvedJavaMethod method : annotationType.getDeclaredMethods()) { - String methodName = canonicalMethodName(method); - if (methodName.equals("equals") || methodName.equals("hashCode") || methodName.equals("toString") || methodName.equals("annotationType") || methodName.equals("proxyClassLookup")) { - /* Ignore non-accessor methods. */ - } else { - ResolvedJavaType returnType = (ResolvedJavaType) method.getSignature().getReturnType(null); - attributes.add(Pair.create(methodName, returnType)); - } - } - - /* Sort them (by any order) so that the Graal graphs are deterministic. */ - Collections.sort(attributes, Comparator.comparing(Pair::getLeft)); - return attributes; - } - - protected static ResolvedJavaMethod findMethod(ResolvedJavaType declaringType, String name, ResolvedJavaType... argumentTypes) { - ResolvedJavaMethod result = null; - outer: for (ResolvedJavaMethod method : declaringType.getDeclaredMethods()) { - if (canonicalMethodName(method).equals(name) && method.getSignature().getParameterCount(false) == argumentTypes.length) { - Signature sig = method.getSignature(); - for (int i = 0; i < argumentTypes.length; i++) { - if (!sig.getParameterType(i, null).resolve(null).isAssignableFrom(argumentTypes[i])) { - continue outer; - } - } - assert result == null : "more than one matching method found"; - result = method; - } - } - assert result != null : "no matching method found"; - return result; - } - - protected static ResolvedJavaField findField(ResolvedJavaType declaringType, String name) { - ResolvedJavaField result = null; - for (ResolvedJavaField field : declaringType.getInstanceFields(false)) { - if (field.getName().equals(name)) { - assert result == null : "more than one matching field found"; - result = field; - } - } - assert result != null : "no matching field found"; - return result; - } - -} 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 deleted file mode 100644 index fcf176262c93..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionField.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2014, 2023, 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.AnnotatedElement; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.ameta.ReadableJavaField; - -import jdk.vm.ci.meta.ResolvedJavaType; - -public abstract class CustomSubstitutionField implements ReadableJavaField, OriginalFieldProvider, AnnotationWrapper { - - protected final ResolvedJavaType declaringClass; - - public CustomSubstitutionField(ResolvedJavaType declaringClass) { - this.declaringClass = declaringClass; - } - - @Override - public int getModifiers() { - return Modifier.PRIVATE; - } - - @Override - public int getOffset() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isInternal() { - return false; - } - - @Override - public boolean isSynthetic() { - return false; - } - - @Override - public ResolvedJavaType getDeclaringClass() { - return declaringClass; - } - - @Override - public AnnotatedElement getAnnotationRoot() { - return null; - } - - @Override - public Field getJavaField() { - throw VMError.shouldNotReachHereAtRuntime(); // ExcludeFromJacocoGeneratedReport - } -} 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 deleted file mode 100644 index a356e1a68da8..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (c) 2014, 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.AnnotatedElement; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; - -import jdk.vm.ci.common.JVMCIError; -import jdk.vm.ci.meta.Assumptions.AssumptionResult; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; - -/** - * A type used as a substitution for an original type. - * - * @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, AnnotationWrapper { - - protected final ResolvedJavaType original; - protected final List fields; - protected final Map methods; - - public CustomSubstitutionType(ResolvedJavaType original) { - this.original = original; - - fields = new ArrayList<>(); - methods = new HashMap<>(); - } - - /** Get the type that this is a substitution for. */ - public ResolvedJavaType getOriginal() { - return original; - } - - /** Get the substitution for a method on the original type. */ - public M getSubstitutionMethod(ResolvedJavaMethod method) { - return methods.get(method); - } - - public void addSubstitutionMethod(ResolvedJavaMethod originalMethod, M substitution) { - methods.put(originalMethod, substitution); - } - - public void addSubstitutionField(F field) { - fields.add(field); - } - - @Override - public JavaKind getJavaKind() { - return JavaKind.Object; - } - - @Override - public ResolvedJavaType resolve(ResolvedJavaType accessingClass) { - return this; - } - - @Override - public boolean hasFinalizer() { - return false; - } - - @Override - public AssumptionResult hasFinalizableSubclass() { - return new AssumptionResult<>(false); - } - - @Override - public boolean isInterface() { - return false; - } - - @Override - public boolean isInstanceClass() { - return true; - } - - @Override - public boolean isArray() { - return false; - } - - @Override - public boolean isPrimitive() { - return false; - } - - @Override - public boolean isEnum() { - return original.isEnum(); - } - - @Override - public int getModifiers() { - return original.getModifiers(); - } - - @Override - public boolean isInitialized() { - return true; - } - - @Override - public void initialize() { - assert isInitialized(); - } - - @Override - public boolean isLinked() { - return true; - } - - @Override - public void link() { - assert isLinked(); - } - - @Override - public boolean hasDefaultMethods() { - assert !isInterface() : "only interfaces can have default methods"; - return false; - } - - @Override - public boolean declaresDefaultMethods() { - assert !isInterface() : "only interfaces can have default methods"; - return false; - } - - @Override - public boolean isAssignableFrom(ResolvedJavaType other) { - return original.isAssignableFrom(other); - } - - @Override - public boolean isInstance(JavaConstant obj) { - return original.isInstance(obj); - } - - @Override - public ResolvedJavaType getSuperclass() { - return original.getSuperclass(); - } - - @Override - public ResolvedJavaType[] getInterfaces() { - return original.getInterfaces(); - } - - @Override - public ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType) { - return original.findLeastCommonAncestor(otherType); - } - - @Override - public ResolvedJavaType getSingleImplementor() { - return original.getSingleImplementor(); - } - - @Override - public AssumptionResult findLeafConcreteSubtype() { - return original.findLeafConcreteSubtype(); - } - - @Override - public ResolvedJavaType getComponentType() { - return original.getComponentType(); - } - - @Override - public ResolvedJavaType getArrayClass() { - return original.getArrayClass(); - } - - @Override - public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { - return original.resolveConcreteMethod(method, callerType); - } - - @Override - public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { - return original.resolveMethod(method, callerType); - } - - @Override - public AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) { - return original.findUniqueConcreteMethod(method); - } - - @Override - public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) { - return fields.toArray(new ResolvedJavaField[fields.size()]); - } - - @Override - public ResolvedJavaField[] getStaticFields() { - return original.getStaticFields(); - } - - @Override - public AnnotatedElement getAnnotationRoot() { - return original; - } - - @Override - public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) { - /* All of our instance fields are synthetic and do not exist in the hosted VM. */ - return null; - } - - @Override - public String getSourceFileName() { - return null; - } - - @Override - public boolean isLocal() { - return false; - } - - @Override - public boolean isMember() { - return false; - } - - @Override - public ResolvedJavaType getEnclosingType() { - return null; - } - - @Override - public ResolvedJavaMethod[] getDeclaredConstructors() { - return new ResolvedJavaMethod[0]; - } - - @Override - public ResolvedJavaMethod[] getDeclaredMethods() { - return original.getDeclaredMethods(); - } - - @Override - public ResolvedJavaMethod getClassInitializer() { - return null; - } - - @Override - public boolean isCloneableWithAllocation() { - throw JVMCIError.unimplemented(); - } - - @SuppressWarnings("deprecation") - @Override - public ResolvedJavaType getHostClass() { - throw JVMCIError.unimplemented(); - } - - @Override - public Class getJavaClass() { - return OriginalClassProvider.getJavaClass(original); - } - -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java index 4c91a8e8c8de..2e9464fcd9b1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageDebugInfoProvider.java @@ -71,7 +71,6 @@ import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ObjectHeader; import com.oracle.svm.core.image.ImageHeapPartition; -import com.oracle.svm.hosted.annotation.CustomSubstitutionType; import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo; import com.oracle.svm.hosted.image.sources.SourceManager; import com.oracle.svm.hosted.lambda.LambdaSubstitutionType; @@ -297,8 +296,6 @@ private static ResolvedJavaType getOriginal(HostedType hostedType) { ResolvedJavaType javaType = hostedType.getWrapped().getWrapped(); if (javaType instanceof SubstitutionType) { return ((SubstitutionType) javaType).getOriginal(); - } else if (javaType instanceof CustomSubstitutionType) { - return ((CustomSubstitutionType) javaType).getOriginal(); } else if (javaType instanceof LambdaSubstitutionType) { return ((LambdaSubstitutionType) javaType).getOriginal(); } else if (javaType instanceof InjectedFieldsType) { 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 6d083eb4adfa..321fe511587b 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 @@ -72,7 +72,6 @@ import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.NativeImageGenerator; import com.oracle.svm.hosted.NativeImageOptions; -import com.oracle.svm.hosted.annotation.AnnotationSubstitutionType; import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.code.IncompatibleClassChangeFallbackMethod; @@ -194,8 +193,6 @@ private ResolvedJavaType findTypeSubstitution(ResolvedJavaType type) { public ResolvedJavaType resolve(ResolvedJavaType type) { if (type instanceof SubstitutionType) { return ((SubstitutionType) type).getAnnotated(); - } else if (type instanceof AnnotationSubstitutionType) { - return ((AnnotationSubstitutionType) type).getOriginal(); } else if (type instanceof InjectedFieldsType) { return ((InjectedFieldsType) type).getOriginal(); }