mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8303431: [JVMCI] libgraal annotation API
Reviewed-by: kvn, never, darcy
This commit is contained in:
parent
c57af319f6
commit
48fd4f2bd3
@ -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") \
|
||||
|
||||
@ -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)},
|
||||
|
||||
@ -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) \
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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<T> 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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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 = {
|
||||
};
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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() {}
|
||||
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user