diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py index 787abc9c7877..9b661cf9f99a 100644 --- a/compiler/mx.compiler/suite.py +++ b/compiler/mx.compiler/suite.py @@ -140,7 +140,6 @@ "requiresConcealed" : { "java.base" : [ "jdk.internal.misc", - "sun.reflect.generics.parser", ], "jdk.internal.vm.ci" : [ "jdk.vm.ci.meta", diff --git a/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java b/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java index fbc530158cf5..4b34dc02d6b7 100644 --- a/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java +++ b/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java @@ -44,7 +44,6 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import jdk.graal.compiler.serviceprovider.GraalServices; import org.graalvm.collections.EconomicMap; import org.graalvm.jniutils.NativeBridgeSupport; import org.graalvm.nativeimage.ImageInfo; @@ -72,6 +71,7 @@ import jdk.graal.compiler.options.OptionDescriptor; import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionsParser; +import jdk.graal.compiler.serviceprovider.GraalServices; import jdk.graal.compiler.truffle.host.TruffleHostEnvironment; import jdk.graal.compiler.util.CollectionsUtil; import jdk.graal.compiler.util.EconomicHashMap; diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsBase.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsBase.java index 67dcc4f511fa..42fb68ca9124 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsBase.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsBase.java @@ -29,6 +29,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; @@ -209,6 +210,13 @@ private static Annotated toAnnotated(AnnotatedElement element) { } } + /** + * Used to test error handling in {@link AnnotationValue#getEnum(Class, String)}. + */ + enum MyEnum { + } + + @SuppressWarnings({"unchecked", "rawtypes"}) public static void assertAnnotationsEquals(Annotation a, AnnotationValue av) { Map values = AnnotationSupport.memberValues(a); for (Map.Entry e : values.entrySet()) { @@ -224,6 +232,46 @@ public static void assertAnnotationsEquals(Annotation a, AnnotationValue av) { if (!(aElement instanceof ExceptionProxy)) { Class elementType = toAnnotationValueElementType(aElement.getClass()); av.get(name, elementType); + + Object actual; + if (elementType == Byte.class) { + actual = av.getByte(name); + } else if (elementType == Boolean.class) { + actual = av.getBoolean(name); + } else if (elementType == Short.class) { + actual = av.getShort(name); + } else if (elementType == Character.class) { + actual = av.getChar(name); + } else if (elementType == Integer.class) { + actual = av.getInt(name); + } else if (elementType == Float.class) { + actual = av.getFloat(name); + } else if (elementType == Long.class) { + actual = av.getLong(name); + } else if (elementType == Double.class) { + actual = av.getDouble(name); + } else if (elementType == String.class) { + actual = av.getString(name); + } else if (elementType == ResolvedJavaType.class) { + actual = av.getType(name); + } else if (elementType == EnumElement.class) { + actual = av.getEnum(name); + Class enumClass = (Class) aElement.getClass(); + var avEnumConstant = av.getEnum(enumClass, name); + assertEquals(aElement, avEnumConstant); + try { + av.getEnum(MyEnum.class, name); + fail("expected " + IllegalArgumentException.class.getName()); + } catch (IllegalArgumentException iae) { + // expected + } + } else if (elementType == AnnotationValue.class) { + actual = av.getAnnotation(name); + } else { + assert elementType == List.class : aElement + " // " + elementType; + actual = avElement; + } + assertAnnotationElementsEqual(aElement, actual); } } } diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsOnMethods.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsOnMethods.java index 18ea598edcb1..99ed483327ea 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsOnMethods.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsOnMethods.java @@ -198,7 +198,7 @@ public void getAnnotationValuesTest() throws Exception { checkAnnotationValues(AnnotationTestInput.class.getDeclaredMethod("missingMember")); List avList = checkAnnotationValues(AnnotationTestInput.class.getDeclaredMethod("addedMember")); try { - avList.getFirst().get("addedElement", Integer.class); + avList.getFirst().getInt("addedElement"); throw new AssertionError("expected " + IllegalArgumentException.class.getName()); } catch (IllegalArgumentException e) { Assert.assertEquals(MemberAdded.class.getName() + " missing element addedElement", e.getMessage()); diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsOnTypes.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsOnTypes.java index 4ce108f19622..ed21cfd7f051 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsOnTypes.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/annotation/test/TestAnnotationsOnTypes.java @@ -102,7 +102,7 @@ public void getAnnotationValuesTest() { // Test that inherited annotations are handled properly. ResolvedJavaType namedType = metaAccess.lookupJavaType(AnnotationTestInput.Named.class); AnnotationValue av = AnnotationValueSupport.getDeclaredAnnotationValue(namedType, metaAccess.lookupJavaType(AnnotationTestInput.OwnName.class)); - Assert.assertEquals("NonInheritedValue", av.get("value", String.class)); + Assert.assertEquals("NonInheritedValue", av.getString("value")); av = getDeclaredAnnotationValue(namedType, metaAccess.lookupJavaType(AnnotationTestInput.InheritedName1.class)); Assert.assertNull(av); av = getDeclaredAnnotationValue(namedType, metaAccess.lookupJavaType(AnnotationTestInput.InheritedName2.class)); diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/api/directives/test/BlackholeDirectiveTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/api/directives/test/BlackholeDirectiveTest.java index f1b4efbf59f5..b01dff051ebb 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/api/directives/test/BlackholeDirectiveTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/api/directives/test/BlackholeDirectiveTest.java @@ -29,13 +29,16 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.junit.Assert; +import org.junit.Test; + +import jdk.graal.compiler.annotation.AnnotationValue; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.api.directives.GraalDirectives; import jdk.graal.compiler.core.test.GraalCompilerTest; import jdk.graal.compiler.nodes.ParameterNode; import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.phases.OptimisticOptimizations; -import org.junit.Assert; -import org.junit.Test; /** * Tests for {@link GraalDirectives#blackhole}. @@ -137,9 +140,9 @@ protected OptimisticOptimizations getOptimisticOptimizations() { @Override protected void checkLowTierGraph(StructuredGraph graph) { - BlackholeSnippet snippet = graph.method().getAnnotation(BlackholeSnippet.class); + AnnotationValue snippet = AnnotationValueSupport.getAnnotationValue(graph.method(), BlackholeSnippet.class); ParameterNode arg = graph.getParameter(0); - if (snippet.expectParameterUsage()) { + if (snippet != null && snippet.getBoolean("expectParameterUsage")) { Assert.assertNotNull("couldn't find ParameterNode(0)", arg); Assert.assertFalse("expected usages of " + arg, arg.hasNoUsages()); } else { diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CheckGraalInvariants.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CheckGraalInvariants.java index 055de323ed54..faddd56a1f69 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CheckGraalInvariants.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CheckGraalInvariants.java @@ -215,6 +215,13 @@ public boolean shouldVerifyFoldableMethods() { return true; } + /** + * Determines if {@link VerifyAnnotatedElementUsage} is to be checked. + */ + public boolean shouldVerifyAnnotatedElementUsages() { + return true; + } + public void verifyCurrentTimeMillis(MetaAccessProvider meta, MethodCallTargetNode t, ResolvedJavaType declaringClass) { final ResolvedJavaType services = meta.lookupJavaType(GraalServices.class); if (!declaringClass.equals(services)) { @@ -393,6 +400,9 @@ public static void runTest(InvariantsTool tool) { if (tool.shouldVerifyFoldableMethods()) { verifiers.add(foldableMethodsVerifier); } + if (tool.shouldVerifyAnnotatedElementUsages()) { + verifiers.add(new VerifyAnnotatedElementUsage()); + } verifiers.add(new VerifyCurrentTimeMillisUsage(tool)); diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyAnnotatedElementUsage.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyAnnotatedElementUsage.java new file mode 100644 index 000000000000..87852eb1ea13 --- /dev/null +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyAnnotatedElementUsage.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013, 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 jdk.graal.compiler.core.test; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import jdk.graal.compiler.annotation.AnnotationValueSupport; +import jdk.graal.compiler.core.common.type.Stamp; +import jdk.graal.compiler.nodes.NodeView; +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.graal.compiler.nodes.java.MethodCallTargetNode; +import jdk.graal.compiler.nodes.spi.CoreProviders; +import jdk.graal.compiler.nodes.spi.UncheckedInterfaceProvider; +import jdk.graal.compiler.util.EconomicHashMap; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; + +/** + * Verifies that calls to methods declared by {@link AnnotatedElement} never have a receiver of type + * {@link ResolvedJavaType}, {@link ResolvedJavaMethod} or {@link ResolvedJavaField}. Once GR-69713, + * is resolved ("Remove AnnotatedElement from JVMCI types"), this verification can be deleted. + */ +public class VerifyAnnotatedElementUsage extends VerifyStringFormatterUsage { + + private volatile Map annotatedElementMethods; + + private static final String JVMCI_META_PACKAGE_PREFIX = "L" + ResolvedJavaType.class.getPackage().getName().replace('.', '/'); + private static final Set ANNOTATED_ELEMENT_METHOD_NAMES = Stream.of(AnnotatedElement.class.getDeclaredMethods()).map(Method::getName).collect(Collectors.toSet()); + + @Override + protected void verify(StructuredGraph graph, CoreProviders context) { + MetaAccessProvider metaAccess = context.getMetaAccess(); + ResolvedJavaType annotatedElementType = metaAccess.lookupJavaType(AnnotatedElement.class); + ResolvedJavaType resolvedJavaTypeType = metaAccess.lookupJavaType(ResolvedJavaType.class); + ResolvedJavaType resolvedJavaMethodType = metaAccess.lookupJavaType(ResolvedJavaMethod.class); + ResolvedJavaType resolvedJavaFieldType = metaAccess.lookupJavaType(ResolvedJavaField.class); + + if (annotatedElementMethods == null) { + Map map = new EconomicHashMap<>(); + for (Method m : AnnotatedElement.class.getDeclaredMethods()) { + map.put(m.getName(), metaAccess.lookupJavaMethod(m).getSignature().toMethodDescriptor()); + } + annotatedElementMethods = map; + } + + for (MethodCallTargetNode t : graph.getNodes(MethodCallTargetNode.TYPE)) { + ResolvedJavaMethod callee = t.targetMethod(); + String descriptor = annotatedElementMethods.get(callee.getName()); + if (descriptor != null && descriptor.equals(callee.getSignature().toMethodDescriptor())) { + if (callee.hasReceiver()) { + ValueNode receiver = t.arguments().getFirst(); + Stamp receiverStamp = receiver.stamp(NodeView.DEFAULT); + if (receiver instanceof UncheckedInterfaceProvider unchecked) { + Stamp uncheckedStamp = unchecked.uncheckedStamp(); + if (uncheckedStamp != null) { + receiverStamp = uncheckedStamp; + } + } + ResolvedJavaType receiverType = receiverStamp.javaType(metaAccess); + if (resolvedJavaTypeType.isAssignableFrom(receiverType) || + resolvedJavaMethodType.isAssignableFrom(receiverType) || + resolvedJavaFieldType.isAssignableFrom(receiverType)) { + + throw new VerificationError( + t, "call to %s with receiver type %s should be replaced by use of %s.%n", + callee.format("%H.%n(%p)"), + receiverType.toClassName(), + AnnotationValueSupport.class.getName()); + } + } + } + } + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValue.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValue.java index e2f40ee92658..ec2e81530de5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValue.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValue.java @@ -200,6 +200,143 @@ public V get(String name, Class elementType) { return elementType.cast(val); } + /** + * Gets the byte element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a byte + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public byte getByte(String name) { + return get(name, Byte.class); + } + + /** + * Gets the boolean element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a boolean + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public boolean getBoolean(String name) { + return get(name, Boolean.class); + } + + /** + * Gets the char element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a char + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public char getChar(String name) { + return get(name, Character.class); + } + + /** + * Gets the short element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a short + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public short getShort(String name) { + return get(name, Short.class); + } + + /** + * Gets the int element denoted by {@code name}. + * + * @throws ClassCastException if the element is not an int + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public int getInt(String name) { + return get(name, Integer.class); + } + + /** + * Gets the float element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a float + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public float getFloat(String name) { + return get(name, Float.class); + } + + /** + * Gets the long element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a long + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public long getLong(String name) { + return get(name, Long.class); + } + + /** + * Gets the double element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a double + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public double getDouble(String name) { + return get(name, Double.class); + } + + /** + * Gets the string element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a string + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public String getString(String name) { + return get(name, String.class); + } + + /** + * Gets the {@link ResolvedJavaType} element denoted by {@code name}. + * + * @throws ClassCastException if the element is not a {@link ResolvedJavaType} + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public ResolvedJavaType getType(String name) { + return get(name, ResolvedJavaType.class); + } + + /** + * Gets the annotation element denoted by {@code name}. + * + * @throws ClassCastException if the element is not an annotation + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public AnnotationValue getAnnotation(String name) { + return get(name, AnnotationValue.class); + } + + /** + * Gets the enum element denoted by {@code name}. + * + * @throws ClassCastException if the element is not an enum + * @throws IllegalArgumentException if this annotation has no element named {@code name} + */ + public EnumElement getEnum(String name) { + return get(name, EnumElement.class); + } + + /** + * Gets the enum element denoted by {@code name}, resolved to an {@code enumClass} constant. + * + * @throws ClassCastException if the element is not an enum + * @throws IllegalArgumentException if this annotation has no element named {@code name}, if + * {@code enumClass} does not match the expected enum type or {@code enumClass} has + * no constant with the specified name + */ + public > T getEnum(Class enumClass, String name) { + EnumElement enumElement = getEnum(name); + String foundType = enumElement.enumType.toClassName(); + if (!foundType.equals(enumClass.getName())) { + throw new IllegalArgumentException("Unexpected enum type: " + foundType); + } + return Enum.valueOf(enumClass, enumElement.name); + } + /** * Gets an unmodifiable view of the elements in this annotation value. The type for each value * in the returned map is specified by {@link #get(String, Class)}. diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValueSupport.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValueSupport.java index 0c9adcb23317..2465de9c7084 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValueSupport.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/AnnotationValueSupport.java @@ -24,6 +24,10 @@ */ package jdk.graal.compiler.annotation; +import static jdk.graal.compiler.core.common.NativeImageSupport.inRuntimeCode; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.RecordComponent; @@ -31,9 +35,14 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import jdk.graal.compiler.core.common.LibGraalSupport; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.util.EconomicHashMap; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.UnresolvedJavaType; import jdk.vm.ci.meta.annotation.Annotated; import jdk.vm.ci.meta.annotation.AnnotationsInfo; @@ -69,17 +78,13 @@ public static AnnotationValue getDeclaredAnnotationValue(ResolvedJavaType annota * present on this element */ public static Map getDeclaredAnnotationValues(Annotated annotated) { - AnnotationsInfo info = getDeclaredAnnotationInfo(annotated); + AnnotationsInfo info = annotated.getDeclaredAnnotationInfo(); if (info == null) { return Collections.emptyMap(); } return AnnotationValueParser.parseAnnotations(info.bytes(), info.constPool(), info.container()); } - private static AnnotationsInfo getDeclaredAnnotationInfo(Annotated annotated) { - return annotated.getDeclaredAnnotationInfo(); - } - /** * Gets the type annotations for {@code annotated} that back the implementation of * {@link Method#getAnnotatedReturnType()}, {@link Method#getAnnotatedReceiverType()}, @@ -131,4 +136,76 @@ public static Object getAnnotationDefaultValue(ResolvedJavaMethod method) { ResolvedJavaType memberType = method.getSignature().getReturnType(container).resolve(container); return AnnotationValueParser.parseMemberValue(memberType, ByteBuffer.wrap(info.bytes()), info.constPool(), container); } + + /** + * Gets the annotation value of type {@code annotationType} present on {@code annotated}. This + * method must only be called in jargraal as it requires the ability convert a {@link Class} + * value to a {@link ResolvedJavaType} value. + */ + @LibGraalSupport.HostedOnly + public static AnnotationValue getAnnotationValue(Annotated annotated, Class annotationType) { + if (inRuntimeCode()) { + throw new GraalError("Cannot look up %s annotation at Native Image runtime", annotationType.getName()); + } + boolean inherited = annotationType.getAnnotation(Inherited.class) != null; + return getAnnotationValue0(annotated, annotationType, inherited); + } + + /** + * Cache for {@link #getAnnotationValue}. Building libgraal-ee shows that this cache grows to + * about 3K entries and there are about 30K accesses so no need to optimize further with an LRU + * cache. + */ + @LibGraalSupport.HostedOnly // + private static final Map> declaredAnnotations = LibGraalSupport.INSTANCE == null ? Collections.synchronizedMap(new EconomicHashMap<>()) : null; + + @LibGraalSupport.HostedOnly + private static AnnotationValue getAnnotationValue0(Annotated annotated, Class annotationType, boolean inherited) { + AnnotationsInfo info = annotated.getDeclaredAnnotationInfo(); + if (info == null && !inherited) { + return null; + } + Map map = declaredAnnotations.get(annotated); + if (map == null) { + /* + * Do not use Map#computeIfAbsent as Collections.SynchronizedMap#computeIfAbsent blocks + * readers during the creation of the cached value. + */ + map = AnnotationValueParser.parseAnnotations(info.bytes(), info.constPool(), info.container()); + var existing = declaredAnnotations.putIfAbsent(annotated, map); + if (existing != null) { + map = existing; + } + } + + AnnotationValue res = lookup(annotationType, map, info); + if (res != null) { + return res; + } + if (inherited && annotated instanceof ResolvedJavaType type && !type.isJavaLangObject()) { + ResolvedJavaType superclass = type.getSuperclass(); + return getAnnotationValue0(superclass, annotationType, true); + } + return null; + } + + @LibGraalSupport.HostedOnly // + private static final Map, ResolvedJavaType> resolvedAnnotationTypeCache = LibGraalSupport.INSTANCE != null ? null + : new ConcurrentHashMap<>(); + + @LibGraalSupport.HostedOnly + private static AnnotationValue lookup(Class annotationType, Map map, AnnotationsInfo info) { + String internalName = "L" + annotationType.getName().replace(".", "/") + ";"; + for (var e : map.entrySet()) { + ResolvedJavaType type = e.getKey(); + if (type.getName().equals(internalName)) { + // The name matches so now double-check the resolved type matches + ResolvedJavaType resolved = resolvedAnnotationTypeCache.computeIfAbsent(annotationType, a -> UnresolvedJavaType.create(internalName).resolve(info.container())); + if (resolved.equals(type)) { + return e.getValue(); + } + } + } + return null; + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/EnumElement.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/EnumElement.java index 98ebeafb4586..73092b684229 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/EnumElement.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/EnumElement.java @@ -45,8 +45,12 @@ public final class EnumElement { * * @param enumType the {@linkplain Enum enum type} * @param name the {@linkplain Enum#name() name} of the enum + * @throws IllegalArgumentException if {@code enumType} is not an enum type */ public EnumElement(ResolvedJavaType enumType, String name) { + if (!enumType.isEnum()) { + throw new IllegalArgumentException(enumType.toClassName() + " is not an enum type"); + } this.enumType = enumType; this.name = name; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/bytecode/BridgeMethodUtils.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/bytecode/BridgeMethodUtils.java index 705758b75129..0f0e93e57de5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/bytecode/BridgeMethodUtils.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/bytecode/BridgeMethodUtils.java @@ -32,6 +32,8 @@ import java.lang.annotation.Annotation; +import jdk.graal.compiler.annotation.AnnotationValue; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.vm.ci.meta.ConstantPool; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -40,7 +42,7 @@ * bridge methods where the bridged methods have method annotations or parameter annotations. Not * all Java compilers copy method annotations and parameter annotations to bridge methods. * - * @see 6695379 + * @see 6695379 * @see 495396 * @see 427745 */ @@ -112,12 +114,12 @@ private static boolean assertionsEnabled() { * A helper for {@link ResolvedJavaMethod#getAnnotation(Class)} that handles the absence of * annotations on bridge methods where the bridged method has annotations. */ - public static T getAnnotation(Class annotationClass, ResolvedJavaMethod method) { - T a = method.getAnnotation(annotationClass); + public static AnnotationValue getAnnotation(Class annotationClass, ResolvedJavaMethod method) { + AnnotationValue a = AnnotationValueSupport.getAnnotationValue(method, annotationClass); if (a == null && method.isBridge()) { ResolvedJavaMethod bridged = getBridgedMethod(method); if (bridged != null) { - a = bridged.getAnnotation(annotationClass); + a = AnnotationValueSupport.getAnnotationValue(bridged, annotationClass); } } return a; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotBytecodeParser.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotBytecodeParser.java index 3ee2949d410d..5e060feed21c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotBytecodeParser.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotBytecodeParser.java @@ -24,6 +24,7 @@ */ package jdk.graal.compiler.hotspot; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.api.replacements.Snippet; import jdk.graal.compiler.core.common.LibGraalSupport; import jdk.graal.compiler.core.common.PermanentBailoutException; @@ -84,7 +85,7 @@ protected boolean applyInvocationPlugin(CallTargetNode.InvokeKind invokeKind, Va if (plugin instanceof GeneratedNodeIntrinsicInvocationPlugin nodeIntrinsicPlugin) { // Snippets are never parsed in libgraal, and they are the root of the compilation // in jargraal, so check the root method for the Snippet annotation. - if (LibGraalSupport.inLibGraalRuntime() || graph.method().getAnnotation(Snippet.class) == null) { + if (LibGraalSupport.inLibGraalRuntime() || AnnotationValueSupport.getAnnotationValue(graph.method(), Snippet.class) == null) { throw new PermanentBailoutException(BAD_NODE_INTRINSIC_PLUGIN_CONTEXT + nodeIntrinsicPlugin.getSource().getSimpleName()); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java index 502f4d23f93e..c1db0ba14031 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotReplacementsImpl.java @@ -32,6 +32,7 @@ import org.graalvm.collections.EconomicSet; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.bytecode.Bytecode; import jdk.graal.compiler.bytecode.BytecodeProvider; import jdk.graal.compiler.bytecode.ResolvedJavaMethodBytecode; @@ -106,7 +107,7 @@ public SymbolicSnippetEncoder maybeInitializeEncoder() { @Override public Class getIntrinsifyingPlugin(ResolvedJavaMethod method) { if (!inLibGraalRuntime()) { - if (method.getAnnotation(HotSpotOperation.class) != null) { + if (AnnotationValueSupport.getAnnotationValue(method, HotSpotOperation.class) != null) { return HotSpotWordOperationPlugin.class; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java index 72c4ce6440fb..fb6c49df79dc 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java @@ -43,6 +43,7 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.api.replacements.Snippet; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; @@ -224,7 +225,7 @@ protected class SnippetInlineInvokePlugin implements InlineInvokePlugin { @Override public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { - if (method.getAnnotation(Fold.class) != null) { + if (AnnotationValueSupport.getAnnotationValue(method, Fold.class) != null) { delayedInvocationPluginMethods.add(method); return InlineInfo.DO_NOT_INLINE_NO_EXCEPTION; } @@ -235,7 +236,7 @@ public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod m @Override public void notifyAfterInline(ResolvedJavaMethod methodToInline) { - assert methodToInline.getAnnotation(Fold.class) == null : methodToInline; + assert AnnotationValueSupport.getAnnotationValue(methodToInline, Fold.class) == null : methodToInline; } } @@ -430,7 +431,7 @@ public synchronized EncodedSnippets encodeSnippets(OptionValues options) { } synchronized void registerSnippet(ResolvedJavaMethod method, ResolvedJavaMethod original, Object receiver, boolean trackNodeSourcePosition) { - assert method.getAnnotation(Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName(); + assert AnnotationValueSupport.getAnnotationValue(method, Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName(); SnippetKey key = new SnippetKey(method, original, receiver); findSnippetMethod(method); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java index 12ac9874bbdc..a814cf88b2ca 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java @@ -72,7 +72,6 @@ import static jdk.graal.compiler.replacements.StandardGraphBuilderPlugins.VectorizedHashCodeInvocationPlugin.T_LONG; import static jdk.vm.ci.meta.DeoptimizationReason.TypeCheckedInliningViolated; -import java.lang.annotation.Annotation; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MutableCallSite; import java.lang.invoke.VolatileCallSite; @@ -85,6 +84,7 @@ import org.graalvm.word.LocationIdentity; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.api.directives.GraalDirectives; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.graal.compiler.core.common.LibGraalSupport; @@ -698,9 +698,10 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec }); } + @LibGraalSupport.HostedOnly private static boolean isAnnotatedByChangesCurrentThread(ResolvedJavaMethod method) { - for (Annotation annotation : method.getAnnotations()) { - if ("jdk.internal.vm.annotation.ChangesCurrentThread".equals(annotation.annotationType().getName())) { + for (ResolvedJavaType annotationType : AnnotationValueSupport.getDeclaredAnnotationValues(method).keySet()) { + if ("jdk.internal.vm.annotation.ChangesCurrentThread".equals(annotationType.toClassName())) { return true; } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotWordOperationPlugin.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotWordOperationPlugin.java index 15b068375ee1..4e01849c658a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotWordOperationPlugin.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotWordOperationPlugin.java @@ -31,6 +31,7 @@ import org.graalvm.word.LocationIdentity; +import jdk.graal.compiler.annotation.AnnotationValue; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.graal.compiler.bytecode.BridgeMethodUtils; import jdk.graal.compiler.core.common.memory.BarrierType; @@ -92,27 +93,26 @@ public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, Va return false; } - HotSpotOperation operation = BridgeMethodUtils.getAnnotation(HotSpotOperation.class, method); + AnnotationValue operation = BridgeMethodUtils.getAnnotation(HotSpotOperation.class, method); if (operation == null) { processWordOperation(b, args, wordTypes.getWordOperation(method, b.getMethod().getDeclaringClass())); return true; } - processHotSpotWordOperation(b, method, args, operation); + HotspotOpcode opcode = operation.getEnum(HotspotOpcode.class, "opcode"); + processHotSpotWordOperation(b, method, args, opcode); return true; } - protected void processHotSpotWordOperation(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, HotSpotOperation operation) { + protected void processHotSpotWordOperation(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, HotspotOpcode opcode) { JavaKind returnKind = method.getSignature().getReturnKind(); - switch (operation.opcode()) { + switch (opcode) { case POINTER_EQ: case POINTER_NE: assert args.length == 2 : args; - HotspotOpcode opcode = operation.opcode(); ValueNode left = args[0]; ValueNode right = args[1]; assert left.stamp(NodeView.DEFAULT) instanceof MetaspacePointerStamp : left + " " + left.stamp(NodeView.DEFAULT); assert right.stamp(NodeView.DEFAULT) instanceof MetaspacePointerStamp : right + " " + right.stamp(NodeView.DEFAULT); - assert opcode == POINTER_EQ || opcode == POINTER_NE : opcode; PointerEqualsNode comparison = b.add(new PointerEqualsNode(left, right)); ValueNode eqValue = b.add(forBoolean(opcode == POINTER_EQ)); @@ -165,7 +165,7 @@ protected void processHotSpotWordOperation(GraphBuilderContext b, ResolvedJavaMe break; default: - throw GraalError.shouldNotReachHere("unknown operation: " + operation.opcode()); // ExcludeFromJacocoGeneratedReport + throw GraalError.shouldNotReachHere("unknown operation: " + opcode); // ExcludeFromJacocoGeneratedReport } } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BytecodeParser.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BytecodeParser.java index dcac57af9c5e..e488d499f86a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BytecodeParser.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BytecodeParser.java @@ -273,6 +273,7 @@ import org.graalvm.collections.Equivalence; import org.graalvm.word.LocationIdentity; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.api.replacements.Snippet; import jdk.graal.compiler.bytecode.Bytecode; @@ -2752,7 +2753,7 @@ protected final void notifyAfterInline(ResolvedJavaMethod inlinedMethod) { protected boolean canInlinePartialIntrinsicExit() { assert !inRuntimeCode(); return InlinePartialIntrinsicExitDuringParsing.getValue(options) && !inBuildtimeCode() && - method.getAnnotation(Snippet.class) == null; + AnnotationValueSupport.getAnnotationValue(method, Snippet.class) == null; } private void printInlining(ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean success, String msg) { @@ -2947,8 +2948,8 @@ protected void genReturn(ValueNode returnVal, JavaKind returnKind) { ResolvedJavaMethod targetMethod = ((Invoke) stateSplit).getTargetMethod(); if (!inRuntimeCode()) { GraalError.guarantee(targetMethod != null, "%s has null target method", stateSplit); - GraalError.guarantee(targetMethod.getAnnotation(Fold.class) != null || - targetMethod.getAnnotation(Node.NodeIntrinsic.class) != null, + GraalError.guarantee(AnnotationValueSupport.getAnnotationValue(targetMethod, Fold.class) != null || + AnnotationValueSupport.getAnnotationValue(targetMethod, Node.NodeIntrinsic.class) != null, "Target should be fold or intrinsic ", targetMethod); } state = new FrameState(BytecodeFrame.AFTER_BCI); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/LIRInstructionClass.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/LIRInstructionClass.java index 1892f252d315..75b0085318ec 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/LIRInstructionClass.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/LIRInstructionClass.java @@ -148,8 +148,9 @@ protected EnumSet getFlags(Field field) { @Override public void scan(Class startSubclass, Class endSuperclass) { - if (startSubclass.getAnnotation(Opcode.class) != null) { - opcodeConstant = startSubclass.getAnnotation(Opcode.class).value(); + Opcode opcode = startSubclass.getAnnotation(Opcode.class); + if (opcode != null) { + opcodeConstant = opcode.value(); } opcodeField = null; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodeinfo/StructuralInput.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodeinfo/StructuralInput.java index 13236ef76d5c..749f81a8a28c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodeinfo/StructuralInput.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodeinfo/StructuralInput.java @@ -39,7 +39,8 @@ private StructuralInput() { throw new Error("Illegal instance of StructuralInput. This class should be used in snippets only."); } - @Retention(RetentionPolicy.RUNTIME) + // Only read by an annotation processor. + @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) @Inherited public @interface MarkerType { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/SnippetParameterInfo.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/SnippetParameterInfo.java index 9c34940c829c..9ccb09594959 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/SnippetParameterInfo.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/SnippetParameterInfo.java @@ -30,10 +30,11 @@ import java.util.BitSet; import java.util.List; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.api.replacements.Snippet; import jdk.graal.compiler.api.replacements.Snippet.ConstantParameter; -import jdk.graal.compiler.api.replacements.Snippet.VarargsParameter; import jdk.graal.compiler.api.replacements.Snippet.NonNullParameter; +import jdk.graal.compiler.api.replacements.Snippet.VarargsParameter; import jdk.graal.compiler.debug.Assertions; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.Local; @@ -49,7 +50,7 @@ public final class SnippetParameterInfo { public SnippetParameterInfo(ResolvedJavaMethod method) { - assert method.getAnnotation(Snippet.class) != null : method + " must be annotated with @" + Snippet.class.getSimpleName(); + assert AnnotationValueSupport.getAnnotationValue(method, Snippet.class) != null : method + " must be annotated with @" + Snippet.class.getSimpleName(); int parameterCount = method.getSignature().getParameterCount(method.hasReceiver()); if (parameterCount > Integer.SIZE) { throw new UnsupportedOperationException("too many arguments"); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/UncheckedInterfaceProvider.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/UncheckedInterfaceProvider.java index 75ba1a25b715..305d16194ed5 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/UncheckedInterfaceProvider.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/spi/UncheckedInterfaceProvider.java @@ -29,8 +29,8 @@ public interface UncheckedInterfaceProvider { /** * Returns a stamp containing information about interface types that has not been verified or - * null if no such stamp is available. A type check is needed before using informations from - * this stamp. + * null if no such stamp is available. A type check is needed before using information from this + * stamp. */ Stamp uncheckedStamp(); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/ReplacementsImpl.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/ReplacementsImpl.java index f0f4b9db2e37..b249841f2771 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/ReplacementsImpl.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/ReplacementsImpl.java @@ -43,6 +43,8 @@ import org.graalvm.collections.Equivalence; import org.graalvm.collections.Pair; +import jdk.graal.compiler.annotation.AnnotationValue; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.api.replacements.Snippet; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; @@ -183,10 +185,10 @@ public Stamp getInjectedStamp(Class type) { @Override public Class getIntrinsifyingPlugin(ResolvedJavaMethod method) { if (!inRuntimeCode()) { - if (method.getAnnotation(Node.NodeIntrinsic.class) != null || method.getAnnotation(Fold.class) != null) { + if (AnnotationValueSupport.getAnnotationValue(method, NodeIntrinsic.class) != null || AnnotationValueSupport.getAnnotationValue(method, Fold.class) != null) { return GeneratedInvocationPlugin.class; } - if (method.getAnnotation(Word.Operation.class) != null) { + if (AnnotationValueSupport.getAnnotationValue(method, Word.Operation.class) != null) { return WordOperationPlugin.class; } } @@ -210,7 +212,7 @@ public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod m // Force inlining when parsing replacements return createIntrinsicInlineInfo(method, defaultBytecodeProvider); } else { - assert inRuntimeCode() || method.getAnnotation(NodeIntrinsic.class) == null : String.format("@%s method %s must only be called from within a replacement%n%s", + assert inRuntimeCode() || AnnotationValueSupport.getAnnotationValue(method, NodeIntrinsic.class) == null : String.format("@%s method %s must only be called from within a replacement%n%s", NodeIntrinsic.class.getSimpleName(), method.format("%h.%n"), b); } @@ -283,7 +285,7 @@ private DebugContext openDebugContext(String idPrefix, ResolvedJavaMethod method @SuppressWarnings("try") public StructuredGraph getSnippet(ResolvedJavaMethod method, ResolvedJavaMethod recursiveEntry, Object[] args, BitSet nonNullParameters, boolean trackNodeSourcePosition, NodeSourcePosition replaceePosition, OptionValues options) { - assert method.getAnnotation(Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName(); + assert AnnotationValueSupport.getAnnotationValue(method, Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName(); assert method.hasBytecodes() : "Snippet must not be abstract or native"; Pair cacheKey = Pair.create(method, options); @@ -316,7 +318,7 @@ public SnippetParameterInfo getSnippetParameterInfo(ResolvedJavaMethod method) { @Override public boolean isSnippet(ResolvedJavaMethod method) { - return method.getAnnotation(Snippet.class) != null; + return AnnotationValueSupport.getAnnotationValue(method, Snippet.class) != null; } @Override @@ -382,7 +384,9 @@ public final StructuredGraph makeGraph(DebugContext debug, BytecodeProvider byte */ public abstract static class GraphMaker { - /** The replacements object that the graphs are created for. */ + /** + * The replacements object that the graphs are created for. + */ protected final ReplacementsImpl replacements; /** @@ -485,7 +489,7 @@ public boolean isDeferredInvoke(StateSplit stateSplit) { if (method == null) { return false; } - if (method.getAnnotation(Fold.class) != null) { + if (AnnotationValueSupport.getAnnotationValue(method, Fold.class) != null) { /* * In SVM, @Fold methods cannot be handled eagerly, e.g., * because @ConstantParameter arguments are not yet constant. Thus, the @@ -494,10 +498,8 @@ public boolean isDeferredInvoke(StateSplit stateSplit) { */ return true; } - Node.NodeIntrinsic annotation = method.getAnnotation(Node.NodeIntrinsic.class); - if (annotation != null && !annotation.hasSideEffect()) { - return true; - } + AnnotationValue annotation = AnnotationValueSupport.getAnnotationValue(method, NodeIntrinsic.class); + return annotation != null && !annotation.getBoolean("hasSideEffect"); } return false; } @@ -537,7 +539,7 @@ protected StructuredGraph buildInitialGraph(DebugContext debug, BytecodeProvider } IntrinsicContext initialIntrinsicContext = null; - Snippet snippetAnnotation = !inRuntimeCode() ? method.getAnnotation(Snippet.class) : null; + AnnotationValue snippetAnnotation = AnnotationValueSupport.getAnnotationValue(method, Snippet.class); if (snippetAnnotation == null) { // Post-parse inlined intrinsic initialIntrinsicContext = new EncodedIntrinsicContext(substitutedMethod, method, bytecodeProvider, context, false); @@ -545,7 +547,7 @@ protected StructuredGraph buildInitialGraph(DebugContext debug, BytecodeProvider // Snippet ResolvedJavaMethod original = substitutedMethod != null ? substitutedMethod : method; initialIntrinsicContext = new EncodedIntrinsicContext(original, method, bytecodeProvider, context, - snippetAnnotation.allowPartialIntrinsicArgumentMismatch()); + snippetAnnotation.getBoolean("allowPartialIntrinsicArgumentMismatch")); } createGraphBuilder(replacements.providers, config, OptimisticOptimizations.NONE, initialIntrinsicContext).apply(graph); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/SnippetTemplate.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/SnippetTemplate.java index 638869f728d6..0b2a40e7876a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/SnippetTemplate.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/SnippetTemplate.java @@ -56,6 +56,7 @@ import org.graalvm.word.LocationIdentity; import org.graalvm.word.WordBase; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.api.replacements.Snippet; import jdk.graal.compiler.api.replacements.Snippet.ConstantParameter; @@ -1513,7 +1514,8 @@ private static boolean verifyIntrinsicsProcessed(StructuredGraph snippetCopy) { for (MethodCallTargetNode target : snippetCopy.getNodes(MethodCallTargetNode.TYPE)) { ResolvedJavaMethod targetMethod = target.targetMethod(); if (targetMethod != null) { - assert targetMethod.getAnnotation(Fold.class) == null && targetMethod.getAnnotation(NodeIntrinsic.class) == null : "plugin should have been processed"; + assert AnnotationValueSupport.getAnnotationValue(targetMethod, Fold.class) == null && + AnnotationValueSupport.getAnnotationValue(targetMethod, NodeIntrinsic.class) == null : "plugin should have been processed"; } } return true; diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/word/WordOperationPlugin.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/word/WordOperationPlugin.java index f31368b1649a..c6d862aa770c 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/word/WordOperationPlugin.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/word/WordOperationPlugin.java @@ -32,8 +32,10 @@ import java.util.Arrays; import org.graalvm.word.LocationIdentity; +import org.graalvm.word.impl.WordFactoryOpcode; import org.graalvm.word.impl.WordFactoryOperation; +import jdk.graal.compiler.annotation.AnnotationValue; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.graal.compiler.bytecode.BridgeMethodUtils; import jdk.graal.compiler.core.common.NumUtil; @@ -264,9 +266,10 @@ public boolean handleInstanceOf(GraphBuilderContext b, ValueNode object, Resolve protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, ResolvedJavaMethod wordMethod) throws GraalError { JavaKind returnKind = wordMethod.getSignature().getReturnKind(); - WordFactoryOperation factoryOperation = BridgeMethodUtils.getAnnotation(WordFactoryOperation.class, wordMethod); + AnnotationValue factoryOperation = BridgeMethodUtils.getAnnotation(WordFactoryOperation.class, wordMethod); if (factoryOperation != null) { - switch (factoryOperation.opcode()) { + WordFactoryOpcode opcode = factoryOperation.getEnum(WordFactoryOpcode.class, "opcode"); + switch (opcode) { case ZERO: assert NumUtil.assertArrayLength(args, 0); b.addPush(returnKind, forIntegerKind(wordKind, 0L)); @@ -284,23 +287,25 @@ protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, Res } } - Word.Operation operation = BridgeMethodUtils.getAnnotation(Word.Operation.class, wordMethod); + AnnotationValue operation = BridgeMethodUtils.getAnnotation(Word.Operation.class, wordMethod); if (operation == null) { throw bailout(b, "Cannot call method on a word value: " + wordMethod.format("%H.%n(%p)")); } - switch (operation.opcode()) { + Opcode opcode = operation.getEnum(Opcode.class, "opcode"); + switch (opcode) { case NODE_CLASS: case INTEGER_DIVISION_NODE_CLASS: assert NumUtil.assertArrayLength(args, 2); ValueNode left = args[0]; - ValueNode right = operation.rightOperandIsInt() ? toUnsigned(b, args[1], JavaKind.Int) : fromSigned(b, args[1]); - - b.addPush(returnKind, createBinaryNodeInstance(b, operation.node(), left, right, operation.opcode() == Word.Opcode.INTEGER_DIVISION_NODE_CLASS)); + ValueNode right = operation.getBoolean("rightOperandIsInt") ? toUnsigned(b, args[1], JavaKind.Int) : fromSigned(b, args[1]); + ResolvedJavaType nodeType = operation.getType("node"); + b.addPush(returnKind, createBinaryNodeInstance(b, nodeType, left, right, opcode == Word.Opcode.INTEGER_DIVISION_NODE_CLASS)); break; case COMPARISON: assert NumUtil.assertArrayLength(args, 2); - b.push(returnKind, comparisonOp(b, operation.condition(), args[0], fromSigned(b, args[1]))); + Condition condition = operation.getEnum(Condition.class, "condition"); + b.push(returnKind, comparisonOp(b, condition, args[0], fromSigned(b, args[1]))); break; case IS_NULL: @@ -332,7 +337,7 @@ protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, Res location = snippetReflection.asObject(LocationIdentity.class, args[2].asJavaConstant()); assert location != null : snippetReflection.asObject(Object.class, args[2].asJavaConstant()); } - b.push(returnKind, readOp(b, readKind, address, location, operation.opcode())); + b.push(returnKind, readOp(b, readKind, address, location, opcode)); break; } @@ -349,7 +354,7 @@ protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, Res location = snippetReflection.asObject(LocationIdentity.class, args[2].asJavaConstant()); assert location != null : snippetReflection.asObject(Object.class, args[2].asJavaConstant()); } - b.push(returnKind, readVolatileOp(b, readKind, address, location, operation.opcode())); + b.push(returnKind, readVolatileOp(b, readKind, address, location, opcode)); break; } case READ_HEAP: { @@ -384,7 +389,7 @@ protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, Res assert GraphUtil.assertIsConstant(args[3]); location = snippetReflection.asObject(LocationIdentity.class, args[3].asJavaConstant()); } - writeOp(b, writeKind, address, location, args[2], operation.opcode()); + writeOp(b, writeKind, address, location, args[2], opcode); break; } @@ -446,7 +451,7 @@ protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, Res b.addPush(returnKind, casOp(valueKind, wordTypes.asKind(returnType), address, location, args[2], args[3])); break; default: - throw new GraalError("Unknown opcode: %s", operation.opcode()); + throw new GraalError("Unknown opcode: %s", opcode); } } @@ -455,15 +460,17 @@ protected void processWordOperation(GraphBuilderContext b, ValueNode[] args, Res * method is called for all {@link Word} operations which are annotated with @Operation(node = * ...) and encapsulates the reflective allocation of the node. */ - private static ValueNode createBinaryNodeInstance(GraphBuilderContext b, Class nodeClass, ValueNode left, ValueNode right, boolean isIntegerDivision) { + @SuppressWarnings("unchecked") + private static ValueNode createBinaryNodeInstance(GraphBuilderContext b, ResolvedJavaType nodeType, ValueNode left, ValueNode right, boolean isIntegerDivision) { try { + Class nodeClass = (Class) Class.forName(nodeType.toClassName()); GuardingNode zeroCheck = isIntegerDivision ? b.maybeEmitExplicitDivisionByZeroCheck(right) : null; Class[] parameterTypes = isIntegerDivision ? new Class[]{ValueNode.class, ValueNode.class, GuardingNode.class} : new Class[]{ValueNode.class, ValueNode.class}; Constructor cons = nodeClass.getDeclaredConstructor(parameterTypes); Object[] initargs = isIntegerDivision ? new Object[]{left, right, zeroCheck} : new Object[]{left, right}; return (ValueNode) cons.newInstance(initargs); } catch (Throwable ex) { - throw new GraalError(ex).addContext(nodeClass.getName()); + throw new GraalError(ex).addContext(nodeType.toClassName()); } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/word/WordTypes.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/word/WordTypes.java index 6e9e4676b336..169a63a5f2ec 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/word/WordTypes.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/word/WordTypes.java @@ -24,6 +24,10 @@ */ package jdk.graal.compiler.word; +import org.graalvm.word.WordBase; +import org.graalvm.word.impl.WordFactoryOperation; + +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.core.common.Fields; import jdk.graal.compiler.core.common.type.AbstractObjectStamp; import jdk.graal.compiler.core.common.type.Stamp; @@ -32,14 +36,11 @@ import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.nodes.ValueNode; import jdk.graal.compiler.nodes.type.StampTool; -import org.graalvm.word.WordBase; - import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -import org.graalvm.word.impl.WordFactoryOperation; /** * Encapsulates information for Java types representing raw words (as opposed to Objects). @@ -82,14 +83,14 @@ public WordTypes(MetaAccessProvider metaAccess, JavaKind wordKind) { * Determines if a given method denotes a word operation. */ public boolean isWordOperation(ResolvedJavaMethod targetMethod) { - if (targetMethod.getAnnotation(WordFactoryOperation.class) != null) { + if (AnnotationValueSupport.getAnnotationValue(targetMethod, WordFactoryOperation.class) != null) { return true; } final boolean isObjectAccess = objectAccessType.equals(targetMethod.getDeclaringClass()); final boolean isBarrieredAccess = barrieredAccessType.equals(targetMethod.getDeclaringClass()); if (isObjectAccess || isBarrieredAccess) { - assert targetMethod.getAnnotation(Word.Operation.class) != null : targetMethod + " should be annotated with @" + Word.Operation.class.getSimpleName(); + assert AnnotationValueSupport.getAnnotationValue(targetMethod, Word.Operation.class) != null : targetMethod + " should be annotated with @" + Word.Operation.class.getSimpleName(); return true; } return isWord(targetMethod.getDeclaringClass()); diff --git a/sdk/src/org.graalvm.collections/src/org/graalvm/collections/EconomicMapImpl.java b/sdk/src/org.graalvm.collections/src/org/graalvm/collections/EconomicMapImpl.java index a215ea49a1a1..2df5a32b3ff9 100644 --- a/sdk/src/org.graalvm.collections/src/org/graalvm/collections/EconomicMapImpl.java +++ b/sdk/src/org.graalvm.collections/src/org/graalvm/collections/EconomicMapImpl.java @@ -48,23 +48,23 @@ * Implementation of a map with a memory-efficient structure that always preserves insertion order * when iterating over keys. Particularly efficient when number of entries is 0 or smaller equal * {@link #INITIAL_CAPACITY} or smaller 256. - * + *

* The key/value pairs are kept in an expanding flat object array with keys at even indices and * values at odd indices. If the map has smaller or equal to {@link #HASH_THRESHOLD} entries, there * is no additional hash data structure and comparisons are done via linear checking of the * key/value pairs. For the case where the equality check is particularly cheap (e.g., just an * object identity comparison), this limit below which the map is without an actual hash table is * higher and configured at {@link #HASH_THRESHOLD_IDENTITY_COMPARE}. - * + *

* When the hash table needs to be constructed, the field {@link #hashArray} becomes a new hash * array where an entry of 0 means no hit and otherwise denotes the entry number in the * {@link #entries} array. The hash array is interpreted as an actual byte array if the indices fit * within 8 bit, or as an array of short values if the indices fit within 16 bit, or as an array of * integer values in other cases. - * + *

* Hash collisions are handled by chaining a linked list of {@link CollisionLink} objects that take * the place of the values in the {@link #entries} array. - * + *

* Removing entries will put {@code null} into the {@link #entries} array. If the occupation of the * map falls below a specific threshold, the map will be compressed via the * {@link #maybeCompress(int)} method. @@ -355,8 +355,7 @@ private int findAndRemoveHash(Object key) { if (compareKeys(key, entryKey)) { Object value = getRawValue(index); int nextIndex = -1; - if (value instanceof CollisionLink) { - CollisionLink collisionLink = (CollisionLink) value; + if (value instanceof CollisionLink collisionLink) { nextIndex = collisionLink.next; } setHashArray(hashIndex, nextIndex + 1); @@ -383,8 +382,7 @@ private int findAndRemoveWithCollision(Object key, CollisionLink initialEntryVal entryKey = getKey(index); if (compareKeys(key, entryKey)) { Object value = getRawValue(index); - if (value instanceof CollisionLink) { - CollisionLink thisCollisionLink = (CollisionLink) value; + if (value instanceof CollisionLink thisCollisionLink) { setRawValue(lastIndex, new CollisionLink(collisionLink.value, thisCollisionLink.next)); } else { setRawValue(lastIndex, collisionLink.value); @@ -581,16 +579,16 @@ private void putHashEntry(Object key, int entryIndex, boolean rehashOnCollision) setHashArray(hashIndex, entryIndex + 1); Object value = getRawValue(entryIndex); if (oldIndex != -1) { - assert entryIndex != oldIndex : "this cannot happen and would create an endless collision link cycle"; - if (value instanceof CollisionLink) { - CollisionLink collisionLink = (CollisionLink) value; + if (entryIndex == oldIndex) { + throw new InternalError("endless collision link cycle, most likely due to unsynchronized concurrent access"); + } + if (value instanceof CollisionLink collisionLink) { setRawValue(entryIndex, new CollisionLink(collisionLink.value, oldIndex)); } else { setRawValue(entryIndex, new CollisionLink(getRawValue(entryIndex), oldIndex)); } } else { - if (value instanceof CollisionLink) { - CollisionLink collisionLink = (CollisionLink) value; + if (value instanceof CollisionLink collisionLink) { setRawValue(entryIndex, collisionLink.value); } } @@ -700,6 +698,7 @@ public void remove() { } } + @SuppressWarnings({"rawtypes", "unchecked"}) @Override public Iterable getValues() { return new Iterable<>() { @@ -811,8 +810,7 @@ private void setKey(int index, Object newValue) { private void setValue(int index, Object newValue) { Object oldValue = getRawValue(index); - if (oldValue instanceof CollisionLink) { - CollisionLink collisionLink = (CollisionLink) oldValue; + if (oldValue instanceof CollisionLink collisionLink) { setRawValue(index, new CollisionLink(newValue, collisionLink.next)); } else { setRawValue(index, newValue); @@ -859,6 +857,7 @@ static String toString(boolean isSet, int size, MapCursor cursor) { return builder.toString(); } + @SuppressWarnings({"rawtypes", "unchecked"}) @Override public Iterator iterator() { return new SparseMapIterator<>() { diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 011000446f03..12f412a8086e 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -606,6 +606,7 @@ ], "jdk.internal.vm.ci" : [ "jdk.vm.ci.meta", + "jdk.vm.ci.meta.annotation", "jdk.vm.ci.code", "jdk.vm.ci.common", "jdk.vm.ci.runtime" @@ -618,6 +619,10 @@ ], "workingSets": "SVM", "jacoco" : "include", + + # Direct reference to jdk.vm.ci.meta.annotation.Annotated + # causes spotbugs analysis to fail with "missing class" error. + "spotbugs": "false", }, "com.oracle.graal.pointsto.standalone": { @@ -634,7 +639,8 @@ "jdk.vm.ci.code", "jdk.vm.ci.amd64", "jdk.vm.ci.hotspot", - "jdk.vm.ci.meta" + "jdk.vm.ci.meta", + "jdk.vm.ci.meta.annotation" ] }, "checkstyle": "com.oracle.svm.core", @@ -682,6 +688,7 @@ "requiresConcealed": { "jdk.internal.vm.ci": [ "jdk.vm.ci.meta", + "jdk.vm.ci.meta.annotation", "jdk.vm.ci.code", ] }, @@ -1275,7 +1282,8 @@ "jdk.vm.ci.code", "jdk.vm.ci.code.stack", "jdk.vm.ci.hotspot", - "jdk.vm.ci.meta" + "jdk.vm.ci.meta", + "jdk.vm.ci.meta.annotation" ], }, "checkstyle": "com.oracle.svm.hosted", diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java index a7d16307ea50..9566599ad60b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisElement.java @@ -55,6 +55,8 @@ import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.annotation.Annotated; +import jdk.vm.ci.meta.annotation.AnnotationsInfo; public abstract class AnalysisElement implements AnnotatedElement { @@ -74,6 +76,14 @@ protected AnalysisElement(boolean enableTrackAcrossLayers) { protected abstract AnalysisUniverse getUniverse(); + public AnnotationsInfo getDeclaredAnnotationInfo() { + return ((Annotated) getWrapped()).getDeclaredAnnotationInfo(); + } + + public AnnotationsInfo getTypeAnnotationInfo() { + return ((Annotated) getWrapped()).getTypeAnnotationInfo(); + } + @Override public final boolean isAnnotationPresent(Class annotationClass) { return getUniverse().getAnnotationExtractor().hasAnnotation(getWrapped(), annotationClass); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/substitutions/GraalSubstitutions.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/substitutions/GraalSubstitutions.java index fc9f2b8dc83b..84ee13a5b619 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/substitutions/GraalSubstitutions.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/substitutions/GraalSubstitutions.java @@ -30,8 +30,10 @@ import java.io.IOException; import java.io.PrintStream; +import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; import org.graalvm.collections.EconomicMap; @@ -63,6 +65,8 @@ import com.oracle.svm.graal.meta.SubstrateMethod; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.annotation.AnnotationValue; +import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.core.common.CompilationIdentifier; import jdk.graal.compiler.core.gen.NodeLIRBuilder; import jdk.graal.compiler.core.match.MatchRuleRegistry; @@ -92,6 +96,8 @@ import jdk.graal.compiler.replacements.nodes.UnaryMathIntrinsicNode; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.annotation.Annotated; @TargetClass(value = InvocationPlugins.class, onlyWith = RuntimeCompilationFeature.IsEnabled.class) final class Target_jdk_graal_compiler_nodes_graphbuilderconf_InvocationPlugins { @@ -209,6 +215,18 @@ final class Target_jdk_graal_compiler_debug_TTY { private static PrintStream out = Log.logStream(); } +@TargetClass(value = AnnotationValueSupport.class, onlyWith = RuntimeCompilationFeature.IsEnabled.class) +final class Target_jdk_graal_compiler_annotation_AnnotationValueSupport { + + @Alias// + @RecomputeFieldValue(kind = Reset)// + private static Map> declaredAnnotations; + + @Alias// + @RecomputeFieldValue(kind = Reset)// + private static Map, ResolvedJavaType> resolvedAnnotationTypeCache; +} + @TargetClass(className = "jdk.graal.compiler.serviceprovider.IsolateUtil", onlyWith = GraalCompilerFeature.IsEnabled.class) final class Target_jdk_graal_compiler_serviceprovider_IsolateUtil { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageSingletonsSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageSingletonsSupportImpl.java index f694df379c4b..bcbf20c26485 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageSingletonsSupportImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageSingletonsSupportImpl.java @@ -44,6 +44,7 @@ import org.graalvm.nativeimage.impl.AnnotationExtractor; import org.graalvm.nativeimage.impl.ImageSingletonsSupport; +import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport; @@ -430,7 +431,9 @@ private void addSingleton(Class key, Object value) { traitMap.getTrait(SingletonTraitKind.LAYERED_INSTALLATION_KIND).ifPresent(trait -> { var kind = SingletonLayeredInstallationKind.getInstallationKind(trait); if (forbiddenInstallationKinds.contains(kind)) { - throw VMError.shouldNotReachHere("Singleton with installation kind %s can no longer be added: %s", kind, value); + if (SubstrateOptions.LayerOptionVerification.getValue()) { + throw VMError.shouldNotReachHere("Singleton with installation kind %s can no longer be added: %s", kind, value); + } } }); } @@ -633,10 +636,10 @@ public SingletonTraitMap getUninstalledSingletonTraitMap(Class key) { var installationKindSupplierClass = annotation.layeredInstallationKind(); /* * Initial Layer information should never be injected, as either - * + * * 1) We are building the initial layer, so it should be present in the * configObject which it exists. - * + * * 2) We are building an extension layer, so it is not relevant for this * layer. */ diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeBuilder.java index 70e7d625a2f9..58e138aaa30c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/info/InfoTreeBuilder.java @@ -56,7 +56,6 @@ import com.oracle.graal.pointsto.infrastructure.WrappedJavaType; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.svm.util.GraalAccess; import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.c.struct.PinnedObjectField; import com.oracle.svm.core.util.VMError; @@ -68,6 +67,8 @@ import com.oracle.svm.hosted.cenum.CEnumCallWrapperMethod; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.util.ClassUtil; +import com.oracle.svm.util.GraalAccess; +import com.oracle.svm.util.OriginalMethodProvider; import jdk.graal.compiler.bytecode.BridgeMethodUtils; import jdk.graal.compiler.phases.util.Providers; @@ -767,6 +768,13 @@ private static T getMethodAnnotation(ResolvedJavaMethod m * when overwriting a method with covariant return types. As a workaround, we look up the * original method and use the annotations of the original method. */ - return BridgeMethodUtils.getAnnotation(annotationClass, method); + T annotation = OriginalMethodProvider.getJavaMethod(method).getAnnotation(annotationClass); + if (annotation == null && method.isBridge()) { + ResolvedJavaMethod bridged = BridgeMethodUtils.getBridgedMethod(method); + if (bridged != null) { + annotation = OriginalMethodProvider.getJavaMethod(bridged).getAnnotation(annotationClass); + } + } + return annotation; } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java index 9bc49096868e..2ad17ee07bfb 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java @@ -69,6 +69,7 @@ import com.oracle.svm.hosted.option.HostedOptionParser; import com.oracle.svm.shaded.org.capnproto.ReaderOptions; import com.oracle.svm.shaded.org.capnproto.Serialize; +import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.TypeResult; import jdk.graal.compiler.core.common.SuppressFBWarnings; @@ -324,8 +325,12 @@ public static HostedImageLayerBuildingSupport initialize(HostedOptionValues valu ValueWithOrigin valueWithOrigin = getLayerCreateValueWithOrigin(values); String layerCreateValue = getLayerCreateValue(valueWithOrigin); String layerCreateArg = SubstrateOptionsParser.commandArgument(SubstrateOptions.LayerCreate, layerCreateValue); - throw UserError.abort("Layer creation option '%s' from %s is not supported when building for platform %s/%s.", + String message = String.format("Layer creation option '%s' from %s is not supported when building for platform %s/%s.", layerCreateArg, valueWithOrigin.origin(), platform.getOS(), platform.getArchitecture()); + if (SubstrateOptions.LayerOptionVerification.getValue(values)) { + throw UserError.abort("%s", message); + } + LogUtils.warning(message); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java index f68dac7f77b2..52ac11b576bd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java @@ -85,7 +85,6 @@ import com.oracle.graal.pointsto.heap.ImageHeapObjectArray; import com.oracle.graal.pointsto.heap.ImageHeapPrimitiveArray; import com.oracle.graal.pointsto.heap.ImageHeapRelocatableConstant; -import com.oracle.svm.util.OriginalFieldProvider; import com.oracle.graal.pointsto.meta.AnalysisElement; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; @@ -173,11 +172,13 @@ import com.oracle.svm.shaded.org.capnproto.TextList; import com.oracle.svm.shaded.org.capnproto.Void; import com.oracle.svm.util.LogUtils; +import com.oracle.svm.util.OriginalFieldProvider; import jdk.graal.compiler.annotation.AnnotationValue; import jdk.graal.compiler.annotation.AnnotationValueSupport; import jdk.graal.compiler.annotation.AnnotationValueType; import jdk.graal.compiler.annotation.EnumElement; +import jdk.graal.compiler.annotation.ErrorElement; import jdk.graal.compiler.core.common.NumUtil; import jdk.graal.compiler.debug.Assertions; import jdk.graal.compiler.debug.GraalError; @@ -449,7 +450,7 @@ private void persistType(AnalysisType type, Supplier> builder) { + EconomicMap persistedMembers = EconomicMap.create(); Map members = annotation.getElements(); Map memberTypes = AnnotationValueType.getInstance(annotation.getAnnotationType()).memberTypes(); if (!members.isEmpty()) { - var list = builder.apply(members.size()); - int i = 0; for (var e : members.entrySet()) { - var b = list.get(i++); - b.setName(e.getKey()); Object v = e.getValue(); - persistAnnotationElement(v, memberTypes.get(e.getKey()), b); + if (v instanceof ErrorElement) { + /* GR-70978 will add support for error elements */ + return; + } + persistedMembers.put(e.getKey(), v); + } + } + if (!persistedMembers.isEmpty()) { + var list = builder.apply(persistedMembers.size()); + MapCursor cursor = persistedMembers.getEntries(); + for (int i = 0; cursor.advance(); i++) { + var b = list.get(i); + String name = cursor.getKey(); + b.setName(name); + Object v = cursor.getValue(); + persistAnnotationElement(v, memberTypes.get(name), b); } } } private void persistAnnotationElement(Object v, ResolvedJavaType memberType, PersistedAnnotationElement.Builder b) { if (memberType.isArray()) { - List list = (List) v; ResolvedJavaType componentType = memberType.getComponentType(); if (!componentType.isPrimitive()) { + List list = (List) v; var ba = b.initMembers(); ba.setClassName(componentType.toJavaName()); var bav = ba.initMemberValues(list.size()); @@ -814,7 +827,7 @@ private void persistAnnotationElement(Object v, ResolvedJavaType memberType, Per ba.setClassName(e.enumType.toJavaName()); ba.setName(e.name); } - default -> throw AnalysisError.shouldNotReachHere("Unknown annotation value: " + v + ", " + v.getClass()); + default -> throw AnalysisError.shouldNotReachHere("Unsupported value for annotation element: " + v + ", " + v.getClass()); } } } @@ -933,17 +946,31 @@ private static boolean requiresLateLoading(ImageHeapConstant imageHeapConstant, } private static void persistConstantPrimitiveArray(PrimitiveArray.Builder builder, JavaKind componentKind, Object array) { - assert componentKind.toJavaClass().equals(array.getClass().getComponentType()); - switch (array) { - case boolean[] a -> persistArray(a, builder::initZ, (b, i) -> b.set(i, a[i])); - case byte[] a -> persistArray(a, builder::initB, (b, i) -> b.set(i, a[i])); - case short[] a -> persistArray(a, builder::initS, (b, i) -> b.set(i, a[i])); - case char[] a -> persistArray(a, builder::initC, (b, i) -> b.set(i, (short) a[i])); - case int[] a -> persistArray(a, builder::initI, (b, i) -> b.set(i, a[i])); - case long[] a -> persistArray(a, builder::initJ, (b, i) -> b.set(i, a[i])); - case float[] a -> persistArray(a, builder::initF, (b, i) -> b.set(i, a[i])); - case double[] a -> persistArray(a, builder::initD, (b, i) -> b.set(i, a[i])); - default -> throw new IllegalArgumentException("Unsupported kind: " + componentKind); + if (array instanceof List l) { + switch (componentKind) { + case Boolean -> persistList(l, builder::initZ, (b, i) -> b.set(i, (boolean) l.get(i))); + case Byte -> persistList(l, builder::initB, (b, i) -> b.set(i, (byte) l.get(i))); + case Short -> persistList(l, builder::initS, (b, i) -> b.set(i, (short) l.get(i))); + case Char -> persistList(l, builder::initC, (b, i) -> b.set(i, (short) (char) l.get(i))); + case Int -> persistList(l, builder::initI, (b, i) -> b.set(i, (int) l.get(i))); + case Long -> persistList(l, builder::initJ, (b, i) -> b.set(i, (long) l.get(i))); + case Float -> persistList(l, builder::initF, (b, i) -> b.set(i, (float) l.get(i))); + case Double -> persistList(l, builder::initD, (b, i) -> b.set(i, (double) l.get(i))); + default -> throw new IllegalArgumentException("Unsupported kind: " + componentKind); + } + } else { + assert componentKind.toJavaClass().equals(array.getClass().getComponentType()) : "%s != %s".formatted(componentKind.toJavaClass(), array.getClass().getComponentType()); + switch (array) { + case boolean[] a -> persistArray(a, builder::initZ, (b, i) -> b.set(i, a[i])); + case byte[] a -> persistArray(a, builder::initB, (b, i) -> b.set(i, a[i])); + case short[] a -> persistArray(a, builder::initS, (b, i) -> b.set(i, a[i])); + case char[] a -> persistArray(a, builder::initC, (b, i) -> b.set(i, (short) a[i])); + case int[] a -> persistArray(a, builder::initI, (b, i) -> b.set(i, a[i])); + case long[] a -> persistArray(a, builder::initJ, (b, i) -> b.set(i, a[i])); + case float[] a -> persistArray(a, builder::initF, (b, i) -> b.set(i, a[i])); + case double[] a -> persistArray(a, builder::initD, (b, i) -> b.set(i, a[i])); + default -> throw new IllegalArgumentException("Unsupported kind: " + componentKind); + } } } @@ -956,6 +983,15 @@ private static void persistArray(A array, IntFunction } } + /** Enables concise one-liners in {@link #persistConstantPrimitiveArray}. */ + private static void persistList(List list, IntFunction init, ObjIntConsumer setter) { + int length = list.size(); + T builder = init.apply(length); + for (int i = 0; i < length; i++) { + setter.accept(builder, i); + } + } + private void persistConstantObjectData(PersistedConstant.Object.Builder builder, IntFunction valuesFunction, int size) { StructList.Builder refsBuilder = builder.initData(size); for (int i = 0; i < size; ++i) { @@ -975,7 +1011,7 @@ private void persistConstantObjectData(PersistedConstant.Object.Builder builder, private boolean maybeWriteConstant(JavaConstant constant, ConstantReference.Builder builder) { if (constant instanceof ImageHeapConstant imageHeapConstant) { - assert constantsMap.containsKey(imageHeapConstant); + assert constantsMap.containsKey(imageHeapConstant) : imageHeapConstant; var ocb = builder.initObjectConstant(); ocb.setConstantId(ImageHeapConstant.getConstantID(imageHeapConstant)); } else if (constant instanceof PrimitiveConstant primitiveConstant) { @@ -1290,7 +1326,7 @@ private static boolean nonNullEntries(List list) { @Override public void writeBoolList(String keyName, List value) { - assert nonNullEntries(value); + assert nonNullEntries(value) : value; boolean[] b = new boolean[value.size()]; for (int i = 0; i < value.size(); i++) { b[i] = value.get(i); @@ -1307,7 +1343,7 @@ public void writeInt(String keyName, int value) { @Override public void writeIntList(String keyName, List value) { - assert nonNullEntries(value); + assert nonNullEntries(value) : value; var previous = keyValueStore.put(keyName, value.stream().mapToInt(i -> i).toArray()); assert previous == null : Assertions.errorMessage(keyName, previous); } @@ -1326,7 +1362,7 @@ public void writeString(String keyName, String value) { @Override public void writeStringList(String keyName, List value) { - assert nonNullEntries(value); + assert nonNullEntries(value) : value; var previous = keyValueStore.put(keyName, value.toArray(String[]::new)); assert previous == null : Assertions.errorMessage(keyName, previous); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedElement.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedElement.java index 054340bb93b6..2c49eeffbed7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedElement.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedElement.java @@ -29,10 +29,21 @@ import com.oracle.svm.core.util.VMError; +import jdk.vm.ci.meta.annotation.Annotated; +import jdk.vm.ci.meta.annotation.AnnotationsInfo; + public abstract class HostedElement implements AnnotatedElement { protected abstract AnnotatedElement getWrapped(); + public AnnotationsInfo getDeclaredAnnotationInfo() { + return ((Annotated) getWrapped()).getDeclaredAnnotationInfo(); + } + + public AnnotationsInfo getTypeAnnotationInfo() { + return ((Annotated) getWrapped()).getTypeAnnotationInfo(); + } + @Override public final boolean isAnnotationPresent(Class annotationClass) { return getWrapped().isAnnotationPresent(annotationClass); diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GraalAccess.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GraalAccess.java index d0a0e001db1e..20d1df074935 100644 --- a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GraalAccess.java +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GraalAccess.java @@ -51,6 +51,9 @@ * {@code HotSpotResolvedObjectTypeImpl} when running on HotSpot). There are methods for going in * the opposite direction in {@link OriginalClassProvider}, {@link OriginalMethodProvider} and * {@link OriginalFieldProvider}. + *

+ * This class is also used to access the JVMCI and {@linkplain #getOriginalProviders compiler + * providers}. */ @Platforms(Platform.HOSTED_ONLY.class) public final class GraalAccess {