mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-28 11:53:09 +00:00
8057043: Type annotations not retained during class redefine / retransform
Reviewed-by: coleenp, sspitsyn, jfranck
This commit is contained in:
parent
209ffcd9a5
commit
ea48bceb5e
@ -41,6 +41,7 @@
|
||||
void JvmtiClassFileReconstituter::write_field_infos() {
|
||||
HandleMark hm(thread());
|
||||
Array<AnnotationArray*>* fields_anno = ikh()->fields_annotations();
|
||||
Array<AnnotationArray*>* fields_type_anno = ikh()->fields_type_annotations();
|
||||
|
||||
// Compute the real number of Java fields
|
||||
int java_fields = ikh()->java_fields_count();
|
||||
@ -55,6 +56,7 @@ void JvmtiClassFileReconstituter::write_field_infos() {
|
||||
// int offset = ikh()->field_offset( index );
|
||||
int generic_signature_index = fs.generic_signature_index();
|
||||
AnnotationArray* anno = fields_anno == NULL ? NULL : fields_anno->at(fs.index());
|
||||
AnnotationArray* type_anno = fields_type_anno == NULL ? NULL : fields_type_anno->at(fs.index());
|
||||
|
||||
// JVMSpec| field_info {
|
||||
// JVMSpec| u2 access_flags;
|
||||
@ -80,6 +82,9 @@ void JvmtiClassFileReconstituter::write_field_infos() {
|
||||
if (anno != NULL) {
|
||||
++attr_count; // has RuntimeVisibleAnnotations attribute
|
||||
}
|
||||
if (type_anno != NULL) {
|
||||
++attr_count; // has RuntimeVisibleTypeAnnotations attribute
|
||||
}
|
||||
|
||||
write_u2(attr_count);
|
||||
|
||||
@ -97,6 +102,9 @@ void JvmtiClassFileReconstituter::write_field_infos() {
|
||||
if (anno != NULL) {
|
||||
write_annotations_attribute("RuntimeVisibleAnnotations", anno);
|
||||
}
|
||||
if (type_anno != NULL) {
|
||||
write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,6 +545,7 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) {
|
||||
AnnotationArray* anno = method->annotations();
|
||||
AnnotationArray* param_anno = method->parameter_annotations();
|
||||
AnnotationArray* default_anno = method->annotation_default();
|
||||
AnnotationArray* type_anno = method->type_annotations();
|
||||
|
||||
// skip generated default interface methods
|
||||
if (method->is_overpass()) {
|
||||
@ -572,6 +581,9 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) {
|
||||
if (param_anno != NULL) {
|
||||
++attr_count; // has RuntimeVisibleParameterAnnotations attribute
|
||||
}
|
||||
if (type_anno != NULL) {
|
||||
++attr_count; // has RuntimeVisibleTypeAnnotations attribute
|
||||
}
|
||||
|
||||
write_u2(attr_count);
|
||||
if (const_method->code_size() > 0) {
|
||||
@ -596,6 +608,9 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) {
|
||||
if (param_anno != NULL) {
|
||||
write_annotations_attribute("RuntimeVisibleParameterAnnotations", param_anno);
|
||||
}
|
||||
if (type_anno != NULL) {
|
||||
write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the class attributes portion of ClassFile structure
|
||||
@ -605,6 +620,7 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
|
||||
u2 inner_classes_length = inner_classes_attribute_length();
|
||||
Symbol* generic_signature = ikh()->generic_signature();
|
||||
AnnotationArray* anno = ikh()->class_annotations();
|
||||
AnnotationArray* type_anno = ikh()->class_type_annotations();
|
||||
|
||||
int attr_count = 0;
|
||||
if (generic_signature != NULL) {
|
||||
@ -622,6 +638,9 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
|
||||
if (anno != NULL) {
|
||||
++attr_count; // has RuntimeVisibleAnnotations attribute
|
||||
}
|
||||
if (type_anno != NULL) {
|
||||
++attr_count; // has RuntimeVisibleTypeAnnotations attribute
|
||||
}
|
||||
if (cpool()->operands() != NULL) {
|
||||
++attr_count;
|
||||
}
|
||||
@ -643,6 +662,9 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
|
||||
if (anno != NULL) {
|
||||
write_annotations_attribute("RuntimeVisibleAnnotations", anno);
|
||||
}
|
||||
if (type_anno != NULL) {
|
||||
write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno);
|
||||
}
|
||||
if (cpool()->operands() != NULL) {
|
||||
write_bootstrapmethod_attribute();
|
||||
}
|
||||
|
||||
@ -1569,6 +1569,29 @@ bool VM_RedefineClasses::rewrite_cp_refs(instanceKlassHandle scratch_class,
|
||||
return false;
|
||||
}
|
||||
|
||||
// rewrite constant pool references in the class_type_annotations:
|
||||
if (!rewrite_cp_refs_in_class_type_annotations(scratch_class, THREAD)) {
|
||||
// propagate failure back to caller
|
||||
return false;
|
||||
}
|
||||
|
||||
// rewrite constant pool references in the fields_type_annotations:
|
||||
if (!rewrite_cp_refs_in_fields_type_annotations(scratch_class, THREAD)) {
|
||||
// propagate failure back to caller
|
||||
return false;
|
||||
}
|
||||
|
||||
// rewrite constant pool references in the methods_type_annotations:
|
||||
if (!rewrite_cp_refs_in_methods_type_annotations(scratch_class, THREAD)) {
|
||||
// propagate failure back to caller
|
||||
return false;
|
||||
}
|
||||
|
||||
// There can be type annotations in the Code part of a method_info attribute.
|
||||
// These annotations are not accessible, even by reflection.
|
||||
// Currently they are not even parsed by the ClassFileParser.
|
||||
// If runtime access is added they will also need to be rewritten.
|
||||
|
||||
// rewrite source file name index:
|
||||
u2 source_file_name_idx = scratch_class->source_file_name_index();
|
||||
if (source_file_name_idx != 0) {
|
||||
@ -2239,6 +2262,588 @@ bool VM_RedefineClasses::rewrite_cp_refs_in_methods_default_annotations(
|
||||
} // end rewrite_cp_refs_in_methods_default_annotations()
|
||||
|
||||
|
||||
// Rewrite constant pool references in a class_type_annotations field.
|
||||
bool VM_RedefineClasses::rewrite_cp_refs_in_class_type_annotations(
|
||||
instanceKlassHandle scratch_class, TRAPS) {
|
||||
|
||||
AnnotationArray* class_type_annotations = scratch_class->class_type_annotations();
|
||||
if (class_type_annotations == NULL || class_type_annotations->length() == 0) {
|
||||
// no class_type_annotations so nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("class_type_annotations length=%d", class_type_annotations->length()));
|
||||
|
||||
int byte_i = 0; // byte index into class_type_annotations
|
||||
return rewrite_cp_refs_in_type_annotations_typeArray(class_type_annotations,
|
||||
byte_i, "ClassFile", THREAD);
|
||||
} // end rewrite_cp_refs_in_class_type_annotations()
|
||||
|
||||
|
||||
// Rewrite constant pool references in a fields_type_annotations field.
|
||||
bool VM_RedefineClasses::rewrite_cp_refs_in_fields_type_annotations(
|
||||
instanceKlassHandle scratch_class, TRAPS) {
|
||||
|
||||
Array<AnnotationArray*>* fields_type_annotations = scratch_class->fields_type_annotations();
|
||||
if (fields_type_annotations == NULL || fields_type_annotations->length() == 0) {
|
||||
// no fields_type_annotations so nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("fields_type_annotations length=%d", fields_type_annotations->length()));
|
||||
|
||||
for (int i = 0; i < fields_type_annotations->length(); i++) {
|
||||
AnnotationArray* field_type_annotations = fields_type_annotations->at(i);
|
||||
if (field_type_annotations == NULL || field_type_annotations->length() == 0) {
|
||||
// this field does not have any annotations so skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
int byte_i = 0; // byte index into field_type_annotations
|
||||
if (!rewrite_cp_refs_in_type_annotations_typeArray(field_type_annotations,
|
||||
byte_i, "field_info", THREAD)) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("bad field_type_annotations at %d", i));
|
||||
// propagate failure back to caller
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} // end rewrite_cp_refs_in_fields_type_annotations()
|
||||
|
||||
|
||||
// Rewrite constant pool references in a methods_type_annotations field.
|
||||
bool VM_RedefineClasses::rewrite_cp_refs_in_methods_type_annotations(
|
||||
instanceKlassHandle scratch_class, TRAPS) {
|
||||
|
||||
for (int i = 0; i < scratch_class->methods()->length(); i++) {
|
||||
Method* m = scratch_class->methods()->at(i);
|
||||
AnnotationArray* method_type_annotations = m->constMethod()->type_annotations();
|
||||
|
||||
if (method_type_annotations == NULL || method_type_annotations->length() == 0) {
|
||||
// this method does not have any annotations so skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("methods type_annotations length=%d", method_type_annotations->length()));
|
||||
|
||||
int byte_i = 0; // byte index into method_type_annotations
|
||||
if (!rewrite_cp_refs_in_type_annotations_typeArray(method_type_annotations,
|
||||
byte_i, "method_info", THREAD)) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("bad method_type_annotations at %d", i));
|
||||
// propagate failure back to caller
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} // end rewrite_cp_refs_in_methods_type_annotations()
|
||||
|
||||
|
||||
// Rewrite constant pool references in a type_annotations
|
||||
// field. This "structure" is adapted from the
|
||||
// RuntimeVisibleTypeAnnotations_attribute described in
|
||||
// section 4.7.20 of the Java SE 8 Edition of the VM spec:
|
||||
//
|
||||
// type_annotations_typeArray {
|
||||
// u2 num_annotations;
|
||||
// type_annotation annotations[num_annotations];
|
||||
// }
|
||||
//
|
||||
bool VM_RedefineClasses::rewrite_cp_refs_in_type_annotations_typeArray(
|
||||
AnnotationArray* type_annotations_typeArray, int &byte_i_ref,
|
||||
const char * location_mesg, TRAPS) {
|
||||
|
||||
if ((byte_i_ref + 2) > type_annotations_typeArray->length()) {
|
||||
// not enough room for num_annotations field
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for num_annotations field"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u2 num_annotations = Bytes::get_Java_u2((address)
|
||||
type_annotations_typeArray->adr_at(byte_i_ref));
|
||||
byte_i_ref += 2;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("num_type_annotations=%d", num_annotations));
|
||||
|
||||
int calc_num_annotations = 0;
|
||||
for (; calc_num_annotations < num_annotations; calc_num_annotations++) {
|
||||
if (!rewrite_cp_refs_in_type_annotation_struct(type_annotations_typeArray,
|
||||
byte_i_ref, location_mesg, THREAD)) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("bad type_annotation_struct at %d", calc_num_annotations));
|
||||
// propagate failure back to caller
|
||||
return false;
|
||||
}
|
||||
}
|
||||
assert(num_annotations == calc_num_annotations, "sanity check");
|
||||
|
||||
if (byte_i_ref != type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("read wrong amount of bytes at end of processing "
|
||||
"type_annotations_typeArray (%d of %d bytes were read)",
|
||||
byte_i_ref, type_annotations_typeArray->length()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} // end rewrite_cp_refs_in_type_annotations_typeArray()
|
||||
|
||||
|
||||
// Rewrite constant pool references in a type_annotation
|
||||
// field. This "structure" is adapted from the
|
||||
// RuntimeVisibleTypeAnnotations_attribute described in
|
||||
// section 4.7.20 of the Java SE 8 Edition of the VM spec:
|
||||
//
|
||||
// type_annotation {
|
||||
// u1 target_type;
|
||||
// union {
|
||||
// type_parameter_target;
|
||||
// supertype_target;
|
||||
// type_parameter_bound_target;
|
||||
// empty_target;
|
||||
// method_formal_parameter_target;
|
||||
// throws_target;
|
||||
// localvar_target;
|
||||
// catch_target;
|
||||
// offset_target;
|
||||
// type_argument_target;
|
||||
// } target_info;
|
||||
// type_path target_path;
|
||||
// annotation anno;
|
||||
// }
|
||||
//
|
||||
bool VM_RedefineClasses::rewrite_cp_refs_in_type_annotation_struct(
|
||||
AnnotationArray* type_annotations_typeArray, int &byte_i_ref,
|
||||
const char * location_mesg, TRAPS) {
|
||||
|
||||
if (!skip_type_annotation_target(type_annotations_typeArray,
|
||||
byte_i_ref, location_mesg, THREAD)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!skip_type_annotation_type_path(type_annotations_typeArray,
|
||||
byte_i_ref, THREAD)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rewrite_cp_refs_in_annotation_struct(type_annotations_typeArray,
|
||||
byte_i_ref, THREAD)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} // end rewrite_cp_refs_in_type_annotation_struct()
|
||||
|
||||
|
||||
// Read, verify and skip over the target_type and target_info part
|
||||
// so that rewriting can continue in the later parts of the struct.
|
||||
//
|
||||
// u1 target_type;
|
||||
// union {
|
||||
// type_parameter_target;
|
||||
// supertype_target;
|
||||
// type_parameter_bound_target;
|
||||
// empty_target;
|
||||
// method_formal_parameter_target;
|
||||
// throws_target;
|
||||
// localvar_target;
|
||||
// catch_target;
|
||||
// offset_target;
|
||||
// type_argument_target;
|
||||
// } target_info;
|
||||
//
|
||||
bool VM_RedefineClasses::skip_type_annotation_target(
|
||||
AnnotationArray* type_annotations_typeArray, int &byte_i_ref,
|
||||
const char * location_mesg, TRAPS) {
|
||||
|
||||
if ((byte_i_ref + 1) > type_annotations_typeArray->length()) {
|
||||
// not enough room for a target_type let alone the rest of a type_annotation
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a target_type"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u1 target_type = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("target_type=0x%.2x", target_type));
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("location=%s", location_mesg));
|
||||
|
||||
// Skip over target_info
|
||||
switch (target_type) {
|
||||
case 0x00:
|
||||
// kind: type parameter declaration of generic class or interface
|
||||
// location: ClassFile
|
||||
case 0x01:
|
||||
// kind: type parameter declaration of generic method or constructor
|
||||
// location: method_info
|
||||
|
||||
{
|
||||
// struct:
|
||||
// type_parameter_target {
|
||||
// u1 type_parameter_index;
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 1) > type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a type_parameter_target"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u1 type_parameter_index = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("type_parameter_target: type_parameter_index=%d",
|
||||
type_parameter_index));
|
||||
} break;
|
||||
|
||||
case 0x10:
|
||||
// kind: type in extends clause of class or interface declaration
|
||||
// (including the direct superclass of an anonymous class declaration),
|
||||
// or in implements clause of interface declaration
|
||||
// location: ClassFile
|
||||
|
||||
{
|
||||
// struct:
|
||||
// supertype_target {
|
||||
// u2 supertype_index;
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 2) > type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a supertype_target"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u2 supertype_index = Bytes::get_Java_u2((address)
|
||||
type_annotations_typeArray->adr_at(byte_i_ref));
|
||||
byte_i_ref += 2;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("supertype_target: supertype_index=%d", supertype_index));
|
||||
} break;
|
||||
|
||||
case 0x11:
|
||||
// kind: type in bound of type parameter declaration of generic class or interface
|
||||
// location: ClassFile
|
||||
case 0x12:
|
||||
// kind: type in bound of type parameter declaration of generic method or constructor
|
||||
// location: method_info
|
||||
|
||||
{
|
||||
// struct:
|
||||
// type_parameter_bound_target {
|
||||
// u1 type_parameter_index;
|
||||
// u1 bound_index;
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 2) > type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a type_parameter_bound_target"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u1 type_parameter_index = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
u1 bound_index = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("type_parameter_bound_target: type_parameter_index=%d, bound_index=%d",
|
||||
type_parameter_index, bound_index));
|
||||
} break;
|
||||
|
||||
case 0x13:
|
||||
// kind: type in field declaration
|
||||
// location: field_info
|
||||
case 0x14:
|
||||
// kind: return type of method, or type of newly constructed object
|
||||
// location: method_info
|
||||
case 0x15:
|
||||
// kind: receiver type of method or constructor
|
||||
// location: method_info
|
||||
|
||||
{
|
||||
// struct:
|
||||
// empty_target {
|
||||
// }
|
||||
//
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("empty_target"));
|
||||
} break;
|
||||
|
||||
case 0x16:
|
||||
// kind: type in formal parameter declaration of method, constructor, or lambda expression
|
||||
// location: method_info
|
||||
|
||||
{
|
||||
// struct:
|
||||
// formal_parameter_target {
|
||||
// u1 formal_parameter_index;
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 1) > type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a formal_parameter_target"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u1 formal_parameter_index = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("formal_parameter_target: formal_parameter_index=%d",
|
||||
formal_parameter_index));
|
||||
} break;
|
||||
|
||||
case 0x17:
|
||||
// kind: type in throws clause of method or constructor
|
||||
// location: method_info
|
||||
|
||||
{
|
||||
// struct:
|
||||
// throws_target {
|
||||
// u2 throws_type_index
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 2) > type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a throws_target"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u2 throws_type_index = Bytes::get_Java_u2((address)
|
||||
type_annotations_typeArray->adr_at(byte_i_ref));
|
||||
byte_i_ref += 2;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("throws_target: throws_type_index=%d", throws_type_index));
|
||||
} break;
|
||||
|
||||
case 0x40:
|
||||
// kind: type in local variable declaration
|
||||
// location: Code
|
||||
case 0x41:
|
||||
// kind: type in resource variable declaration
|
||||
// location: Code
|
||||
|
||||
{
|
||||
// struct:
|
||||
// localvar_target {
|
||||
// u2 table_length;
|
||||
// struct {
|
||||
// u2 start_pc;
|
||||
// u2 length;
|
||||
// u2 index;
|
||||
// } table[table_length];
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 2) > type_annotations_typeArray->length()) {
|
||||
// not enough room for a table_length let alone the rest of a localvar_target
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a localvar_target table_length"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u2 table_length = Bytes::get_Java_u2((address)
|
||||
type_annotations_typeArray->adr_at(byte_i_ref));
|
||||
byte_i_ref += 2;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("localvar_target: table_length=%d", table_length));
|
||||
|
||||
int table_struct_size = 2 + 2 + 2; // 3 u2 variables per table entry
|
||||
int table_size = table_length * table_struct_size;
|
||||
|
||||
if ((byte_i_ref + table_size) > type_annotations_typeArray->length()) {
|
||||
// not enough room for a table
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a table array of length %d", table_length));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip over table
|
||||
byte_i_ref += table_size;
|
||||
} break;
|
||||
|
||||
case 0x42:
|
||||
// kind: type in exception parameter declaration
|
||||
// location: Code
|
||||
|
||||
{
|
||||
// struct:
|
||||
// catch_target {
|
||||
// u2 exception_table_index;
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 2) > type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a catch_target"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u2 exception_table_index = Bytes::get_Java_u2((address)
|
||||
type_annotations_typeArray->adr_at(byte_i_ref));
|
||||
byte_i_ref += 2;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("catch_target: exception_table_index=%d", exception_table_index));
|
||||
} break;
|
||||
|
||||
case 0x43:
|
||||
// kind: type in instanceof expression
|
||||
// location: Code
|
||||
case 0x44:
|
||||
// kind: type in new expression
|
||||
// location: Code
|
||||
case 0x45:
|
||||
// kind: type in method reference expression using ::new
|
||||
// location: Code
|
||||
case 0x46:
|
||||
// kind: type in method reference expression using ::Identifier
|
||||
// location: Code
|
||||
|
||||
{
|
||||
// struct:
|
||||
// offset_target {
|
||||
// u2 offset;
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 2) > type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a offset_target"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u2 offset = Bytes::get_Java_u2((address)
|
||||
type_annotations_typeArray->adr_at(byte_i_ref));
|
||||
byte_i_ref += 2;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("offset_target: offset=%d", offset));
|
||||
} break;
|
||||
|
||||
case 0x47:
|
||||
// kind: type in cast expression
|
||||
// location: Code
|
||||
case 0x48:
|
||||
// kind: type argument for generic constructor in new expression or
|
||||
// explicit constructor invocation statement
|
||||
// location: Code
|
||||
case 0x49:
|
||||
// kind: type argument for generic method in method invocation expression
|
||||
// location: Code
|
||||
case 0x4A:
|
||||
// kind: type argument for generic constructor in method reference expression using ::new
|
||||
// location: Code
|
||||
case 0x4B:
|
||||
// kind: type argument for generic method in method reference expression using ::Identifier
|
||||
// location: Code
|
||||
|
||||
{
|
||||
// struct:
|
||||
// type_argument_target {
|
||||
// u2 offset;
|
||||
// u1 type_argument_index;
|
||||
// }
|
||||
//
|
||||
if ((byte_i_ref + 3) > type_annotations_typeArray->length()) {
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a type_argument_target"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u2 offset = Bytes::get_Java_u2((address)
|
||||
type_annotations_typeArray->adr_at(byte_i_ref));
|
||||
byte_i_ref += 2;
|
||||
u1 type_argument_index = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("type_argument_target: offset=%d, type_argument_index=%d",
|
||||
offset, type_argument_index));
|
||||
} break;
|
||||
|
||||
default:
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("unknown target_type"));
|
||||
#ifdef ASSERT
|
||||
ShouldNotReachHere();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} // end skip_type_annotation_target()
|
||||
|
||||
|
||||
// Read, verify and skip over the type_path part so that rewriting
|
||||
// can continue in the later parts of the struct.
|
||||
//
|
||||
// type_path {
|
||||
// u1 path_length;
|
||||
// {
|
||||
// u1 type_path_kind;
|
||||
// u1 type_argument_index;
|
||||
// } path[path_length];
|
||||
// }
|
||||
//
|
||||
bool VM_RedefineClasses::skip_type_annotation_type_path(
|
||||
AnnotationArray* type_annotations_typeArray, int &byte_i_ref, TRAPS) {
|
||||
|
||||
if ((byte_i_ref + 1) > type_annotations_typeArray->length()) {
|
||||
// not enough room for a path_length let alone the rest of the type_path
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for a type_path"));
|
||||
return false;
|
||||
}
|
||||
|
||||
u1 path_length = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("type_path: path_length=%d", path_length));
|
||||
|
||||
int calc_path_length = 0;
|
||||
for (; calc_path_length < path_length; calc_path_length++) {
|
||||
if ((byte_i_ref + 1 + 1) > type_annotations_typeArray->length()) {
|
||||
// not enough room for a path
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("length() is too small for path entry %d of %d",
|
||||
calc_path_length, path_length));
|
||||
return false;
|
||||
}
|
||||
|
||||
u1 type_path_kind = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
u1 type_argument_index = type_annotations_typeArray->at(byte_i_ref);
|
||||
byte_i_ref += 1;
|
||||
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("type_path: path[%d]: type_path_kind=%d, type_argument_index=%d",
|
||||
calc_path_length, type_path_kind, type_argument_index));
|
||||
|
||||
if (type_path_kind > 3 || (type_path_kind != 3 && type_argument_index != 0)) {
|
||||
// not enough room for a path
|
||||
RC_TRACE_WITH_THREAD(0x02000000, THREAD,
|
||||
("inconsistent type_path values"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
assert(path_length == calc_path_length, "sanity check");
|
||||
|
||||
return true;
|
||||
} // end skip_type_annotation_type_path()
|
||||
|
||||
|
||||
// Rewrite constant pool references in the method's stackmap table.
|
||||
// These "structures" are adapted from the StackMapTable_attribute that
|
||||
// is described in section 4.8.4 of the 6.0 version of the VM spec
|
||||
@ -3223,23 +3828,6 @@ void VM_RedefineClasses::compute_added_deleted_matching_methods() {
|
||||
|
||||
void VM_RedefineClasses::swap_annotations(instanceKlassHandle the_class,
|
||||
instanceKlassHandle scratch_class) {
|
||||
// Since there is currently no rewriting of type annotations indexes
|
||||
// into the CP, we null out type annotations on scratch_class before
|
||||
// we swap annotations with the_class rather than facing the
|
||||
// possibility of shipping annotations with broken indexes to
|
||||
// Java-land.
|
||||
ClassLoaderData* loader_data = scratch_class->class_loader_data();
|
||||
AnnotationArray* new_class_type_annotations = scratch_class->class_type_annotations();
|
||||
if (new_class_type_annotations != NULL) {
|
||||
MetadataFactory::free_array<u1>(loader_data, new_class_type_annotations);
|
||||
scratch_class->annotations()->set_class_type_annotations(NULL);
|
||||
}
|
||||
Array<AnnotationArray*>* new_field_type_annotations = scratch_class->fields_type_annotations();
|
||||
if (new_field_type_annotations != NULL) {
|
||||
Annotations::free_contents(loader_data, new_field_type_annotations);
|
||||
scratch_class->annotations()->set_fields_type_annotations(NULL);
|
||||
}
|
||||
|
||||
// Swap annotation fields values
|
||||
Annotations* old_annotations = the_class->annotations();
|
||||
the_class->set_annotations(scratch_class->annotations());
|
||||
|
||||
@ -452,6 +452,17 @@ class VM_RedefineClasses: public VM_Operation {
|
||||
instanceKlassHandle scratch_class, TRAPS);
|
||||
bool rewrite_cp_refs_in_element_value(
|
||||
AnnotationArray* class_annotations, int &byte_i_ref, TRAPS);
|
||||
bool rewrite_cp_refs_in_type_annotations_typeArray(
|
||||
AnnotationArray* type_annotations_typeArray, int &byte_i_ref,
|
||||
const char * location_mesg, TRAPS);
|
||||
bool rewrite_cp_refs_in_type_annotation_struct(
|
||||
AnnotationArray* type_annotations_typeArray, int &byte_i_ref,
|
||||
const char * location_mesg, TRAPS);
|
||||
bool skip_type_annotation_target(
|
||||
AnnotationArray* type_annotations_typeArray, int &byte_i_ref,
|
||||
const char * location_mesg, TRAPS);
|
||||
bool skip_type_annotation_type_path(
|
||||
AnnotationArray* type_annotations_typeArray, int &byte_i_ref, TRAPS);
|
||||
bool rewrite_cp_refs_in_fields_annotations(
|
||||
instanceKlassHandle scratch_class, TRAPS);
|
||||
void rewrite_cp_refs_in_method(methodHandle method,
|
||||
@ -463,6 +474,12 @@ class VM_RedefineClasses: public VM_Operation {
|
||||
instanceKlassHandle scratch_class, TRAPS);
|
||||
bool rewrite_cp_refs_in_methods_parameter_annotations(
|
||||
instanceKlassHandle scratch_class, TRAPS);
|
||||
bool rewrite_cp_refs_in_class_type_annotations(
|
||||
instanceKlassHandle scratch_class, TRAPS);
|
||||
bool rewrite_cp_refs_in_fields_type_annotations(
|
||||
instanceKlassHandle scratch_class, TRAPS);
|
||||
bool rewrite_cp_refs_in_methods_type_annotations(
|
||||
instanceKlassHandle scratch_class, TRAPS);
|
||||
void rewrite_cp_refs_in_stack_map_table(methodHandle method, TRAPS);
|
||||
void rewrite_cp_refs_in_verification_type_info(
|
||||
address& stackmap_addr_ref, address stackmap_end, u2 frame_i,
|
||||
|
||||
410
hotspot/test/runtime/RedefineTests/RedefineAnnotations.java
Normal file
410
hotspot/test/runtime/RedefineTests/RedefineAnnotations.java
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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
|
||||
* @library /testlibrary
|
||||
* @summary Test that type annotations are retained after a retransform
|
||||
* @run main RedefineAnnotations buildagent
|
||||
* @run main/othervm -javaagent:redefineagent.jar RedefineAnnotations
|
||||
*/
|
||||
|
||||
import static com.oracle.java.testlibrary.Asserts.assertTrue;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.NoSuchFieldException;
|
||||
import java.lang.NoSuchMethodException;
|
||||
import java.lang.RuntimeException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.instrument.UnmodifiableClassException;
|
||||
import java.lang.reflect.AnnotatedArrayType;
|
||||
import java.lang.reflect.AnnotatedParameterizedType;
|
||||
import java.lang.reflect.AnnotatedType;
|
||||
import java.lang.reflect.AnnotatedWildcardType;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.FieldVisitor;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ASM5;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE_USE)
|
||||
@interface TestAnn {
|
||||
String site();
|
||||
}
|
||||
|
||||
public class RedefineAnnotations {
|
||||
static Instrumentation inst;
|
||||
public static void premain(String agentArgs, Instrumentation inst) {
|
||||
RedefineAnnotations.inst = inst;
|
||||
}
|
||||
|
||||
static class Transformer implements ClassFileTransformer {
|
||||
|
||||
public byte[] asm(ClassLoader loader, String className,
|
||||
Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain, byte[] classfileBuffer)
|
||||
throws IllegalClassFormatException {
|
||||
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
ClassVisitor cv = new ReAddDummyFieldsClassVisitor(ASM5, cw) { };
|
||||
ClassReader cr = new ClassReader(classfileBuffer);
|
||||
cr.accept(cv, 0);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
public class ReAddDummyFieldsClassVisitor extends ClassVisitor {
|
||||
|
||||
LinkedList<F> fields = new LinkedList<>();
|
||||
|
||||
public ReAddDummyFieldsClassVisitor(int api, ClassVisitor cv) {
|
||||
super(api, cv);
|
||||
}
|
||||
|
||||
@Override public FieldVisitor visitField(int access, String name,
|
||||
String desc, String signature, Object value) {
|
||||
if (name.startsWith("dummy")) {
|
||||
// Remove dummy field
|
||||
fields.addLast(new F(access, name, desc, signature, value));
|
||||
return null;
|
||||
}
|
||||
return cv.visitField(access, name, desc, signature, value);
|
||||
}
|
||||
|
||||
@Override public void visitEnd() {
|
||||
F f;
|
||||
while ((f = fields.pollFirst()) != null) {
|
||||
// Re-add dummy fields
|
||||
cv.visitField(f.access, f.name, f.desc, f.signature, f.value);
|
||||
}
|
||||
}
|
||||
|
||||
private class F {
|
||||
private int access;
|
||||
private String name;
|
||||
private String desc;
|
||||
private String signature;
|
||||
private Object value;
|
||||
F(int access, String name, String desc, String signature, Object value) {
|
||||
this.access = access;
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
this.signature = signature;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public byte[] transform(ClassLoader loader, String className,
|
||||
Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain, byte[] classfileBuffer)
|
||||
throws IllegalClassFormatException {
|
||||
|
||||
if (className.contains("TypeAnnotatedTestClass")) {
|
||||
try {
|
||||
// Here we remove and re-add the dummy fields. This shuffles the constant pool
|
||||
return asm(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
|
||||
} catch (Throwable e) {
|
||||
// The retransform native code that called this method does not propagate
|
||||
// exceptions. Instead of getting an uninformative generic error, catch
|
||||
// problems here and print it, then exit.
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildAgent() {
|
||||
try {
|
||||
ClassFileInstaller.main("RedefineAnnotations");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not write agent classfile", e);
|
||||
}
|
||||
|
||||
try {
|
||||
PrintWriter pw = new PrintWriter("MANIFEST.MF");
|
||||
pw.println("Premain-Class: RedefineAnnotations");
|
||||
pw.println("Agent-Class: RedefineAnnotations");
|
||||
pw.println("Can-Retransform-Classes: true");
|
||||
pw.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("Could not write manifest file for the agent", e);
|
||||
}
|
||||
|
||||
sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar");
|
||||
if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "RedefineAnnotations.class" })) {
|
||||
throw new RuntimeException("Could not write the agent jar file");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String argv[]) throws NoSuchFieldException, NoSuchMethodException {
|
||||
if (argv.length == 1 && argv[0].equals("buildagent")) {
|
||||
buildAgent();
|
||||
return;
|
||||
}
|
||||
|
||||
if (inst == null) {
|
||||
throw new RuntimeException("Instrumentation object was null");
|
||||
}
|
||||
|
||||
RedefineAnnotations test = new RedefineAnnotations();
|
||||
test.testTransformAndVerify();
|
||||
}
|
||||
|
||||
// Class type annotations
|
||||
private Annotation classTypeParameterTA;
|
||||
private Annotation extendsTA;
|
||||
private Annotation implementsTA;
|
||||
|
||||
// Field type annotations
|
||||
private Annotation fieldTA;
|
||||
private Annotation innerTA;
|
||||
private Annotation[] arrayTA = new Annotation[4];
|
||||
private Annotation[] mapTA = new Annotation[5];
|
||||
|
||||
// Method type annotations
|
||||
private Annotation returnTA, methodTypeParameterTA, formalParameterTA, throwsTA;
|
||||
|
||||
private void testTransformAndVerify()
|
||||
throws NoSuchFieldException, NoSuchMethodException {
|
||||
|
||||
Class<TypeAnnotatedTestClass> c = TypeAnnotatedTestClass.class;
|
||||
Class<?> myClass = c;
|
||||
|
||||
/*
|
||||
* Verify that the expected annotations are where they should be before transform.
|
||||
*/
|
||||
verifyClassTypeAnnotations(c);
|
||||
verifyFieldTypeAnnotations(c);
|
||||
verifyMethodTypeAnnotations(c);
|
||||
|
||||
try {
|
||||
inst.addTransformer(new Transformer(), true);
|
||||
inst.retransformClasses(myClass);
|
||||
} catch (UnmodifiableClassException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that the expected annotations are where they should be after transform.
|
||||
* Also verify that before and after are equal.
|
||||
*/
|
||||
verifyClassTypeAnnotations(c);
|
||||
verifyFieldTypeAnnotations(c);
|
||||
verifyMethodTypeAnnotations(c);
|
||||
}
|
||||
|
||||
private void verifyClassTypeAnnotations(Class c) {
|
||||
Annotation anno;
|
||||
|
||||
anno = c.getTypeParameters()[0].getAnnotations()[0];
|
||||
verifyTestAnn(classTypeParameterTA, anno, "classTypeParameter");
|
||||
classTypeParameterTA = anno;
|
||||
|
||||
anno = c.getAnnotatedSuperclass().getAnnotations()[0];
|
||||
verifyTestAnn(extendsTA, anno, "extends");
|
||||
extendsTA = anno;
|
||||
|
||||
anno = c.getAnnotatedInterfaces()[0].getAnnotations()[0];
|
||||
verifyTestAnn(implementsTA, anno, "implements");
|
||||
implementsTA = anno;
|
||||
}
|
||||
|
||||
private void verifyFieldTypeAnnotations(Class c)
|
||||
throws NoSuchFieldException, NoSuchMethodException {
|
||||
|
||||
verifyBasicFieldTypeAnnotations(c);
|
||||
verifyInnerFieldTypeAnnotations(c);
|
||||
verifyArrayFieldTypeAnnotations(c);
|
||||
verifyMapFieldTypeAnnotations(c);
|
||||
}
|
||||
|
||||
private void verifyBasicFieldTypeAnnotations(Class c)
|
||||
throws NoSuchFieldException, NoSuchMethodException {
|
||||
|
||||
Annotation anno = c.getDeclaredField("typeAnnotatedBoolean").getAnnotatedType().getAnnotations()[0];
|
||||
verifyTestAnn(fieldTA, anno, "field");
|
||||
fieldTA = anno;
|
||||
}
|
||||
|
||||
private void verifyInnerFieldTypeAnnotations(Class c)
|
||||
throws NoSuchFieldException, NoSuchMethodException {
|
||||
|
||||
AnnotatedType at = c.getDeclaredField("typeAnnotatedInner").getAnnotatedType();
|
||||
Annotation anno = at.getAnnotations()[0];
|
||||
verifyTestAnn(innerTA, anno, "inner");
|
||||
innerTA = anno;
|
||||
}
|
||||
|
||||
private void verifyArrayFieldTypeAnnotations(Class c)
|
||||
throws NoSuchFieldException, NoSuchMethodException {
|
||||
|
||||
Annotation anno;
|
||||
AnnotatedType at;
|
||||
|
||||
at = c.getDeclaredField("typeAnnotatedArray").getAnnotatedType();
|
||||
anno = at.getAnnotations()[0];
|
||||
verifyTestAnn(arrayTA[0], anno, "array1");
|
||||
arrayTA[0] = anno;
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
at = ((AnnotatedArrayType) at).getAnnotatedGenericComponentType();
|
||||
anno = at.getAnnotations()[0];
|
||||
verifyTestAnn(arrayTA[i], anno, "array" + (i + 1));
|
||||
arrayTA[i] = anno;
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyMapFieldTypeAnnotations(Class c)
|
||||
throws NoSuchFieldException, NoSuchMethodException {
|
||||
|
||||
Annotation anno;
|
||||
AnnotatedType atBase;
|
||||
AnnotatedType atParameter;
|
||||
atBase = c.getDeclaredField("typeAnnotatedMap").getAnnotatedType();
|
||||
|
||||
anno = atBase.getAnnotations()[0];
|
||||
verifyTestAnn(mapTA[0], anno, "map1");
|
||||
mapTA[0] = anno;
|
||||
|
||||
atParameter =
|
||||
((AnnotatedParameterizedType) atBase).
|
||||
getAnnotatedActualTypeArguments()[0];
|
||||
anno = ((AnnotatedWildcardType) atParameter).getAnnotations()[0];
|
||||
verifyTestAnn(mapTA[1], anno, "map2");
|
||||
mapTA[1] = anno;
|
||||
|
||||
anno =
|
||||
((AnnotatedWildcardType) atParameter).
|
||||
getAnnotatedUpperBounds()[0].getAnnotations()[0];
|
||||
verifyTestAnn(mapTA[2], anno, "map3");
|
||||
mapTA[2] = anno;
|
||||
|
||||
atParameter =
|
||||
((AnnotatedParameterizedType) atBase).
|
||||
getAnnotatedActualTypeArguments()[1];
|
||||
anno = ((AnnotatedParameterizedType) atParameter).getAnnotations()[0];
|
||||
verifyTestAnn(mapTA[3], anno, "map4");
|
||||
mapTA[3] = anno;
|
||||
|
||||
anno =
|
||||
((AnnotatedParameterizedType) atParameter).
|
||||
getAnnotatedActualTypeArguments()[0].getAnnotations()[0];
|
||||
verifyTestAnn(mapTA[4], anno, "map5");
|
||||
mapTA[4] = anno;
|
||||
}
|
||||
|
||||
private void verifyMethodTypeAnnotations(Class c)
|
||||
throws NoSuchFieldException, NoSuchMethodException {
|
||||
Annotation anno;
|
||||
Executable typeAnnotatedMethod =
|
||||
c.getDeclaredMethod("typeAnnotatedMethod", TypeAnnotatedTestClass.class);
|
||||
|
||||
anno = typeAnnotatedMethod.getAnnotatedReturnType().getAnnotations()[0];
|
||||
verifyTestAnn(returnTA, anno, "return");
|
||||
returnTA = anno;
|
||||
|
||||
anno = typeAnnotatedMethod.getTypeParameters()[0].getAnnotations()[0];
|
||||
verifyTestAnn(methodTypeParameterTA, anno, "methodTypeParameter");
|
||||
methodTypeParameterTA = anno;
|
||||
|
||||
anno = typeAnnotatedMethod.getAnnotatedParameterTypes()[0].getAnnotations()[0];
|
||||
verifyTestAnn(formalParameterTA, anno, "formalParameter");
|
||||
formalParameterTA = anno;
|
||||
|
||||
anno = typeAnnotatedMethod.getAnnotatedExceptionTypes()[0].getAnnotations()[0];
|
||||
verifyTestAnn(throwsTA, anno, "throws");
|
||||
throwsTA = anno;
|
||||
}
|
||||
|
||||
private static void verifyTestAnn(Annotation verifyAgainst, Annotation anno, String expectedSite) {
|
||||
verifyTestAnnSite(anno, expectedSite);
|
||||
|
||||
// When called before transform verifyAgainst will be null, when called
|
||||
// after transform it will be the annotation from before the transform
|
||||
if (verifyAgainst != null) {
|
||||
assertTrue(anno.equals(verifyAgainst),
|
||||
"Annotations do not match before and after." +
|
||||
" Before: \"" + verifyAgainst + "\", After: \"" + anno + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyTestAnnSite(Annotation testAnn, String expectedSite) {
|
||||
String expectedAnn = "@TestAnn(site=" + expectedSite + ")";
|
||||
assertTrue(testAnn.toString().equals(expectedAnn),
|
||||
"Expected \"" + expectedAnn + "\", got \"" + testAnn + "\"");
|
||||
}
|
||||
|
||||
public static class TypeAnnotatedTestClass <@TestAnn(site="classTypeParameter") S,T>
|
||||
extends @TestAnn(site="extends") Thread
|
||||
implements @TestAnn(site="implements") Runnable {
|
||||
|
||||
public @TestAnn(site="field") boolean typeAnnotatedBoolean;
|
||||
|
||||
public
|
||||
RedefineAnnotations.
|
||||
@TestAnn(site="inner") TypeAnnotatedTestClass
|
||||
typeAnnotatedInner;
|
||||
|
||||
public
|
||||
@TestAnn(site="array4") boolean
|
||||
@TestAnn(site="array1") []
|
||||
@TestAnn(site="array2") []
|
||||
@TestAnn(site="array3") []
|
||||
typeAnnotatedArray;
|
||||
|
||||
public @TestAnn(site="map1") Map
|
||||
<@TestAnn(site="map2") ? extends @TestAnn(site="map3") String,
|
||||
@TestAnn(site="map4") List<@TestAnn(site="map5") Object>> typeAnnotatedMap;
|
||||
|
||||
public int dummy1;
|
||||
public int dummy2;
|
||||
public int dummy3;
|
||||
|
||||
@TestAnn(site="return") <@TestAnn(site="methodTypeParameter") U,V> Class
|
||||
typeAnnotatedMethod(@TestAnn(site="formalParameter") TypeAnnotatedTestClass arg)
|
||||
throws @TestAnn(site="throws") ClassNotFoundException {
|
||||
|
||||
@TestAnn(site="local_variable_type") int foo = 0;
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
|
||||
public void run() {}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user