8057043: Type annotations not retained during class redefine / retransform
Reviewed-by: coleenp, sspitsyn, jfranck
--- a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Wed Oct 22 02:31:25 2014 -0700
+++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Wed Oct 22 13:59:56 2014 +0200
@@ -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 @@
// 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 @@
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 @@
if (anno != NULL) {
write_annotations_attribute("RuntimeVisibleAnnotations", anno);
}
+ if (type_anno != NULL) {
+ write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno);
+ }
}
}
@@ -537,6 +545,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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();
}
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed Oct 22 02:31:25 2014 -0700
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed Oct 22 13:59:56 2014 +0200
@@ -1569,6 +1569,29 @@
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 @@
} // 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::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());
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed Oct 22 02:31:25 2014 -0700
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed Oct 22 13:59:56 2014 +0200
@@ -452,6 +452,17 @@
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 @@
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,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/RedefineTests/RedefineAnnotations.java Wed Oct 22 13:59:56 2014 +0200
@@ -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() {}
+ }
+}