8057043: Type annotations not retained during class redefine / retransform

Reviewed-by: coleenp, sspitsyn, jfranck
This commit is contained in:
Andreas Eriksson 2014-10-22 13:59:56 +02:00
parent 209ffcd9a5
commit ea48bceb5e
4 changed files with 1054 additions and 17 deletions

View file

@ -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());