8303431: [JVMCI] libgraal annotation API

Reviewed-by: kvn, never, darcy
This commit is contained in:
Doug Simon 2023-04-19 16:01:57 +00:00
parent c57af319f6
commit 48fd4f2bd3
34 changed files with 2298 additions and 51 deletions

View File

@ -756,6 +756,8 @@
template(encodeThrowable_name, "encodeThrowable") \
template(encodeThrowable_signature, "(Ljava/lang/Throwable;JI)I") \
template(decodeAndThrowThrowable_name, "decodeAndThrowThrowable") \
template(encodeAnnotations_name, "encodeAnnotations") \
template(encodeAnnotations_signature, "([BLjava/lang/Class;Ljdk/internal/reflect/ConstantPool;Z[Ljava/lang/Class;)[B")\
template(decodeAndThrowThrowable_signature, "(JZ)V") \
template(classRedefinedCount_name, "classRedefinedCount") \
template(classLoader_name, "classLoader") \

View File

@ -2672,9 +2672,7 @@ C2V_VMENTRY_NULL(jobject, asReflectionExecutable, (JNIEnv* env, jobject, ARGUMEN
return JNIHandles::make_local(THREAD, executable);
}
C2V_VMENTRY_NULL(jobject, asReflectionField, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass), jint index))
requireInHotSpot("asReflectionField", JVMCI_CHECK_NULL);
Klass* klass = UNPACK_PAIR(Klass, klass);
static InstanceKlass* check_field(Klass* klass, jint index, JVMCI_TRAPS) {
if (!klass->is_instance_klass()) {
JVMCI_THROW_MSG_NULL(IllegalArgumentException,
err_msg("Expected non-primitive type, got %s", klass->external_name()));
@ -2684,11 +2682,100 @@ C2V_VMENTRY_NULL(jobject, asReflectionField, (JNIEnv* env, jobject, ARGUMENT_PAI
JVMCI_THROW_MSG_NULL(IllegalArgumentException,
err_msg("Field index %d out of bounds for %s", index, klass->external_name()));
}
return iklass;
}
C2V_VMENTRY_NULL(jobject, asReflectionField, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass), jint index))
requireInHotSpot("asReflectionField", JVMCI_CHECK_NULL);
Klass* klass = UNPACK_PAIR(Klass, klass);
InstanceKlass* iklass = check_field(klass, index, JVMCIENV);
fieldDescriptor fd(iklass, index);
oop reflected = Reflection::new_field(&fd, CHECK_NULL);
return JNIHandles::make_local(THREAD, reflected);
}
static jbyteArray get_encoded_annotation_data(InstanceKlass* holder, AnnotationArray* annotations_array, bool for_class,
jint filter_length, jlong filter_klass_pointers,
JavaThread* THREAD, JVMCIEnv* JVMCIENV) {
// Get a ConstantPool object for annotation parsing
Handle jcp = reflect_ConstantPool::create(CHECK_NULL);
reflect_ConstantPool::set_cp(jcp(), holder->constants());
// load VMSupport
Symbol* klass = vmSymbols::jdk_internal_vm_VMSupport();
Klass* k = SystemDictionary::resolve_or_fail(klass, true, CHECK_NULL);
InstanceKlass* vm_support = InstanceKlass::cast(k);
if (vm_support->should_be_initialized()) {
vm_support->initialize(CHECK_NULL);
}
typeArrayOop annotations_oop = Annotations::make_java_array(annotations_array, CHECK_NULL);
typeArrayHandle annotations = typeArrayHandle(THREAD, annotations_oop);
InstanceKlass** filter = filter_length == 1 ?
(InstanceKlass**) &filter_klass_pointers:
(InstanceKlass**) filter_klass_pointers;
objArrayOop filter_oop = oopFactory::new_objectArray(filter_length, CHECK_NULL);
objArrayHandle filter_classes(THREAD, filter_oop);
for (int i = 0; i < filter_length; i++) {
filter_classes->obj_at_put(i, filter[i]->java_mirror());
}
// invoke VMSupport.encodeAnnotations
JavaValue result(T_OBJECT);
JavaCallArguments args;
args.push_oop(annotations);
args.push_oop(Handle(THREAD, holder->java_mirror()));
args.push_oop(jcp);
args.push_int(for_class);
args.push_oop(filter_classes);
Symbol* signature = vmSymbols::encodeAnnotations_signature();
JavaCalls::call_static(&result,
vm_support,
vmSymbols::encodeAnnotations_name(),
signature,
&args,
CHECK_NULL);
oop res = result.get_oop();
if (JVMCIENV->is_hotspot()) {
return (jbyteArray) JNIHandles::make_local(THREAD, res);
}
typeArrayOop ba = typeArrayOop(res);
int ba_len = ba->length();
jbyte* ba_buf = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jbyte, ba_len);
if (ba_buf == nullptr) {
JVMCI_THROW_MSG_NULL(InternalError,
err_msg("could not allocate %d bytes", ba_len));
}
memcpy(ba_buf, ba->byte_at_addr(0), ba_len);
JVMCIPrimitiveArray ba_dest = JVMCIENV->new_byteArray(ba_len, JVMCI_CHECK_NULL);
JVMCIENV->copy_bytes_from(ba_buf, ba_dest, 0, ba_len);
return JVMCIENV->get_jbyteArray(ba_dest);
}
C2V_VMENTRY_NULL(jbyteArray, getEncodedClassAnnotationData, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass),
jobject filter, jint filter_length, jlong filter_klass_pointers))
InstanceKlass* holder = InstanceKlass::cast(UNPACK_PAIR(Klass, klass));
return get_encoded_annotation_data(holder, holder->class_annotations(), true, filter_length, filter_klass_pointers, THREAD, JVMCIENV);
C2V_END
C2V_VMENTRY_NULL(jbyteArray, getEncodedExecutableAnnotationData, (JNIEnv* env, jobject, ARGUMENT_PAIR(method),
jobject filter, jint filter_length, jlong filter_klass_pointers))
methodHandle method(THREAD, UNPACK_PAIR(Method, method));
return get_encoded_annotation_data(method->method_holder(), method->annotations(), false, filter_length, filter_klass_pointers, THREAD, JVMCIENV);
C2V_END
C2V_VMENTRY_NULL(jbyteArray, getEncodedFieldAnnotationData, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass), jint index,
jobject filter, jint filter_length, jlong filter_klass_pointers))
InstanceKlass* holder = check_field(InstanceKlass::cast(UNPACK_PAIR(Klass, klass)), index, JVMCIENV);
fieldDescriptor fd(holder, index);
return get_encoded_annotation_data(holder, fd.annotations(), false, filter_length, filter_klass_pointers, THREAD, JVMCIENV);
C2V_END
C2V_VMENTRY_NULL(jobjectArray, getFailedSpeculations, (JNIEnv* env, jobject, jlong failed_speculations_address, jobjectArray current))
FailedSpeculation* head = *((FailedSpeculation**)(address) failed_speculations_address);
int result_length = 0;
@ -2969,6 +3056,9 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "getCode", CC "(" HS_INSTALLED_CODE ")[B", FN_PTR(getCode)},
{CC "asReflectionExecutable", CC "(" HS_METHOD2 ")" REFLECTION_EXECUTABLE, FN_PTR(asReflectionExecutable)},
{CC "asReflectionField", CC "(" HS_KLASS2 "I)" REFLECTION_FIELD, FN_PTR(asReflectionField)},
{CC "getEncodedClassAnnotationData", CC "(" HS_KLASS2 OBJECT "IJ)[B", FN_PTR(getEncodedClassAnnotationData)},
{CC "getEncodedExecutableAnnotationData", CC "(" HS_METHOD2 OBJECT "IJ)[B", FN_PTR(getEncodedExecutableAnnotationData)},
{CC "getEncodedFieldAnnotationData", CC "(" HS_KLASS2 "I" OBJECT "IJ)[B", FN_PTR(getEncodedFieldAnnotationData)},
{CC "getFailedSpeculations", CC "(J[[B)[[B", FN_PTR(getFailedSpeculations)},
{CC "getFailedSpeculationsAddress", CC "(" HS_METHOD2 ")J", FN_PTR(getFailedSpeculationsAddress)},
{CC "releaseFailedSpeculations", CC "(J)V", FN_PTR(releaseFailedSpeculations)},

View File

@ -104,6 +104,7 @@
\
static_field(Abstract_VM_Version, _features, uint64_t) \
\
nonstatic_field(Annotations, _class_annotations, AnnotationArray*) \
nonstatic_field(Annotations, _fields_annotations, Array<AnnotationArray*>*) \
\
nonstatic_field(Array<int>, _length, int) \

View File

@ -24,16 +24,28 @@
*/
package jdk.internal.vm;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.reflect.ConstantPool;
import sun.reflect.annotation.AnnotationParser;
import sun.reflect.annotation.AnnotationSupport;
import sun.reflect.annotation.AnnotationType;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.IncompleteAnnotationException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import jdk.internal.misc.VM;
import jdk.internal.misc.Unsafe;
import java.util.List;
/*
* Support class used by JVMCI, JVMTI and VM attach mechanism.
@ -167,4 +179,404 @@ public class VMSupport {
U.copyMemory(encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, buffer + 4, encoding.length);
return requiredSize;
}
/**
* Parses {@code rawAnnotations} into a list of {@link Annotation}s and then
* serializes them to a byte array with {@link #encodeAnnotations(Collection)}.
*/
public static byte[] encodeAnnotations(byte[] rawAnnotations,
Class<?> declaringClass,
ConstantPool cp,
boolean forClass,
Class<? extends Annotation>[] selectAnnotationClasses)
{
for (Class<?> c : selectAnnotationClasses) {
if (!c.isAnnotation()) {
throw new IllegalArgumentException(c + " is not an annotation interface");
}
}
Map<Class<? extends Annotation>, Annotation> annotations =
AnnotationParser.parseSelectAnnotations(rawAnnotations, cp, declaringClass, selectAnnotationClasses);
if (forClass && annotations.size() != selectAnnotationClasses.length) {
Class<?> superClass = declaringClass.getSuperclass();
nextSuperClass:
while (superClass != null) {
JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
Map<Class<? extends Annotation>, Annotation> superAnnotations =
AnnotationParser.parseSelectAnnotations(
jla.getRawClassAnnotations(superClass),
jla.getConstantPool(superClass),
superClass,
selectAnnotationClasses);
for (Map.Entry<Class<? extends Annotation>, Annotation> e : superAnnotations.entrySet()) {
Class<? extends Annotation> annotationClass = e.getKey();
if (!annotations.containsKey(annotationClass) && AnnotationType.getInstance(annotationClass).isInherited()) {
if (annotations.isEmpty()) {
// An empty map might be unmodifiable (e.g. Collections.emptyMap()).
annotations = new LinkedHashMap<Class<? extends Annotation>, Annotation>();
}
annotations.put(annotationClass, e.getValue());
if (annotations.size() == selectAnnotationClasses.length) {
break nextSuperClass;
}
}
}
superClass = superClass.getSuperclass();
}
}
return encodeAnnotations(annotations.values());
}
/**
* Encodes annotations to a byte array. The byte array can be decoded with {@link #decodeAnnotations(byte[], AnnotationDecoder)}.
*/
public static byte[] encodeAnnotations(Collection<Annotation> annotations) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
try (DataOutputStream dos = new DataOutputStream(baos)) {
writeLength(dos, annotations.size());
for (Annotation a : annotations) {
encodeAnnotation(dos, a);
}
}
return baos.toByteArray();
} catch (Exception e) {
throw new InternalError(e);
}
}
private static void encodeAnnotation(DataOutputStream dos, Annotation a) throws Exception {
Class<? extends Annotation> type = a.annotationType();
Map<String, Object> values = AnnotationSupport.memberValues(a);
dos.writeUTF(type.getName());
writeLength(dos, values.size());
for (Map.Entry<String, Object> e : values.entrySet()) {
Object value = e.getValue();
if (value == null) {
// IncompleteAnnotationException
dos.writeByte('x');
dos.writeUTF(new IncompleteAnnotationException(type, e.getKey()).toString());
continue;
}
Class<?> valueType = value.getClass();
dos.writeUTF(e.getKey());
if (valueType == Byte.class) {
dos.writeByte('B');
dos.writeByte((byte) value);
} else if (valueType == Character.class) {
dos.writeByte('C');
dos.writeChar((char) value);
} else if (valueType == Double.class) {
dos.writeByte('D');
dos.writeDouble((double) value);
} else if (valueType == Float.class) {
dos.writeByte('F');
dos.writeFloat((float) value);
} else if (valueType == Integer.class) {
dos.writeByte('I');
dos.writeInt((int) value);
} else if (valueType == Long.class) {
dos.writeByte('J');
dos.writeLong((long) value);
} else if (valueType == Short.class) {
dos.writeByte('S');
dos.writeShort((short) value);
} else if (valueType == Boolean.class) {
dos.writeByte('Z');
dos.writeBoolean((boolean) value);
} else if (valueType == String.class) {
dos.writeByte('s');
dos.writeUTF((String) value);
} else if (valueType == Class.class) {
dos.writeByte('c');
dos.writeUTF(((Class<?>) value).getName());
} else if (valueType.isEnum()) {
dos.writeByte('e');
dos.writeUTF(valueType.getName());
dos.writeUTF(((Enum<?>) value).name());
} else if (value instanceof Annotation) {
dos.writeByte('@');
encodeAnnotation(dos, (Annotation) value);
} else if (valueType.isArray()) {
Class<?> componentType = valueType.getComponentType();
if (componentType == byte.class) {
byte[] array = (byte[]) value;
dos.writeByte('[');
dos.writeByte('B');
writeLength(dos, array.length);
dos.write(array);
} else if (componentType == char.class) {
char[] array = (char[]) value;
dos.writeByte('[');
dos.writeByte('C');
writeLength(dos, array.length);
for (char c : array) {
dos.writeChar(c);
}
} else if (componentType == double.class) {
double[] array = (double[]) value;
dos.writeByte('[');
dos.writeByte('D');
writeLength(dos, array.length);
for (double v : array) {
dos.writeDouble(v);
}
} else if (componentType == float.class) {
float[] array = (float[]) value;
dos.writeByte('[');
dos.writeByte('F');
writeLength(dos, array.length);
for (float v : array) {
dos.writeFloat(v);
}
} else if (componentType == int.class) {
int[] array = (int[]) value;
dos.writeByte('[');
dos.writeByte('I');
writeLength(dos, array.length);
for (int j : array) {
dos.writeInt(j);
}
} else if (componentType == long.class) {
long[] array = (long[]) value;
dos.writeByte('[');
dos.writeByte('J');
writeLength(dos, array.length);
for (long l : array) {
dos.writeLong(l);
}
} else if (componentType == short.class) {
short[] array = (short[]) value;
dos.writeByte('[');
dos.writeByte('S');
writeLength(dos, array.length);
for (short item : array) {
dos.writeShort(item);
}
} else if (componentType == boolean.class) {
boolean[] array = (boolean[]) value;
dos.writeByte('[');
dos.writeByte('Z');
writeLength(dos, array.length);
for (boolean b : array) {
dos.writeBoolean(b);
}
} else if (componentType == String.class) {
String[] array = (String[]) value;
dos.writeByte('[');
dos.writeByte('s');
writeLength(dos, array.length);
for (String s : array) {
dos.writeUTF(s);
}
} else if (componentType == Class.class) {
Class<?>[] array = (Class<?>[]) value;
dos.writeByte('[');
dos.writeByte('c');
writeLength(dos, array.length);
for (Class<?> aClass : array) {
dos.writeUTF(aClass.getName());
}
} else if (componentType.isEnum()) {
Enum<?>[] array = (Enum<?>[]) value;
dos.writeByte('[');
dos.writeByte('e');
dos.writeUTF(componentType.getName());
writeLength(dos, array.length);
for (Enum<?> anEnum : array) {
dos.writeUTF(anEnum.name());
}
} else if (componentType.isAnnotation()) {
Annotation[] array = (Annotation[]) value;
dos.writeByte('[');
dos.writeByte('@');
writeLength(dos, array.length);
for (Annotation annotation : array) {
encodeAnnotation(dos, annotation);
}
} else {
dos.writeByte('x');
dos.writeUTF(value.toString());
}
} else {
dos.writeByte('x');
dos.writeUTF(value.toString());
}
}
}
/**
* Helper for {@link #decodeAnnotations(byte[], AnnotationDecoder)} to convert a byte
* array (ostensibly produced by {@link VMSupport#encodeAnnotations}) into objects.
*
* @param <T> type to which a type name is {@linkplain #resolveType(String) resolved}
* @param <A> type of the object representing a decoded annotation
* @param <E> type of the object representing a decoded enum constant
* @param <X> type of the object representing a decoded error
*/
public interface AnnotationDecoder<T, A, E, X> {
/**
* Resolves a name in {@link Class#getName()} format to an object of type {@code T}.
*/
T resolveType(String name);
/**
* Creates an object representing a decoded annotation.
*
* @param type the annotation interface of the annotation
* @param elements elements of the annotation
*/
A newAnnotation(T type, Map.Entry<String, Object>[] elements);
/**
* Creates an object representing a decoded enum constant.
*
* @param enumType the enum type
* @param name the name of the enum constant
*/
E newEnumValue(T enumType, String name);
/**
* Creates an object representing a decoded error value.
*
* @param description of the error
*/
X newErrorValue(String description);
}
/**
* Decodes annotations serialized in {@code encoded} to objects.
*
* @param <T> type to which a type name is resolved
* @param <A> type of the object representing a decoded annotation
* @param <E> type of the object representing a decoded enum constant
* @param <X> type of the object representing a decoded error
* @return an immutable list of {@code A} objects
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static <T, A, E, X> List<A> decodeAnnotations(byte[] encoded, AnnotationDecoder<T, A, E, X> decoder) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
DataInputStream dis = new DataInputStream(bais);
return (List<A>) readArray(dis, () -> decodeAnnotation(dis, decoder));
} catch (Exception e) {
throw new InternalError(e);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static <T, A, E, X> A decodeAnnotation(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder) throws IOException {
String typeName = dis.readUTF();
T type = decoder.resolveType(typeName);
int n = readLength(dis);
Map.Entry[] elements = new Map.Entry[n];
for (int i = 0; i < n; i++) {
String name = dis.readUTF();
byte tag = dis.readByte();
elements[i] = Map.entry(name, switch (tag) {
case 'B' -> dis.readByte();
case 'C' -> dis.readChar();
case 'D' -> dis.readDouble();
case 'F' -> dis.readFloat();
case 'I' -> dis.readInt();
case 'J' -> dis.readLong();
case 'S' -> dis.readShort();
case 'Z' -> dis.readBoolean();
case 's' -> dis.readUTF();
case 'c' -> decoder.resolveType(dis.readUTF());
case 'e' -> decoder.newEnumValue(decoder.resolveType(dis.readUTF()), dis.readUTF());
case '@' -> decodeAnnotation(dis, decoder);
case '[' -> decodeArray(dis, decoder);
case 'x' -> decoder.newErrorValue(dis.readUTF());
default -> throw new InternalError("Unsupported tag: " + tag);
});
}
return decoder.newAnnotation(type, (Map.Entry<String, Object>[]) elements);
}
@FunctionalInterface
interface IOReader {
Object read() throws IOException;
}
private static <T, A, E, X> Object decodeArray(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder) throws IOException {
byte componentTag = dis.readByte();
return switch (componentTag) {
case 'B' -> readArray(dis, dis::readByte);
case 'C' -> readArray(dis, dis::readChar);
case 'D' -> readArray(dis, dis::readDouble);
case 'F' -> readArray(dis, dis::readFloat);
case 'I' -> readArray(dis, dis::readInt);
case 'J' -> readArray(dis, dis::readLong);
case 'S' -> readArray(dis, dis::readShort);
case 'Z' -> readArray(dis, dis::readBoolean);
case 's' -> readArray(dis, dis::readUTF);
case 'c' -> readArray(dis, () -> readClass(dis, decoder));
case 'e' -> {
T enumType = decoder.resolveType(dis.readUTF());
yield readArray(dis, () -> readEnum(dis, decoder, enumType));
}
case '@' -> readArray(dis, () -> decodeAnnotation(dis, decoder));
default -> throw new InternalError("Unsupported component tag: " + componentTag);
};
}
/**
* Reads an enum encoded at the current read position of {@code dis} and
* returns it as an object of type {@code E}.
*/
private static <T, A, E, X> E readEnum(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder, T enumType) throws IOException {
return decoder.newEnumValue(enumType, dis.readUTF());
}
/**
* Reads a class encoded at the current read position of {@code dis} and
* returns it as an object of type {@code T}.
*/
private static <T, A, E, X> T readClass(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder) throws IOException {
return decoder.resolveType(dis.readUTF());
}
/**
* Reads an array encoded at the current read position of {@code dis} and
* returns it in an immutable list.
*
* @param reader reads array elements from {@code dis}
* @return an immutable list of {@code A} objects
*/
private static List<Object> readArray(DataInputStream dis, IOReader reader) throws IOException {
Object[] array = new Object[readLength(dis)];
for (int i = 0; i < array.length; i++) {
array[i] = reader.read();
}
return List.of(array);
}
/**
* Encodes {@code length} in 1 byte if it is less than 128.
*/
private static void writeLength(DataOutputStream dos, int length) throws IOException {
if (length < 0) {
throw new NegativeArraySizeException();
} else if (length <= 127) {
dos.writeByte((byte) (0x80 | length));
} else {
dos.writeInt(length);
}
}
private static int readLength(DataInputStream dis) throws IOException {
int ch1 = dis.readByte();
int length;
if (ch1 < 0) {
length = ch1 & 0x7F;
} else {
int ch2 = dis.read();
int ch3 = dis.read();
int ch4 = dis.read();
length = (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0);
}
return length;
}
}

View File

@ -259,7 +259,8 @@ module java.base {
jdk.incubator.concurrent,
jdk.internal.jvmstat,
jdk.management,
jdk.management.agent;
jdk.management.agent,
jdk.internal.vm.ci;
exports jdk.internal.vm.annotation to
java.instrument,
jdk.internal.vm.ci,

View File

@ -677,6 +677,13 @@ class AnnotationInvocationHandler implements InvocationHandler, Serializable {
UnsafeAccessor.setMemberValues(this, mv);
}
/**
* Gets an unmodifiable view on the member values.
*/
Map<String, Object> memberValues() {
return Collections.unmodifiableMap(memberValues);
}
private static class UnsafeAccessor {
private static final jdk.internal.misc.Unsafe unsafe
= jdk.internal.misc.Unsafe.getUnsafe();

View File

@ -83,14 +83,14 @@ public class AnnotationParser {
* Like {@link #parseAnnotations(byte[], sun.reflect.ConstantPool, Class)}
* with an additional parameter {@code selectAnnotationClasses} which selects the
* annotation types to parse (other than selected are quickly skipped).<p>
* This method is only used to parse select meta annotations in the construction
* This method is used to parse select meta annotations in the construction
* phase of {@link AnnotationType} instances to prevent infinite recursion.
*
* @param selectAnnotationClasses an array of annotation types to select when parsing
*/
@SafeVarargs
@SuppressWarnings("varargs") // selectAnnotationClasses is used safely
static Map<Class<? extends Annotation>, Annotation> parseSelectAnnotations(
public static Map<Class<? extends Annotation>, Annotation> parseSelectAnnotations(
byte[] rawAnnotations,
ConstantPool constPool,
Class<?> container,
@ -336,6 +336,8 @@ public class AnnotationParser {
ByteBuffer buf,
ConstantPool constPool,
Class<?> container) {
// Note that VMSupport.encodeAnnotation (used by JVMCI) may need to
// be updated if new annotation member types are added.
Object result = null;
int tag = buf.get();
switch(tag) {

View File

@ -281,4 +281,13 @@ public final class AnnotationSupport {
}
}
}
/**
* Gets an unmodifiable view of {@code a}'s elements.
*
* @return a map from element names to element values
*/
public static Map<String, Object> memberValues(Annotation a) {
return ((AnnotationInvocationHandler) Proxy.getInvocationHandler(a)).memberValues();
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 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.
*
* 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.vm.ci.hotspot;
import java.util.Map;
import jdk.internal.vm.VMSupport.AnnotationDecoder;
import jdk.vm.ci.meta.AnnotationData;
import jdk.vm.ci.meta.EnumData;
import jdk.vm.ci.meta.ErrorData;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaUtil;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.UnresolvedJavaType;
/**
* Implementation of {@link AnnotationDecoder} that resolves type names to {@link JavaType} values
* and employs {@link AnnotationData} and {@link EnumData} to represent decoded annotations and enum
* constants respectively.
*/
final class AnnotationDataDecoder implements AnnotationDecoder<JavaType, AnnotationData, EnumData, ErrorData> {
static final AnnotationDataDecoder INSTANCE = new AnnotationDataDecoder();
@Override
public JavaType resolveType(String name) {
String internalName = MetaUtil.toInternalName(name);
return UnresolvedJavaType.create(internalName);
}
@Override
public AnnotationData newAnnotation(JavaType type, Map.Entry<String, Object>[] elements) {
return new AnnotationData(type, elements);
}
@Override
public EnumData newEnumValue(JavaType enumType, String name) {
return new EnumData(enumType, name);
}
@Override
public ErrorData newErrorValue(String description) {
return new ErrorData(description);
}
static ResolvedJavaType[] asArray(ResolvedJavaType type1, ResolvedJavaType type2, ResolvedJavaType... types) {
ResolvedJavaType[] filter = new ResolvedJavaType[2 + types.length];
filter[0] = type1;
filter[1] = type2;
System.arraycopy(types, 0, filter, 2, types.length);
return filter;
}
}

View File

@ -29,6 +29,7 @@ import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import jdk.internal.misc.Unsafe;
import jdk.vm.ci.code.BytecodeFrame;
import jdk.vm.ci.code.InstalledCode;
import jdk.vm.ci.code.InvalidInstalledCodeException;
@ -48,6 +49,12 @@ import jdk.vm.ci.meta.ResolvedJavaType;
* Calls from Java into HotSpot. The behavior of all the methods in this class that take a native
* pointer as an argument (e.g., {@link #getSymbol(long)}) is undefined if the argument does not
* denote a valid native object.
*
* Note also that some calls pass a raw VM value to avoid a JNI upcall. For example,
* {@link #getBytecode(HotSpotResolvedJavaMethodImpl, long)} needs the raw {@code Method*} value
* (stored in {@link HotSpotResolvedJavaMethodImpl#methodHandle}) in the C++ implementation. The
* {@link HotSpotResolvedJavaMethodImpl} wrapper is still passed as well as it may be the last
* reference keeping the raw value alive.
*/
final class CompilerToVM {
/**
@ -1303,4 +1310,89 @@ final class CompilerToVM {
native void notifyCompilerInliningEvent(int compileId, HotSpotResolvedJavaMethodImpl caller, long callerPointer,
HotSpotResolvedJavaMethodImpl callee, long calleePointer, boolean succeeded, String message, int bci);
/**
* Gets the serialized annotation info for {@code type} by calling
* {@code VMSupport.encodeAnnotations} in the HotSpot heap.
*/
byte[] getEncodedClassAnnotationData(HotSpotResolvedObjectTypeImpl type, ResolvedJavaType[] filter) {
try (KlassPointers a = new KlassPointers(filter)) {
return getEncodedClassAnnotationData(type, type.getKlassPointer(),
a.types, a.types.length, a.buffer());
}
}
native byte[] getEncodedClassAnnotationData(HotSpotResolvedObjectTypeImpl type, long klassPointer,
Object filter, int filterLength, long filterKlassPointers);
/**
* Gets the serialized annotation info for {@code method} by calling
* {@code VMSupport.encodeAnnotations} in the HotSpot heap.
*/
byte[] getEncodedExecutableAnnotationData(HotSpotResolvedJavaMethodImpl method, ResolvedJavaType[] filter) {
try (KlassPointers a = new KlassPointers(filter)) {
return getEncodedExecutableAnnotationData(method, method.getMethodPointer(),
a.types, a.types.length, a.buffer());
}
}
native byte[] getEncodedExecutableAnnotationData(HotSpotResolvedJavaMethodImpl method, long methodPointer,
Object filter, int filterLength, long filterKlassPointers);
/**
* Gets the serialized annotation info for the field denoted by {@code holder} and
* {@code fieldIndex} by calling {@code VMSupport.encodeAnnotations} in the HotSpot heap.
*/
byte[] getEncodedFieldAnnotationData(HotSpotResolvedObjectTypeImpl holder, int fieldIndex, ResolvedJavaType[] filter) {
try (KlassPointers a = new KlassPointers(filter)) {
return getEncodedFieldAnnotationData(holder, holder.getKlassPointer(), fieldIndex,
a.types, a.types.length, a.buffer());
}
}
native byte[] getEncodedFieldAnnotationData(HotSpotResolvedObjectTypeImpl holder, long klassPointer, int fieldIndex,
Object filterTypes, int filterLength, long filterKlassPointers);
/**
* Helper for passing {@Klass*} values to native code.
*/
static final class KlassPointers implements AutoCloseable {
final ResolvedJavaType[] types;
long pointersArray;
final Unsafe unsafe = UnsafeAccess.UNSAFE;
KlassPointers(ResolvedJavaType[] types) {
this.types = types;
}
/**
* Gets the buffer in which to pass the {@Klass*} values to JNI.
*
* @return a {@Klass*} value if {@code types.length == 1} otherwise the address of a native
* buffer holding an array of {@Klass*} values
*/
long buffer() {
int length = types.length;
if (length == 1) {
return ((HotSpotResolvedObjectTypeImpl) types[0]).getKlassPointer();
} else {
pointersArray = unsafe.allocateMemory(length * Long.BYTES);
long pos = pointersArray;
for (int i = 0; i < types.length; i++) {
HotSpotResolvedObjectTypeImpl hsType = (HotSpotResolvedObjectTypeImpl) types[i];
unsafe.putLong(pos, hsType.getKlassPointer());
pos += Long.BYTES;
}
}
return pointersArray;
}
@Override
public void close() {
if (types.length != 1 && pointersArray != 0) {
unsafe.freeMemory(pointersArray);
pointersArray = 0;
}
}
}
}

View File

@ -23,14 +23,17 @@
package jdk.vm.ci.hotspot;
import static jdk.internal.misc.Unsafe.ADDRESS_SIZE;
import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
import static jdk.vm.ci.hotspot.HotSpotVMConfig.config;
import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;
import jdk.internal.vm.annotation.Stable;
import jdk.internal.vm.VMSupport;
import jdk.vm.ci.meta.AnnotationData;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.ResolvedJavaType;
@ -227,4 +230,25 @@ class HotSpotResolvedJavaFieldImpl implements HotSpotResolvedJavaField {
public JavaConstant getConstantValue() {
return holder.getFieldInfo(index).getConstantValue(holder);
}
@Override
public AnnotationData getAnnotationData(ResolvedJavaType annotationType) {
if (!hasAnnotations()) {
return null;
}
return getAnnotationData0(annotationType).get(0);
}
@Override
public List<AnnotationData> getAnnotationData(ResolvedJavaType type1, ResolvedJavaType type2, ResolvedJavaType... types) {
if (!hasAnnotations()) {
return Collections.emptyList();
}
return getAnnotationData0(AnnotationDataDecoder.asArray(type1, type2, types));
}
private List<AnnotationData> getAnnotationData0(ResolvedJavaType... filter) {
byte[] encoded = compilerToVM().getEncodedFieldAnnotationData(holder, index, filter);
return VMSupport.decodeAnnotations(encoded, AnnotationDataDecoder.INSTANCE);
}
}

View File

@ -35,10 +35,14 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Executable;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import jdk.internal.vm.VMSupport;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option;
import jdk.vm.ci.meta.AnnotationData;
import jdk.vm.ci.meta.Constant;
import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.DefaultProfilingInfo;
@ -523,7 +527,7 @@ final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSp
@Override
public Annotation[] getAnnotations() {
if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0 || isClassInitializer()) {
if (!hasAnnotations()) {
return new Annotation[0];
}
return runtime().reflection.getMethodAnnotations(this);
@ -531,7 +535,7 @@ final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSp
@Override
public Annotation[] getDeclaredAnnotations() {
if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0 || isClassInitializer()) {
if (!hasAnnotations()) {
return new Annotation[0];
}
return runtime().reflection.getMethodDeclaredAnnotations(this);
@ -539,12 +543,19 @@ final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSp
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0 || isClassInitializer()) {
if (!hasAnnotations()) {
return null;
}
return runtime().reflection.getMethodAnnotation(this, annotationClass);
}
/**
* Returns whether this method has annotations.
*/
private boolean hasAnnotations() {
return (getConstMethodFlags() & config().constMethodHasMethodAnnotations) != 0 && !isClassInitializer();
}
@Override
public boolean isBridge() {
return (BRIDGE & getModifiers()) != 0;
@ -752,4 +763,25 @@ final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSp
public int methodIdnum() {
return UNSAFE.getChar(getConstMethod() + config().constMethodMethodIdnumOffset);
}
@Override
public AnnotationData getAnnotationData(ResolvedJavaType type) {
if (!hasAnnotations()) {
return null;
}
return getAnnotationData0(type).get(0);
}
@Override
public List<AnnotationData> getAnnotationData(ResolvedJavaType type1, ResolvedJavaType type2, ResolvedJavaType... types) {
if (!hasAnnotations()) {
return Collections.emptyList();
}
return getAnnotationData0(AnnotationDataDecoder.asArray(type1, type2, types));
}
private List<AnnotationData> getAnnotationData0(ResolvedJavaType... filter) {
byte[] encoded = compilerToVM().getEncodedExecutableAnnotationData(this, filter);
return VMSupport.decodeAnnotations(encoded, AnnotationDataDecoder.INSTANCE);
}
}

View File

@ -35,10 +35,14 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import jdk.internal.vm.VMSupport;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.AnnotationData;
import jdk.vm.ci.meta.Assumptions.AssumptionResult;
import jdk.vm.ci.meta.Assumptions.ConcreteMethod;
import jdk.vm.ci.meta.Assumptions.ConcreteSubtype;
@ -871,18 +875,56 @@ final class HotSpotResolvedObjectTypeImpl extends HotSpotResolvedJavaType implem
return getConstantPool().getSourceFileName();
}
/**
* Determines if this type may have annotations. A positive result does not mean this type has
* annotations but a negative result guarantees this type has no annotations.
*
* @param includingInherited if true, expand this query to include superclasses of this type
*/
private boolean mayHaveAnnotations(boolean includingInherited) {
if (isArray()) {
return false;
}
HotSpotVMConfig config = config();
final long metaspaceAnnotations = UNSAFE.getAddress(getKlassPointer() + config.instanceKlassAnnotationsOffset);
if (metaspaceAnnotations != 0) {
long classAnnotations = UNSAFE.getAddress(metaspaceAnnotations + config.annotationsClassAnnotationsOffset);
if (classAnnotations != 0) {
return true;
}
}
if (includingInherited) {
HotSpotResolvedObjectTypeImpl superClass = getSuperclass();
if (superClass != null) {
return superClass.mayHaveAnnotations(true);
}
}
return false;
}
private static final Annotation[] NO_ANNOTATIONS = {};
@Override
public Annotation[] getAnnotations() {
if (!mayHaveAnnotations(true)) {
return NO_ANNOTATIONS;
}
return runtime().reflection.getAnnotations(this);
}
@Override
public Annotation[] getDeclaredAnnotations() {
if (!mayHaveAnnotations(false)) {
return NO_ANNOTATIONS;
}
return runtime().reflection.getDeclaredAnnotations(this);
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
if (!mayHaveAnnotations(true)) {
return null;
}
return runtime().reflection.getAnnotation(this, annotationClass);
}
@ -1062,4 +1104,25 @@ final class HotSpotResolvedObjectTypeImpl extends HotSpotResolvedJavaType implem
public boolean isCloneableWithAllocation() {
return (getAccessFlags() & config().jvmAccIsCloneableFast) != 0;
}
@Override
public AnnotationData getAnnotationData(ResolvedJavaType annotationType) {
if (!mayHaveAnnotations(true)) {
return null;
}
return getAnnotationData0(annotationType).get(0);
}
@Override
public List<AnnotationData> getAnnotationData(ResolvedJavaType type1, ResolvedJavaType type2, ResolvedJavaType... types) {
if (!mayHaveAnnotations(true)) {
return Collections.emptyList();
}
return getAnnotationData0(AnnotationDataDecoder.asArray(type1, type2, types));
}
private List<AnnotationData> getAnnotationData0(ResolvedJavaType... filter) {
byte[] encoded = compilerToVM().getEncodedClassAnnotationData(this, filter);
return VMSupport.decodeAnnotations(encoded, AnnotationDataDecoder.INSTANCE);
}
}

View File

@ -27,9 +27,12 @@ import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.common.NativeImageReinitialize;
import jdk.vm.ci.meta.AnnotationData;
import jdk.vm.ci.meta.Assumptions.AssumptionResult;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
@ -317,4 +320,15 @@ public final class HotSpotResolvedPrimitiveType extends HotSpotResolvedJavaType
JavaConstant getJavaMirror() {
return mirror;
}
@Override
public AnnotationData getAnnotationData(ResolvedJavaType type) {
return null;
}
@Override
public List<AnnotationData> getAnnotationData(ResolvedJavaType type1, ResolvedJavaType type2, ResolvedJavaType... types) {
return Collections.emptyList();
}
}

View File

@ -110,6 +110,7 @@ class HotSpotVMConfig extends HotSpotVMConfigAccess {
final int instanceKlassStateBeingInitialized = getConstant("InstanceKlass::being_initialized", Integer.class);
final int annotationsFieldAnnotationsOffset = getFieldOffset("Annotations::_fields_annotations", Integer.class, "Array<AnnotationArray*>*");
final int annotationsClassAnnotationsOffset = getFieldOffset("Annotations::_class_annotations", Integer.class, "AnnotationArray*");
final int fieldsAnnotationsBaseOffset = getFieldValue("CompilerToVM::Data::_fields_annotations_base_offset", Integer.class, "int");
final int arrayU1LengthOffset = getFieldOffset("Array<int>::_length", Integer.class, "int");

View File

@ -40,7 +40,7 @@ import jdk.vm.ci.meta.JavaConstant;
*/
final class IndirectHotSpotObjectConstantImpl extends HotSpotObjectConstantImpl {
/**
* An object handle in {@code JVMCI::_object_handles}.
* An object handle in {@code JVMCIRuntime::_oop_handles}.
*/
private long objectHandle;

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 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.
*
* 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.vm.ci.meta;
import java.lang.annotation.Inherited;
import java.util.List;
/**
* Represents a program element such as a method, constructor, field or class for which annotations
* may be present.
*/
public interface Annotated {
/**
* Constructs the annotations present on this element whose types are in the set composed of {@code type1},
* {@code type2} and {@code types}. All enum types referenced by the returned annotation are
* initialized. Class initialization is not triggered for enum types referenced by other
* annotations of this element.
*
* If this element is a class, then {@link Inherited} annotations are included in the set of
* annotations considered.
*
* See {@link java.lang.reflect.AnnotatedElement} for the definition of <em>present</em>.
*
* @param type1 an annotation type
* @param type2 an annotation type
* @param types more annotation types
* @return an immutable list of the annotations present on this element that match one of the
* given types
* @throws IllegalArgumentException if any type in the set composed of {@code type1},
* {@code type2} and {@code types} is not an annotation interface type
* @throws UnsupportedOperationException if this operation is not supported
*/
default List<AnnotationData> getAnnotationData(ResolvedJavaType type1, ResolvedJavaType type2, ResolvedJavaType... types) {
throw new UnsupportedOperationException();
}
/**
* Constructs the annotation present on this element of type {@code type}.
*
* See {@link java.lang.reflect.AnnotatedElement} for the definition of <em>present</em>.
*
* @param type the type object corresponding to the annotation interface type
* @return this element's annotation for the specified annotation type if present on this
* element, else null
* @throws IllegalArgumentException if {@code type} is not an annotation interface type
* @throws UnsupportedOperationException if this operation is not supported
*/
default AnnotationData getAnnotationData(ResolvedJavaType type) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 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.
*
* 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.vm.ci.meta;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* Represents an annotation where element values are represented with the types described
* {@linkplain #get here}.
*
* In contrast to the standard annotation API based on {@link Annotation}, use of
* {@link AnnotationData} allows annotations to be queried without the JVMCI runtime having to
* support dynamic loading of arbitrary {@link Annotation} classes. Such support is impossible in a
* closed world, ahead-of-time compiled environment such as libgraal.
*/
public final class AnnotationData {
private final JavaType type;
private final Map<String, Object> elements;
private static final Set<Class<?>> ELEMENT_TYPES = Set.of(
Boolean.class,
Byte.class,
Character.class,
Short.class,
Integer.class,
Float.class,
Long.class,
Double.class,
String.class,
EnumData.class,
AnnotationData.class);
/**
* Creates an annotation.
*
* @param type the annotation interface of this annotation, represented as a {@link JavaType}
* @param elements the names and values of this annotation's element values. Each value's type
* must be one of the {@code AnnotationData} types described {@linkplain #get here}
* or it must be a {@link ErrorData} object whose {@code toString()} value describes
* the error raised while parsing the element. There is no distinction between a
* value explicitly present in the annotation and an element's default value.
* @throws IllegalArgumentException if the value of an entry in {@code elements} is not of an
* accepted type
* @throws NullPointerException if any of the above parameters is null or any entry in
* {@code elements} is null
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public AnnotationData(JavaType type, Map.Entry<String, Object>[] elements) {
this.type = Objects.requireNonNull(type);
for (Map.Entry<String, Object> e : elements) {
Object value = e.getValue();
if (!(value instanceof ErrorData) &&
!(value instanceof JavaType) &&
!(value instanceof List) &&
!ELEMENT_TYPES.contains(value.getClass())) {
throw new IllegalArgumentException("illegal type for element " + e.getKey() + ": " + value.getClass().getName());
}
}
this.elements = Map.ofEntries(elements);
}
/**
* @return the annotation interface of this annotation, represented as a {@link JavaType}
*/
public JavaType getAnnotationType() {
return type;
}
// @formatter:off
/**
* Gets the annotation element denoted by {@code name}. The following table shows the
* correspondence between the type of an element as declared by a method in the annotation
* interface and the type of value returned by this method:
* <table>
* <thead>
* <tr><th>Annotation</th> <th>AnnotationData</th></tr>
* </thead><tbody>
* <tr><td>boolean</td> <td>Boolean</td></tr>
* <tr><td>byte</td> <td>Byte</td></tr>
* <tr><td>char</td> <td>Character</td></tr>
* <tr><td>short</td> <td>Short</td></tr>
* <tr><td>int</td> <td>Integer</td></tr>
* <tr><td>float</td> <td>Float</td></tr>
* <tr><td>long</td> <td>Long</td></tr>
* <tr><td>double</td> <td>Double</td></tr>
* <tr><td>String</td> <td>String</td></tr>
* <tr><td>Class</td> <td>JavaType</td></tr>
* <tr><td>Enum</td> <td>EnumData</td></tr>
* <tr><td>Annotation</td> <td>AnnotationData</td></tr>
* <tr><td>[]</td><td>immutable List&lt;T&gt; where T is one of the above types</td></tr>
* </tbody>
* </table>
*
* @param <V> the type of the element as per the {@code AnnotationData} column in the above
* table or {@link Object}
* @param elementType the class for the type of the element
* @return the annotation element denoted by {@code name}
* @throws ClassCastException if the element is not of type {@code V}
* @throws IllegalArgumentException if this annotation has no element named {@code name} or if
* there was an error parsing or creating the element value
*/
// @formatter:on
@SuppressWarnings("unchecked")
public <V> V get(String name, Class<V> elementType) {
Object val = elements.get(name);
if (val == null) {
throw new IllegalArgumentException("no element named " + name);
}
Class<? extends Object> valClass = val.getClass();
if (valClass == ErrorData.class) {
throw new IllegalArgumentException(val.toString());
}
return elementType.cast(val);
}
@Override
public String toString() {
return "@" + type.getName() + "(" + elements + ")";
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof AnnotationData) {
AnnotationData that = (AnnotationData) obj;
return this.type.equals(that.type) && this.elements.equals(that.elements);
}
return false;
}
@Override
public int hashCode() {
return type.hashCode() ^ elements.hashCode();
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 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.
*
* 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.vm.ci.meta;
/**
* Represents an enum constant within {@link AnnotationData}.
*/
public final class EnumData {
private final JavaType type;
private final String name;
/**
* Creates an enum constant.
*
* @param type the {@linkplain Enum enum type}
* @param name the {@linkplain Enum#name() name} of the enum
*/
public EnumData(JavaType type, String name) {
this.type = type;
this.name = name;
}
/**
* Gets the {@linkplain Enum enum type}.
*/
public JavaType getEnumType() {
return type;
}
/**
* Gets the {@linkplain Enum#name() name} of the enum.
*/
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof EnumData) {
EnumData that = (EnumData) obj;
return this.type.equals(that.type) && this.name.equals(that.name);
}
return false;
}
@Override
public int hashCode() {
return this.type.hashCode() ^ this.name.hashCode();
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 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.
*
* 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.vm.ci.meta;
/**
* Represents an error constant within {@link AnnotationData}.
*
* Similar to {@code sun.reflect.annotation.ExceptionProxy}.
*/
public final class ErrorData {
private final String description;
/**
* Creates an error constant.
*
* @param description description of the error
*/
public ErrorData(String description) {
this.description = description;
}
@Override
public String toString() {
return description;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ErrorData) {
ErrorData that = (ErrorData) obj;
return this.description.equals(that.description);
}
return false;
}
@Override
public int hashCode() {
return description.hashCode();
}
}

View File

@ -29,7 +29,7 @@ import java.lang.reflect.Modifier;
* Represents a reference to a resolved Java field. Fields, like methods and types, are resolved
* through {@link ConstantPool constant pools}.
*/
public interface ResolvedJavaField extends JavaField, ModifiersProvider, AnnotatedElement {
public interface ResolvedJavaField extends JavaField, ModifiersProvider, AnnotatedElement, Annotated {
/**
* {@inheritDoc}

View File

@ -33,7 +33,7 @@ import java.lang.reflect.Type;
* Represents a resolved Java method. Methods, like fields and types, are resolved through
* {@link ConstantPool constant pools}.
*/
public interface ResolvedJavaMethod extends JavaMethod, InvokeTarget, ModifiersProvider, AnnotatedElement {
public interface ResolvedJavaMethod extends JavaMethod, InvokeTarget, ModifiersProvider, AnnotatedElement, Annotated {
/**
* Returns the method's bytecode. The returned bytecode does not contain breakpoints or non-Java

View File

@ -31,7 +31,7 @@ import jdk.vm.ci.meta.Assumptions.AssumptionResult;
* thereof. Types, like fields and methods, are resolved through {@link ConstantPool constant pools}
* .
*/
public interface ResolvedJavaType extends JavaType, ModifiersProvider, AnnotatedElement {
public interface ResolvedJavaType extends JavaType, ModifiersProvider, AnnotatedElement, Annotated {
/**
* Checks whether this type has a finalizer method.
*
@ -137,8 +137,8 @@ public interface ResolvedJavaType extends JavaType, ModifiersProvider, Annotated
boolean isAssignableFrom(ResolvedJavaType other);
/**
* Returns {@code null} since support for VM anonymous class was removed by JDK-8243287.
* This method is preserved for JVMCI backwards compatibility.
* Returns {@code null} since support for VM anonymous class was removed by JDK-8243287. This
* method is preserved for JVMCI backwards compatibility.
*/
@Deprecated
default ResolvedJavaType getHostClass() {

View File

@ -25,10 +25,20 @@
* @test
* @requires vm.jvmci
* @library ../../../../../
* @compile ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/AnnotationTestInput.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/MemberDeleted.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/MemberTypeChanged.java
* TestResolvedJavaType.java
* @clean jdk.internal.vm.test.AnnotationTestInput$Missing
* @compile ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/alt/MemberDeleted.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/alt/MemberTypeChanged.java
* @modules jdk.internal.vm.ci/jdk.vm.ci.meta
* jdk.internal.vm.ci/jdk.vm.ci.runtime
* jdk.internal.vm.ci/jdk.vm.ci.common
* java.base/jdk.internal.reflect
* java.base/jdk.internal.misc
* java.base/jdk.internal.vm
* java.base/sun.reflect.annotation
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler jdk.vm.ci.runtime.test.TestResolvedJavaField
*/
@ -57,6 +67,7 @@ import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import jdk.internal.vm.test.AnnotationTestInput;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.JavaConstant;
@ -181,6 +192,14 @@ public class TestResolvedJavaField extends FieldUniverse {
return null;
}
@Test
public void getAnnotationDataTest() throws Exception {
TestResolvedJavaType.getAnnotationDataTest(AnnotationTestInput.class.getDeclaredField("annotatedField"));
for (Field f : fields.keySet()) {
TestResolvedJavaType.getAnnotationDataTest(f);
}
}
// @formatter:off
private static final String[] untestedApiMethods = {
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -25,9 +25,20 @@
* @test
* @requires vm.jvmci
* @library ../../../../../
* @compile ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/AnnotationTestInput.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/MemberDeleted.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/MemberTypeChanged.java
* TestResolvedJavaType.java
* @clean jdk.internal.vm.test.AnnotationTestInput$Missing
* @compile ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/alt/MemberDeleted.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/alt/MemberTypeChanged.java
* @modules jdk.internal.vm.ci/jdk.vm.ci.meta
* jdk.internal.vm.ci/jdk.vm.ci.runtime
* jdk.internal.vm.ci/jdk.vm.ci.common
* java.base/jdk.internal.reflect
* java.base/jdk.internal.misc
* java.base/jdk.internal.vm
* java.base/sun.reflect.annotation
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler jdk.vm.ci.runtime.test.TestResolvedJavaMethod
*/
@ -61,11 +72,16 @@ import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import jdk.internal.vm.test.AnnotationTestInput;
import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.ExceptionHandler;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaMethod.Parameter;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.runtime.test.TestResolvedJavaMethod.AnnotationDataTest.Annotation1;
import jdk.vm.ci.runtime.test.TestResolvedJavaMethod.AnnotationDataTest.Annotation2;
import jdk.vm.ci.runtime.test.TestResolvedJavaMethod.AnnotationDataTest.Annotation3;
import jdk.vm.ci.runtime.test.TestResolvedJavaMethod.AnnotationDataTest.NumbersDE;
/**
* Tests for {@link ResolvedJavaMethod}.
@ -474,6 +490,83 @@ public class TestResolvedJavaMethod extends MethodUniverse {
}
}
/**
* Encapsulates input for {@link TestResolvedJavaMethod#getAnnotationDataTest}.
*/
static class AnnotationDataTest {
public enum NumbersEN {
One,
Two;
}
public enum NumbersDE {
Eins,
Zwei;
}
public enum NumbersUA {
Odyn,
Dva;
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Annotation1 {
NumbersEN value() default NumbersEN.One;
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Annotation2 {
NumbersDE value() default NumbersDE.Eins;
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Annotation3 {
NumbersUA value() default NumbersUA.Odyn;
}
@Annotation1
@Annotation2
@Annotation3(NumbersUA.Dva)
static void methodWithThreeAnnotations() {
}
}
@Test
public void getAnnotationDataTest() throws Exception {
TestResolvedJavaType.getAnnotationDataTest(AnnotationTestInput.class.getDeclaredMethod("annotatedMethod"));
TestResolvedJavaType.getAnnotationDataTest(AnnotationTestInput.class.getDeclaredMethod("missingAnnotation"));
try {
TestResolvedJavaType.getAnnotationDataTest(AnnotationTestInput.class.getDeclaredMethod("missingNestedAnnotation"));
throw new AssertionError("expected " + NoClassDefFoundError.class.getName());
} catch (NoClassDefFoundError e) {
Assert.assertEquals("jdk/internal/vm/test/AnnotationTestInput$Missing", e.getMessage());
}
TestResolvedJavaType.getAnnotationDataTest(AnnotationTestInput.class.getDeclaredMethod("missingTypeOfClassMember"));
TestResolvedJavaType.getAnnotationDataTest(AnnotationTestInput.class.getDeclaredMethod("missingMember"));
TestResolvedJavaType.getAnnotationDataTest(AnnotationTestInput.class.getDeclaredMethod("changeTypeOfMember"));
for (Method m : methods.keySet()) {
TestResolvedJavaType.getAnnotationDataTest(m);
}
ResolvedJavaMethod m = metaAccess.lookupJavaMethod(AnnotationDataTest.class.getDeclaredMethod("methodWithThreeAnnotations"));
ResolvedJavaType a1 = metaAccess.lookupJavaType(Annotation1.class);
ResolvedJavaType a2 = metaAccess.lookupJavaType(Annotation2.class);
ResolvedJavaType a3 = metaAccess.lookupJavaType(Annotation3.class);
ResolvedJavaType a4 = metaAccess.lookupJavaType(AnnotationDataTest.class);
ResolvedJavaType numbersDEType = metaAccess.lookupJavaType(NumbersDE.class);
// Ensure NumbersDE is not initialized before Annotation2 is requested
Assert.assertFalse(numbersDEType.isInitialized());
Assert.assertEquals(2, m.getAnnotationData(a1, a3).size());
// Ensure NumbersDE is initialized after Annotation2 is requested
Assert.assertNotNull(m.getAnnotationData(a2));
Assert.assertTrue(numbersDEType.isInitialized());
}
private Method findTestMethod(Method apiMethod) {
String testName = apiMethod.getName() + "Test";
for (Method m : getClass().getDeclaredMethods()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -25,12 +25,20 @@
* @test
* @requires vm.jvmci
* @library ../../../../../
* @compile ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/AnnotationTestInput.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/MemberDeleted.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/MemberTypeChanged.java
* @clean jdk.internal.vm.test.AnnotationTestInput$Missing
* @compile ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/alt/MemberDeleted.java
* ../../../../../../../../../../../jdk/jdk/internal/vm/AnnotationEncodingDecoding/alt/MemberTypeChanged.java
* @modules java.base/jdk.internal.org.objectweb.asm
* java.base/jdk.internal.reflect
* jdk.internal.vm.ci/jdk.vm.ci.meta
* jdk.internal.vm.ci/jdk.vm.ci.runtime
* jdk.internal.vm.ci/jdk.vm.ci.common
* java.base/jdk.internal.misc
* java.base/jdk.internal.vm
* java.base/sun.reflect.annotation
* @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler jdk.vm.ci.runtime.test.TestResolvedJavaType
*/
@ -57,30 +65,43 @@ import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Assert;
import org.junit.Test;
import jdk.internal.org.objectweb.asm.*;
import jdk.internal.reflect.ConstantPool;
import jdk.internal.vm.test.AnnotationTestInput;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.Annotated;
import jdk.vm.ci.meta.AnnotationData;
import jdk.vm.ci.meta.EnumData;
import jdk.vm.ci.meta.Assumptions.AssumptionResult;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaUtil;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.UnresolvedJavaType;
import sun.reflect.annotation.AnnotationSupport;
/**
* Tests for {@link ResolvedJavaType}.
@ -177,7 +198,8 @@ public class TestResolvedJavaType extends TypeUniverse {
@Test
public void lambdaInternalNameTest() {
// Verify that the last dot in lambda types is properly handled when transitioning from internal name to java
// Verify that the last dot in lambda types is properly handled when transitioning from
// internal name to java
// name and vice versa.
Supplier<Runnable> lambda = () -> () -> System.out.println("run");
ResolvedJavaType lambdaType = metaAccess.lookupJavaType(lambda.getClass());
@ -903,11 +925,11 @@ public class TestResolvedJavaType extends TypeUniverse {
return f.getName().equals("allowedModes") || f.getName().equals("lookupClass");
}
if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ClassLoader.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(AccessibleObject.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(Constructor.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(Field.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(Method.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(Module.class))) {
f.getDeclaringClass().equals(metaAccess.lookupJavaType(AccessibleObject.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(Constructor.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(Field.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(Method.class)) ||
f.getDeclaringClass().equals(metaAccess.lookupJavaType(Module.class))) {
return true;
}
return false;
@ -1131,6 +1153,40 @@ public class TestResolvedJavaType extends TypeUniverse {
return null;
}
@Test
public void getAnnotationDataTest() throws Exception {
getAnnotationDataTest(AnnotationTestInput.AnnotatedClass.class);
getAnnotationDataTest(int.class);
getAnnotationDataTest(void.class);
for (Class<?> c : classes) {
getAnnotationDataTest(c);
}
// Primitive classes have no annotations but we cannot directly
// test absence of annotations. Instead, just ensure empty answers
// are returned when looking up an arbitrary annotation type.
Class<?>[] prims = {void.class, byte.class, int.class, double.class, float.class, short.class, char.class, long.class};
ResolvedJavaType overrideType = metaAccess.lookupJavaType(Override.class);
for (Class<?> c : prims) {
ResolvedJavaType type = metaAccess.lookupJavaType(c);
AnnotationData ad = type.getAnnotationData(overrideType);
Assert.assertNull(String.valueOf(ad), ad);
List<AnnotationData> adArray = type.getAnnotationData(overrideType, overrideType);
Assert.assertEquals(0, adArray.size());
}
// Test that inherited annotations are handled properly.
ResolvedJavaType namedType = metaAccess.lookupJavaType(AnnotationTestInput.Named.class);
AnnotationData ad = metaAccess.lookupJavaType(AnnotationTestInput.OwnName.class).getAnnotationData(namedType);
Assert.assertEquals("NonInheritedValue", ad.get("value", String.class));
ad = metaAccess.lookupJavaType(AnnotationTestInput.InheritedName1.class).getAnnotationData(namedType);
Assert.assertEquals("Super1", ad.get("value", String.class));
ad = metaAccess.lookupJavaType(AnnotationTestInput.InheritedName2.class).getAnnotationData(namedType);
Assert.assertEquals("Super2", ad.get("value", String.class));
ad = metaAccess.lookupJavaType(AnnotationTestInput.InheritedName3.class).getAnnotationData(namedType);
Assert.assertEquals("Super1", ad.get("value", String.class));
}
// @formatter:off
private static final String[] untestedApiMethods = {
"initialize",
@ -1174,4 +1230,129 @@ public class TestResolvedJavaType extends TypeUniverse {
private static boolean isSignaturePolymorphic(ResolvedJavaMethod method) {
return method.getAnnotation(SIGNATURE_POLYMORPHIC_CLASS) != null;
}
/**
* Tests that {@link AnnotationData} obtained from a {@link Class}, {@link Method} or
* {@link Field} matches {@link AnnotatedElement#getAnnotations()} for the corresponding JVMCI
* object.
*
* @param annotated a {@link Class}, {@link Method} or {@link Field} object
*/
public static void getAnnotationDataTest(AnnotatedElement annotated) throws Exception {
testGetAnnotationData(annotated, List.of(annotated.getAnnotations()));
}
private static void testGetAnnotationData(AnnotatedElement annotated, List<Annotation> annotations) throws AssertionError {
for (Annotation a : annotations) {
AnnotationData ad = toAnnotated(annotated).getAnnotationData(metaAccess.lookupJavaType(a.annotationType()));
assertAnnotationsEquals(a, ad);
// Check that encoding/decoding produces a stable result
AnnotationData ad2 = toAnnotated(annotated).getAnnotationData(metaAccess.lookupJavaType(a.annotationType()));
assertEquals(ad, ad2);
}
if (annotations.size() < 2) {
return;
}
ResolvedJavaType type1 = metaAccess.lookupJavaType(annotations.get(0).annotationType());
ResolvedJavaType type2 = metaAccess.lookupJavaType(annotations.get(1).annotationType());
for (int i = 2; i < annotations.size(); i++) {
ResolvedJavaType[] types = annotations.//
subList(2, i + 1).//
stream().map(a -> metaAccess.lookupJavaType(a.annotationType())).//
toArray(ResolvedJavaType[]::new);
List<AnnotationData> annotationData = toAnnotated(annotated).getAnnotationData(type1, type2, types);
assertEquals(2 + types.length, annotationData.size());
for (int j = 0; j < annotationData.size(); j++) {
Annotation a = annotations.get(j);
AnnotationData ad = annotationData.get(j);
assertAnnotationsEquals(a, ad);
}
}
}
private static Annotated toAnnotated(AnnotatedElement element) {
if (element instanceof Class<?> t) {
return metaAccess.lookupJavaType(t);
} else if (element instanceof Method m) {
return metaAccess.lookupJavaMethod(m);
} else {
Field f = (Field) element;
return metaAccess.lookupJavaField(f);
}
}
private static UnresolvedJavaType asType(Class<?> valueType) {
return UnresolvedJavaType.create(MetaUtil.toInternalName(valueType.getName()));
}
private static void assertAnnotationsEquals(Annotation a, AnnotationData ad) {
Map<String, Object> values = AnnotationSupport.memberValues(a);
for (Map.Entry<String, Object> e : values.entrySet()) {
String name = e.getKey();
Object aValue = e.getValue();
Object adValue;
try {
adValue = ad.get(name, Object.class);
} catch (IllegalArgumentException ex) {
assertEquals(aValue.toString(), ex.getMessage());
continue;
}
try {
assertAnnotationElementsEqual(aValue, adValue);
} catch (ClassCastException ex) {
throw new AssertionError(a.getClass().getName() + "." + name + " has wrong type: " + adValue.getClass().getName(), ex);
}
}
}
private static void assertAnnotationElementsEqual(Object aValue, Object adValue) {
Class<?> valueType = aValue.getClass();
if (valueType.isEnum()) {
assertEnumObjectsEquals(aValue, adValue);
} else if (aValue instanceof Class) {
assertClassObjectsEquals(aValue, adValue);
} else if (aValue instanceof Annotation) {
assertAnnotationObjectsEquals(aValue, adValue);
} else if (valueType.isArray()) {
List<?> adList = (List<?>) adValue;
int length = Array.getLength(aValue);
assertEquals(length, adList.size());
for (int i = 0; i < length; i++) {
assertAnnotationElementsEqual(Array.get(aValue, i), adList.get(i));
}
} else {
assertEquals(aValue.getClass(), adValue.getClass());
assertEquals(aValue, adValue);
}
}
private static void assertClassObjectsEquals(Object aValue, Object adValue) {
String aName = ((Class<?>) aValue).getName();
String adName = ((JavaType) adValue).toClassName();
assertEquals(aName, adName);
}
private static void assertEnumObjectsEquals(Object aValue, Object adValue) {
EnumData adEnum = (EnumData) adValue;
String adEnumName = adEnum.getName();
String aEnumName = ((Enum<?>) aValue).name();
assertEquals(adEnumName, aEnumName);
}
private static void assertAnnotationObjectsEquals(Object aValue, Object adValue) {
Annotation aAnnotation = (Annotation) aValue;
AnnotationData adAnnotation = (AnnotationData) adValue;
assertAnnotationsEquals(aAnnotation, adAnnotation);
}
private static void assertArraysEqual(Object aValue, Object adValue, int length, BiConsumer<Object, Object> assertEqualty) {
Object[] aArray = (Object[]) aValue;
Object[] adArray = (Object[]) adValue;
for (int i = 0; i < length; i++) {
assertEqualty.accept(aArray[i], adArray[i]);
}
}
}

View File

@ -22,14 +22,8 @@
*/
package jdk.vm.ci.runtime.test;
import jdk.internal.misc.Unsafe;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.runtime.JVMCI;
import org.junit.Test;
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isStatic;
import java.io.Serializable;
import java.lang.reflect.Array;
@ -54,8 +48,15 @@ import java.util.TreeMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isStatic;
import org.junit.Test;
import jdk.internal.misc.Unsafe;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.runtime.JVMCI;
/**
* Context for type related tests.

View File

@ -0,0 +1,375 @@
/*
* Copyright (c) 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.
*
* 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.internal.vm.test;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class AnnotationTestInput {
enum Mood {
HAPPY,
SAD,
CONFUSED;
}
private class PrivateClass {}
@Single(string = "a",
stringArray = {"a", "b"},
classValue = String.class,
classArray = {String.class, Exception.class},
byteValue = 1,
byteArray = {1, 2, Byte.MIN_VALUE, Byte.MAX_VALUE},
charValue = 'a',
charArray = {'a', 'b',
Character.MIN_VALUE, Character.MAX_VALUE,
'\b', '\f', '\n', '\r', '\t', '\\', '\'', '\"', '\u012A'},
doubleValue = 3.3D,
doubleArray = {3.3D, 4.4D,
Double.MIN_VALUE, Double.MAX_VALUE,
Double.NaN,
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY},
floatValue = 4.4F,
floatArray = {4.4F, 5.5F,
Float.MIN_VALUE, Float.MAX_VALUE,
Float.NaN,
Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY},
intValue = 5,
intArray = {5, 6, Integer.MIN_VALUE, Integer.MAX_VALUE},
longValue = 6L,
longArray = {6L, 7L, Long.MIN_VALUE, Long.MAX_VALUE},
shortValue = 7,
shortArray = {7, 8, Short.MIN_VALUE, Short.MAX_VALUE},
booleanValue = true,
booleanArray = {true, false},
mood = Mood.SAD,
moodArray = {Mood.CONFUSED, Mood.HAPPY},
nested = @NestedAnno("nested1"),
nestedArray = {@NestedAnno("nested2"), @NestedAnno("nested3")})
@Single(string = "A",
stringArray = {"A", "B"},
classValue = Thread.class,
classArray = {Thread.class, PrivateClass.class},
byteValue = -1,
byteArray = {-1, -2},
charValue = 'A',
charArray = {'a', 'b'},
doubleValue = -3.3D,
doubleArray = {3.3D, 4.4D},
floatValue = -4.4F,
floatArray = {4.4F, 5.5F},
intValue = -5,
intArray = {5, 6},
longValue = -6L,
longArray = {6L, 7L},
shortValue = -7,
shortArray = {7, 8},
booleanValue = true,
booleanArray = {true, false},
mood = Mood.CONFUSED,
moodArray = {Mood.SAD, Mood.CONFUSED},
nested = @NestedAnno("nested4"),
nestedArray = {@NestedAnno("nested5"), @NestedAnno("nested6")})
@SingleWithDefaults
@Deprecated
@SuppressWarnings("unchecked")
public void annotatedMethod() {
}
@Named("Super1")
public static class Super1 {}
@Named("Super2")
public static class Super2 extends Super1 {}
public static class Super3 extends Super1 {}
@Named("NonInheritedValue")
public static class OwnName extends Super1 {}
public static class InheritedName1 extends Super1 {}
public static class InheritedName2 extends Super2 {}
public static class InheritedName3 extends Super3 {}
@Named("AnnotatedClass")
@Single(string = "a",
stringArray = {"a", "b"},
classValue = String.class,
classArray = {String.class, Exception.class},
byteValue = 1,
byteArray = {1, 2, Byte.MIN_VALUE, Byte.MAX_VALUE},
charValue = 'a',
charArray = {'a', 'b',
Character.MIN_VALUE, Character.MAX_VALUE,
'\b', '\f', '\n', '\r', '\t', '\\', '\'', '\"', '\u012A'},
doubleValue = 3.3D,
doubleArray = {3.3D, 4.4D,
Double.MIN_VALUE, Double.MAX_VALUE,
Double.NaN,
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY},
floatValue = 4.4F,
floatArray = {4.4F, 5.5F,
Float.MIN_VALUE, Float.MAX_VALUE,
Float.NaN,
Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY},
intValue = 5,
intArray = {5, 6, Integer.MIN_VALUE, Integer.MAX_VALUE},
longValue = 6L,
longArray = {6L, 7L, Long.MIN_VALUE, Long.MAX_VALUE},
shortValue = 7,
shortArray = {7, 8, Short.MIN_VALUE, Short.MAX_VALUE},
booleanValue = true,
booleanArray = {true, false},
mood = Mood.SAD,
moodArray = {Mood.CONFUSED, Mood.HAPPY},
nested = @NestedAnno("nested7"),
nestedArray = {@NestedAnno("nested8"), @NestedAnno("nested9")})
@Single(string = "A",
stringArray = {"A", "B"},
classValue = Thread.class,
classArray = {Thread.class, PrivateClass.class},
byteValue = -1,
byteArray = {-1, -2},
charValue = 'A',
charArray = {'a', 'b'},
doubleValue = -3.3D,
doubleArray = {3.3D, 4.4D},
floatValue = -4.4F,
floatArray = {4.4F, 5.5F},
intValue = -5,
intArray = {5, 6},
longValue = -6L,
longArray = {6L, 7L},
shortValue = -7,
shortArray = {7, 8},
booleanValue = true,
booleanArray = {true, false},
mood = Mood.CONFUSED,
moodArray = {Mood.SAD, Mood.CONFUSED},
nested = @NestedAnno("nested10"),
nestedArray = {@NestedAnno("nested11"), @NestedAnno("nested12")})
@Deprecated
@SuppressWarnings({"rawtypes", "all"})
public static class AnnotatedClass {}
@Single(string = "a",
stringArray = {"a", "b"},
classValue = String.class,
classArray = {String.class, Exception.class},
byteValue = 1,
byteArray = {1, 2, Byte.MIN_VALUE, Byte.MAX_VALUE},
charValue = 'a',
charArray = {'a', 'b',
Character.MIN_VALUE, Character.MAX_VALUE,
'\b', '\f', '\n', '\r', '\t', '\\', '\'', '\"', '\u012A'},
doubleValue = 3.3D,
doubleArray = {3.3D, 4.4D,
Double.MIN_VALUE, Double.MAX_VALUE,
Double.NaN,
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY},
floatValue = 4.4F,
floatArray = {4.4F, 5.5F,
Float.MIN_VALUE, Float.MAX_VALUE,
Float.NaN,
Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY},
intValue = 5,
intArray = {5, 6, Integer.MIN_VALUE, Integer.MAX_VALUE},
longValue = 6L,
longArray = {6L, 7L, Long.MIN_VALUE, Long.MAX_VALUE},
shortValue = 7,
shortArray = {7, 8, Short.MIN_VALUE, Short.MAX_VALUE},
booleanValue = true,
booleanArray = {true, false},
mood = Mood.SAD,
moodArray = {Mood.CONFUSED, Mood.HAPPY},
nested = @NestedAnno("nested12"),
nestedArray = {@NestedAnno("nested13"), @NestedAnno("nested14")})
@Single(string = "A",
stringArray = {"A", "B"},
classValue = Thread.class,
classArray = {Thread.class, PrivateClass.class},
byteValue = -1,
byteArray = {-1, -2},
charValue = 'A',
charArray = {'a', 'b'},
doubleValue = -3.3D,
doubleArray = {3.3D, 4.4D},
floatValue = -4.4F,
floatArray = {4.4F, 5.5F},
intValue = -5,
intArray = {5, 6},
longValue = -6L,
longArray = {6L, 7L},
shortValue = -7,
shortArray = {7, 8},
booleanValue = true,
booleanArray = {true, false},
mood = Mood.CONFUSED,
moodArray = {Mood.SAD, Mood.CONFUSED},
nested = @NestedAnno("nested15"),
nestedArray = {@NestedAnno("nested16"), @NestedAnno("nested17")})
private static final int annotatedField = 45;
@Retention(RetentionPolicy.RUNTIME)
public @interface NestedAnno {
String value();
}
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface Named {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(SingleList.class)
public @interface Single {
Class<?> classValue();
Class<?>[] classArray();
String string();
String[] stringArray();
byte byteValue();
byte[] byteArray();
char charValue();
char[] charArray();
double doubleValue();
double[] doubleArray();
float floatValue();
float[] floatArray();
int intValue();
int[] intArray();
long longValue();
long[] longArray();
short shortValue();
short[] shortArray();
boolean booleanValue();
boolean[] booleanArray();
Mood mood();
Mood[] moodArray();
NestedAnno nested();
NestedAnno[] nestedArray();
}
@Retention(RetentionPolicy.RUNTIME)
@interface SingleWithDefaults {
Class<?> classValue() default SingleWithDefaults.class;
Class<?>[] classArray() default {};
String string() default "anonymous";
String[] stringArray() default {};
byte byteValue() default 101;
byte[] byteArray() default {};
char charValue() default 'Z';
char[] charArray() default {};
double doubleValue() default 102.102D;
double[] doubleArray() default {};
float floatValue() default 103.103F;
float[] floatArray() default {};
int intValue() default 104;
int[] intArray() default {};
long longValue() default 105L;
long[] longArray() default {};
short shortValue() default 105;
short[] shortArray() default {};
boolean booleanValue() default true;
boolean[] booleanArray() default {};
Mood mood() default Mood.HAPPY;
Mood[] moodArray() default {};
}
@Retention(RetentionPolicy.RUNTIME)
public @interface SingleList {
Single[] value();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Missing {}
@Retention(RetentionPolicy.RUNTIME)
public @interface MissingWrapper {
Missing value();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MissingContainer {
Class<?> value();
}
/**
* Method with a directly missing annotation.
*/
@Missing
public void missingAnnotation() {}
/**
* Method with an indirectly missing nested annotation.
*/
@MissingWrapper(@Missing)
public void missingNestedAnnotation() {}
/**
* Method with an annotation that has a Class member
* that cannot be resolved.
*/
@MissingContainer(Missing.class)
public void missingTypeOfClassMember() {}
/**
* Method with an annotation that has a member
* that is deleted in a newer version of the annotation.
*/
@MemberDeleted(value = "evolving", retained = -34, deleted = 56)
public void missingMember() {}
/**
* Method with an annotation that has a member named "any"
* whose type is changed from int to String in a newer version
* of the annotation.
*/
@MemberTypeChanged(value = "evolving", retained = -34, any = 56)
public void changeTypeOfMember() {}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 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.
*
* 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.internal.vm.test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MemberDeleted {
String value();
int retained();
int deleted();
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 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.
*
* 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.internal.vm.test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MemberTypeChanged {
String value();
int retained();
int any();
}

View File

@ -0,0 +1,257 @@
/*
* Copyright (c) 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.
*
* 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.
*/
/*
* @test
* @compile AnnotationTestInput.java MemberDeleted.java MemberTypeChanged.java
* @modules java.base/jdk.internal.vm
* java.base/sun.reflect.annotation
* @clean jdk.internal.vm.test.AnnotationTestInput$Missing
* @compile alt/MemberDeleted.java alt/MemberTypeChanged.java
* @run testng/othervm
* jdk.internal.vm.test.TestAnnotationEncodingDecoding
*/
package jdk.internal.vm.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import org.testng.Assert;
import org.testng.annotations.Test;
import sun.reflect.annotation.AnnotationSupport;
import sun.reflect.annotation.AnnotationParser;
import sun.reflect.annotation.ExceptionProxy;
import jdk.internal.vm.VMSupport;
import jdk.internal.vm.VMSupport.AnnotationDecoder;
public class TestAnnotationEncodingDecoding {
@Test
public void encodeDecodeTest() throws Exception {
checkDecodedEqualsEncoded(AnnotationTestInput.class.getDeclaredField("annotatedField"));
checkDecodedEqualsEncoded(AnnotationTestInput.class.getDeclaredMethod("annotatedMethod"));
checkDecodedEqualsEncoded(AnnotationTestInput.AnnotatedClass.class);
checkDecodedEqualsEncoded(AnnotationTestInput.class.getDeclaredMethod("missingAnnotation"));
checkDecodedEqualsEncoded(AnnotationTestInput.class.getDeclaredMethod("missingNestedAnnotation"), true);
checkDecodedEqualsEncoded(AnnotationTestInput.class.getDeclaredMethod("missingTypeOfClassMember"), false);
checkDecodedEqualsEncoded(AnnotationTestInput.class.getDeclaredMethod("missingMember"));
checkDecodedEqualsEncoded(AnnotationTestInput.class.getDeclaredMethod("changeTypeOfMember"), false);
}
private void checkDecodedEqualsEncoded(AnnotatedElement annotated) {
checkDecodedEqualsEncoded(annotated, false);
}
private void checkDecodedEqualsEncoded(AnnotatedElement annotated, boolean expectNCDFE) {
Annotation[] annotations = getAnnotations(annotated, expectNCDFE);
if (annotations == null) {
return;
}
byte[] encoded = VMSupport.encodeAnnotations(List.of(annotations));
MyDecoder decoder = new MyDecoder();
List<AnnotationConst> decoded = VMSupport.decodeAnnotations(encoded, decoder);
int i = 0;
for (AnnotationConst actual : decoded) {
AnnotationConst expect = new AnnotationConst(annotations[i]);
checkEquals(actual, expect);
checkEquals(actual.toString(), expect.toString());
i++;
}
}
private static Annotation[] getAnnotations(AnnotatedElement annotated, boolean expectNCDFE) throws AssertionError {
try {
Annotation[] annotations = annotated.getAnnotations();
Assert.assertFalse(expectNCDFE, annotated.toString());
return annotations;
} catch (NoClassDefFoundError e) {
if (!expectNCDFE) {
throw new AssertionError(annotated.toString(), e);
}
return null;
}
}
private static void checkEquals(Object actual, Object expect) {
if (!actual.equals(expect)) {
throw new AssertionError(String.format("actual != expect%nactual: %s%n%nexpect: %s", actual, expect));
}
}
public static final class AnnotationConst {
final Class<?> type;
final Map<String, Object> elements;
AnnotationConst(Class<?> type, Map.Entry<String, Object>[] elements) {
this.type = type;
this.elements = Map.ofEntries(elements);
}
AnnotationConst(Annotation a) {
Map<String, Object> values = AnnotationSupport.memberValues(a);
this.type = a.annotationType();
Map.Entry[] elements = new Map.Entry[values.size()];
int i = 0;
for (Map.Entry<String, Object> e : values.entrySet()) {
elements[i++] = Map.entry(e.getKey(), decodeValue(e.getValue()));
}
this.elements = Map.ofEntries(elements);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AnnotationConst) {
AnnotationConst that = (AnnotationConst) obj;
return this.type.equals(that.type) &&
this.elements.equals(that.elements);
}
return false;
}
@Override
public String toString() {
return "@" + type.getName() + "(" + elements + ")";
}
private Object decodeValue(Object value) {
Class<?> valueType = value.getClass();
if (value instanceof Enum) {
return new EnumConst(valueType, ((Enum<?>) value).name());
} else if (value instanceof Annotation) {
return new AnnotationConst((Annotation) value);
} else if (valueType.isArray()) {
int len = Array.getLength(value);
Object[] arr = new Object[len];
for (int i = 0; i < len; i++) {
arr[i] = decodeValue(Array.get(value, i));
}
return List.of(arr);
} else if (value instanceof ExceptionProxy) {
return new ErrorConst(value.toString());
} else {
return value;
}
}
public Class<?> getType() {
return type;
}
}
public static final class ErrorConst {
final String desc;
public ErrorConst(String desc) {
this.desc = Objects.requireNonNull(desc);
}
@Override
public String toString() {
return desc;
}
@Override
public int hashCode() {
return desc.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ErrorConst) {
return ((ErrorConst) obj).desc.equals(desc);
}
return false;
}
}
public static final class EnumConst {
final Class<?> type;
final String name;
public EnumConst(Class<?> type, String name) {
this.type = type;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof EnumConst) {
EnumConst that = (EnumConst) obj;
return this.type.equals(that.type) &&
this.name.equals(that.name);
}
return false;
}
@Override
public String toString() {
return type.getName() + "." + name;
}
public Class<?> getEnumType() {
return type;
}
public String getName() {
return name;
}
}
static class MyDecoder implements AnnotationDecoder<Class<?>, AnnotationConst, EnumConst, ErrorConst> {
@Override
public Class<?> resolveType(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public AnnotationConst newAnnotation(Class<?> type, Map.Entry<String, Object>[] elements) {
return new AnnotationConst(type, elements);
}
@Override
public EnumConst newEnumValue(Class<?> enumType, String name) {
return new EnumConst(enumType, name);
}
@Override
public ErrorConst newErrorValue(String description) {
return new ErrorConst(description);
}
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 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.
*
* 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.internal.vm.test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MemberDeleted {
String value();
int retained();
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 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.
*
* 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.internal.vm.test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MemberTypeChanged {
String value();
int retained();
String any();
}

View File

@ -58,15 +58,6 @@ public class TestTranslatedException {
}
encodeDecode(throwable);
}
@SuppressWarnings("unchecked")
@Test
public void encodeDecodeTest2() throws Exception {
Throwable throwable = new ExceptionInInitializerError(new InvocationTargetException(new Untranslatable("test exception", new NullPointerException()), "invoke"));
for (int i = 0; i < 10; i++) {
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
}
encodeDecode(throwable);
}
private void encodeDecode(Throwable throwable) throws Exception {
Unsafe unsafe = Unsafe.getUnsafe();