Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_hotspot
提交
d9cf0a49
D
dragonwell8_hotspot
项目概览
openanolis
/
dragonwell8_hotspot
通知
2
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_hotspot
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
d9cf0a49
编写于
10月 22, 2014
作者:
A
aeriksso
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
8057043: Type annotations not retained during class redefine / retransform
Reviewed-by: coleenp, sspitsyn, jfranck
上级
35de3fa1
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
1054 addition
and
17 deletion
+1054
-17
src/share/vm/prims/jvmtiClassFileReconstituter.cpp
src/share/vm/prims/jvmtiClassFileReconstituter.cpp
+22
-0
src/share/vm/prims/jvmtiRedefineClasses.cpp
src/share/vm/prims/jvmtiRedefineClasses.cpp
+605
-17
src/share/vm/prims/jvmtiRedefineClasses.hpp
src/share/vm/prims/jvmtiRedefineClasses.hpp
+17
-0
test/runtime/RedefineTests/RedefineAnnotations.java
test/runtime/RedefineTests/RedefineAnnotations.java
+410
-0
未找到文件。
src/share/vm/prims/jvmtiClassFileReconstituter.cpp
浏览文件 @
d9cf0a49
...
...
@@ -54,6 +54,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
();
...
...
@@ -68,6 +69,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;
...
...
@@ -93,6 +95,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
);
...
...
@@ -110,6 +115,9 @@ void JvmtiClassFileReconstituter::write_field_infos() {
if
(
anno
!=
NULL
)
{
write_annotations_attribute
(
"RuntimeVisibleAnnotations"
,
anno
);
}
if
(
type_anno
!=
NULL
)
{
write_annotations_attribute
(
"RuntimeVisibleTypeAnnotations"
,
type_anno
);
}
}
}
...
...
@@ -550,6 +558,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
())
{
...
...
@@ -585,6 +594,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
)
{
...
...
@@ -609,6 +621,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
...
...
@@ -618,6 +633,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
)
{
...
...
@@ -635,6 +651,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
;
}
...
...
@@ -656,6 +675,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
();
}
...
...
src/share/vm/prims/jvmtiRedefineClasses.cpp
浏览文件 @
d9cf0a49
...
...
@@ -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
());
...
...
src/share/vm/prims/jvmtiRedefineClasses.hpp
浏览文件 @
d9cf0a49
...
...
@@ -457,6 +457,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
,
...
...
@@ -468,6 +479,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
,
...
...
test/runtime/RedefineTests/RedefineAnnotations.java
0 → 100644
浏览文件 @
d9cf0a49
/*
* 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
()
{}
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录