Skip to content

8364729: Improve JVMCI annotation handling #26704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3014,7 +3014,7 @@ C2V_VMENTRY_NULL(jobject, asReflectionField, (JNIEnv* env, jobject, ARGUMENT_PAI
return JNIHandles::make_local(THREAD, reflected);
C2V_END

static jbyteArray get_encoded_annotation_data(InstanceKlass* holder, AnnotationArray* annotations_array, bool for_class,
static jbyteArray get_encoded_annotation_values(InstanceKlass* holder, AnnotationArray* annotations_array, bool for_class,
jint filter_length, jlong filter_klass_pointers,
JavaThread* THREAD, JVMCI_TRAPS) {
// Get a ConstantPool object for annotation parsing
Expand Down Expand Up @@ -3077,26 +3077,26 @@ static jbyteArray get_encoded_annotation_data(InstanceKlass* holder, AnnotationA
return JVMCIENV->get_jbyteArray(ba_dest);
}

C2V_VMENTRY_NULL(jbyteArray, getEncodedClassAnnotationData, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass),
C2V_VMENTRY_NULL(jbyteArray, getEncodedClassAnnotationValues, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass),
jobject filter, jint filter_length, jlong filter_klass_pointers))
CompilerThreadCanCallJava canCallJava(thread, true); // Requires Java support
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);
return get_encoded_annotation_values(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),
C2V_VMENTRY_NULL(jbyteArray, getEncodedExecutableAnnotationValues, (JNIEnv* env, jobject, ARGUMENT_PAIR(method),
jobject filter, jint filter_length, jlong filter_klass_pointers))
CompilerThreadCanCallJava canCallJava(thread, true); // Requires Java support
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);
return get_encoded_annotation_values(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,
C2V_VMENTRY_NULL(jbyteArray, getEncodedFieldAnnotationValues, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass), jint index,
jobject filter, jint filter_length, jlong filter_klass_pointers))
CompilerThreadCanCallJava canCallJava(thread, true); // Requires Java support
InstanceKlass* holder = check_field(InstanceKlass::cast(UNPACK_PAIR(Klass, klass)), index, JVMCI_CHECK_NULL);
fieldDescriptor fd(holder, index);
return get_encoded_annotation_data(holder, fd.annotations(), false, filter_length, filter_klass_pointers, THREAD, JVMCIENV);
return get_encoded_annotation_values(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))
Expand Down Expand Up @@ -3440,9 +3440,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 "getEncodedClassAnnotationValues", CC "(" HS_KLASS2 OBJECT "IJ)[B", FN_PTR(getEncodedClassAnnotationValues)},
{CC "getEncodedExecutableAnnotationValues", CC "(" HS_METHOD2 OBJECT "IJ)[B", FN_PTR(getEncodedExecutableAnnotationValues)},
{CC "getEncodedFieldAnnotationValues", CC "(" HS_KLASS2 "I" OBJECT "IJ)[B", FN_PTR(getEncodedFieldAnnotationValues)},
{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)},
Expand Down
22 changes: 8 additions & 14 deletions src/java.base/share/classes/java/lang/reflect/Method.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
import sun.reflect.generics.scope.MethodScope;
import sun.reflect.annotation.AnnotationType;
import sun.reflect.annotation.AnnotationParser;
import sun.reflect.annotation.EnumValue;
import sun.reflect.annotation.EnumValueArray;
import java.lang.annotation.Annotation;
import java.lang.annotation.AnnotationFormatError;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -197,6 +199,10 @@ byte[] getAnnotationBytes() {
return annotations;
}

byte[] getAnnotationDefaultBytes() {
return annotationDefault;
}

/**
* Returns the {@code Class} object representing the class or interface
* that declares the method represented by this object.
Expand Down Expand Up @@ -750,22 +756,10 @@ void setMethodAccessor(MethodAccessor accessor) {
* @jls 9.6.2 Defaults for Annotation Interface Elements
*/
public Object getDefaultValue() {
if (annotationDefault == null)
if (annotationDefault == null) {
return null;
Class<?> memberType = AnnotationType.invocationHandlerReturnType(
getReturnType());
Object result = AnnotationParser.parseMemberValue(
memberType, ByteBuffer.wrap(annotationDefault),
SharedSecrets.getJavaLangAccess().
getConstantPool(getDeclaringClass()),
getDeclaringClass());
if (result instanceof ExceptionProxy) {
if (result instanceof TypeNotPresentExceptionProxy proxy) {
throw new TypeNotPresentException(proxy.typeName(), proxy.getCause());
}
throw new AnnotationFormatError("Invalid default: " + this);
}
return result;
return AnnotationParser.parseAnnotationDefault(this, annotationDefault, true);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ public Class<?>[] getExecutableSharedExceptionTypes(Executable ex) {
return ex.getSharedExceptionTypes();
}

public byte[] getAnnotationDefaultBytes(Method arg) {
return arg.getAnnotationDefaultBytes();
}

//
// Copying routines, needed to quickly fabricate new Field,
// Method, and Constructor objects from templates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public interface JavaLangReflectAccess {
/** Gets the shared array of exception types of an Executable. */
public Class<?>[] getExecutableSharedExceptionTypes(Executable ex);

/** Gets the value of Method.defaultAnnotation */
public byte[] getAnnotationDefaultBytes(Method arg);

// Copying routines, needed to quickly fabricate new Field,
// Method, and Constructor objects from templates

Expand Down
100 changes: 72 additions & 28 deletions src/java.base/share/classes/jdk/internal/vm/VMSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
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;
Expand All @@ -39,7 +38,7 @@
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.IncompleteAnnotationException;
import java.lang.annotation.AnnotationTypeMismatchException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.LinkedHashMap;
Expand All @@ -48,6 +47,11 @@
import java.util.Set;
import java.util.List;

import sun.reflect.annotation.AnnotationTypeMismatchExceptionProxy;
import sun.reflect.annotation.EnumValue;
import sun.reflect.annotation.EnumValueArray;
import sun.reflect.annotation.TypeNotPresentExceptionProxy;

/*
* Support class used by JVMCI, JVMTI and VM attach mechanism.
*/
Expand Down Expand Up @@ -200,7 +204,7 @@ public static byte[] encodeAnnotations(byte[] rawAnnotations,
}
}
Map<Class<? extends Annotation>, Annotation> annotations =
AnnotationParser.parseSelectAnnotations(rawAnnotations, cp, declaringClass, selectAnnotationClasses);
AnnotationParser.parseSelectAnnotations(rawAnnotations, cp, declaringClass, false, selectAnnotationClasses);
if (forClass && annotations.size() != selectAnnotationClasses.length) {
Class<?> superClass = declaringClass.getSuperclass();
nextSuperClass:
Expand All @@ -211,6 +215,7 @@ public static byte[] encodeAnnotations(byte[] rawAnnotations,
jla.getRawClassAnnotations(superClass),
jla.getConstantPool(superClass),
superClass,
false,
selectAnnotationClasses);

for (Map.Entry<Class<? extends Annotation>, Annotation> e : superAnnotations.entrySet()) {
Expand Down Expand Up @@ -259,8 +264,6 @@ private static void encodeAnnotation(DataOutputStream dos, Annotation a) throws
Object value = e.getValue();
if (value == null) {
// IncompleteAnnotationException
dos.writeByte('x');
dos.writeUTF(new IncompleteAnnotationException(type, e.getKey()).toString());
continue;
}
Class<?> valueType = value.getClass();
Expand Down Expand Up @@ -295,6 +298,21 @@ private static void encodeAnnotation(DataOutputStream dos, Annotation a) throws
} else if (valueType == Class.class) {
dos.writeByte('c');
dos.writeUTF(((Class<?>) value).getName());
} else if (valueType == EnumValue.class) {
EnumValue enumValue = (EnumValue) value;
dos.writeByte('e');
dos.writeUTF(enumValue.enumType.getName());
dos.writeUTF(enumValue.constName);
} else if (valueType == EnumValueArray.class) {
EnumValueArray enumValueArray = (EnumValueArray) value;
List<String> array = enumValueArray.constNames;
dos.writeByte('[');
dos.writeByte('e');
dos.writeUTF(enumValueArray.enumType.getName());
writeLength(dos, array.size());
for (String s : array) {
dos.writeUTF(s);
}
} else if (valueType.isEnum()) {
dos.writeByte('e');
dos.writeUTF(valueType.getName());
Expand Down Expand Up @@ -400,13 +418,16 @@ private static void encodeAnnotation(DataOutputStream dos, Annotation a) throws
encodeAnnotation(dos, annotation);
}
} else {
dos.writeByte('x');
dos.writeUTF(value.toString());
throw new InternalError("Unsupported annotation element component type " + componentType);
}

} else {
} else if (value instanceof TypeNotPresentExceptionProxy proxy) {
dos.writeByte('x');
dos.writeUTF(value.toString());
dos.writeUTF(proxy.typeName());
} else if (value instanceof AnnotationTypeMismatchExceptionProxy proxy) {
dos.writeByte('y');
dos.writeUTF(proxy.foundType());
} else {
throw new InternalError("Unsupported annotation element type " + valueType);
}
}
}
Expand All @@ -418,9 +439,11 @@ private static void encodeAnnotation(DataOutputStream dos, Annotation a) throws
* @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
* @param <EA> type of the object representing a decoded array of enum constants
* @param <MT> type of the object representing a missing type
* @param <ETM> type of the object representing a decoded element type mismatch
*/
public interface AnnotationDecoder<T, A, E, X> {
public interface AnnotationDecoder<T, A, E, EA, MT, ETM> {
/**
* Resolves a name in {@link Class#getName()} format to an object of type {@code T}.
*/
Expand All @@ -435,19 +458,36 @@ public interface AnnotationDecoder<T, A, E, X> {
A newAnnotation(T type, Map.Entry<String, Object>[] elements);

/**
* Creates an object representing a decoded enum constant.
* Creates an object representing a decoded enum.
*
* @param enumType the enum type
* @param name the name of the enum constant
*/
E newEnumValue(T enumType, String name);
E newEnum(T enumType, String name);

/**
* Creates an object representing a decoded error value.
* Creates an object representing a decoded enum array.
*
* @param description of the error
* @param enumType the enum type
* @param names the names of the enum constants
*/
X newErrorValue(String description);
EA newEnumArray(T enumType, List<String> names);

/**
* Creates an object representing a missing type.
*
* @param typeName see {@link TypeNotPresentException#typeName()}
*/
MT newMissingType(String typeName);

/**
* Creates an object representing element of an annotation whose type
* has changed after the annotation was compiled.
*
* @param foundType see {@link AnnotationTypeMismatchException#foundType()}
*/
ETM newElementTypeMismatch(String foundType);

}

/**
Expand All @@ -456,11 +496,12 @@ public interface AnnotationDecoder<T, A, E, X> {
* @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
* @param <MT> type of the object representing a missing type
* @param <ETM> type of the object representing a decoded element type mismatch
* @return an immutable list of {@code A} objects
*/
@SuppressWarnings("unchecked")
public static <T, A, E, X> List<A> decodeAnnotations(byte[] encoded, AnnotationDecoder<T, A, E, X> decoder) {
public static <T, A, E, EA, MT, ETM> List<A> decodeAnnotations(byte[] encoded, AnnotationDecoder<T, A, E, EA, MT, ETM> decoder) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
DataInputStream dis = new DataInputStream(bais);
Expand All @@ -471,7 +512,7 @@ public static <T, A, E, X> List<A> decodeAnnotations(byte[] encoded, AnnotationD
}

@SuppressWarnings({"rawtypes", "unchecked"})
private static <T, A, E, X> A decodeAnnotation(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder) throws IOException {
private static <T, A, E, EA, MT, ETM> A decodeAnnotation(DataInputStream dis, AnnotationDecoder<T, A, E, EA, MT, ETM> decoder) throws IOException {
String typeName = dis.readUTF();
T type = decoder.resolveType(typeName);
int n = readLength(dis);
Expand All @@ -490,10 +531,11 @@ private static <T, A, E, X> A decodeAnnotation(DataInputStream dis, AnnotationDe
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 'e' -> decoder.newEnum(decoder.resolveType(dis.readUTF()), dis.readUTF());
case '@' -> decodeAnnotation(dis, decoder);
case '[' -> decodeArray(dis, decoder);
case 'x' -> decoder.newErrorValue(dis.readUTF());
case 'x' -> decoder.newMissingType(dis.readUTF());
case 'y' -> decoder.newElementTypeMismatch(dis.readUTF());
default -> throw new InternalError("Unsupported tag: " + tag);
});
}
Expand All @@ -504,7 +546,7 @@ interface IOReader {
Object read() throws IOException;
}

private static <T, A, E, X> Object decodeArray(DataInputStream dis, AnnotationDecoder<T, A, E, X> decoder) throws IOException {
private static <T, A, E, EA, MT, ETM> Object decodeArray(DataInputStream dis, AnnotationDecoder<T, A, E, EA, MT, ETM> decoder) throws IOException {
byte componentTag = dis.readByte();
return switch (componentTag) {
case 'B' -> readArray(dis, dis::readByte);
Expand All @@ -519,7 +561,7 @@ private static <T, A, E, X> Object decodeArray(DataInputStream dis, AnnotationDe
case 'c' -> readArray(dis, () -> readClass(dis, decoder));
case 'e' -> {
T enumType = decoder.resolveType(dis.readUTF());
yield readArray(dis, () -> readEnum(dis, decoder, enumType));
yield readEnumArray(dis, decoder, enumType);
}
case '@' -> readArray(dis, () -> decodeAnnotation(dis, decoder));
default -> throw new InternalError("Unsupported component tag: " + componentTag);
Expand All @@ -530,15 +572,17 @@ private static <T, A, E, X> Object decodeArray(DataInputStream dis, AnnotationDe
* 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());
@SuppressWarnings("unchecked")
private static <T, A, E, EA, MT, ETM> EA readEnumArray(DataInputStream dis, AnnotationDecoder<T, A, E, EA, MT, ETM> decoder, T enumType) throws IOException {
List<String> names = (List<String>) readArray(dis, dis::readUTF);
return decoder.newEnumArray(enumType, names);
}

/**
* 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 {
private static <T, A, E, EA, MT, ETM> T readClass(DataInputStream dis, AnnotationDecoder<T, A, E, EA, MT, ETM> decoder) throws IOException {
return decoder.resolveType(dis.readUTF());
}

Expand All @@ -549,7 +593,7 @@ private static <T, A, E, X> T readClass(DataInputStream dis, AnnotationDecoder<T
* @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 {
private static List<?> 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();
Expand Down
Loading