提交 d9cf0a49 编写于 作者: A aeriksso

8057043: Type annotations not retained during class redefine / retransform

Reviewed-by: coleenp, sspitsyn, jfranck
上级 35de3fa1
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
void JvmtiClassFileReconstituter::write_field_infos() { void JvmtiClassFileReconstituter::write_field_infos() {
HandleMark hm(thread()); HandleMark hm(thread());
Array<AnnotationArray*>* fields_anno = ikh()->fields_annotations(); Array<AnnotationArray*>* fields_anno = ikh()->fields_annotations();
Array<AnnotationArray*>* fields_type_anno = ikh()->fields_type_annotations();
// Compute the real number of Java fields // Compute the real number of Java fields
int java_fields = ikh()->java_fields_count(); int java_fields = ikh()->java_fields_count();
...@@ -68,6 +69,7 @@ void JvmtiClassFileReconstituter::write_field_infos() { ...@@ -68,6 +69,7 @@ void JvmtiClassFileReconstituter::write_field_infos() {
// int offset = ikh()->field_offset( index ); // int offset = ikh()->field_offset( index );
int generic_signature_index = fs.generic_signature_index(); int generic_signature_index = fs.generic_signature_index();
AnnotationArray* anno = fields_anno == NULL ? NULL : fields_anno->at(fs.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| field_info {
// JVMSpec| u2 access_flags; // JVMSpec| u2 access_flags;
...@@ -93,6 +95,9 @@ void JvmtiClassFileReconstituter::write_field_infos() { ...@@ -93,6 +95,9 @@ void JvmtiClassFileReconstituter::write_field_infos() {
if (anno != NULL) { if (anno != NULL) {
++attr_count; // has RuntimeVisibleAnnotations attribute ++attr_count; // has RuntimeVisibleAnnotations attribute
} }
if (type_anno != NULL) {
++attr_count; // has RuntimeVisibleTypeAnnotations attribute
}
write_u2(attr_count); write_u2(attr_count);
...@@ -110,6 +115,9 @@ void JvmtiClassFileReconstituter::write_field_infos() { ...@@ -110,6 +115,9 @@ void JvmtiClassFileReconstituter::write_field_infos() {
if (anno != NULL) { if (anno != NULL) {
write_annotations_attribute("RuntimeVisibleAnnotations", anno); 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) { ...@@ -550,6 +558,7 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) {
AnnotationArray* anno = method->annotations(); AnnotationArray* anno = method->annotations();
AnnotationArray* param_anno = method->parameter_annotations(); AnnotationArray* param_anno = method->parameter_annotations();
AnnotationArray* default_anno = method->annotation_default(); AnnotationArray* default_anno = method->annotation_default();
AnnotationArray* type_anno = method->type_annotations();
// skip generated default interface methods // skip generated default interface methods
if (method->is_overpass()) { if (method->is_overpass()) {
...@@ -585,6 +594,9 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) { ...@@ -585,6 +594,9 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) {
if (param_anno != NULL) { if (param_anno != NULL) {
++attr_count; // has RuntimeVisibleParameterAnnotations attribute ++attr_count; // has RuntimeVisibleParameterAnnotations attribute
} }
if (type_anno != NULL) {
++attr_count; // has RuntimeVisibleTypeAnnotations attribute
}
write_u2(attr_count); write_u2(attr_count);
if (const_method->code_size() > 0) { if (const_method->code_size() > 0) {
...@@ -609,6 +621,9 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) { ...@@ -609,6 +621,9 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) {
if (param_anno != NULL) { if (param_anno != NULL) {
write_annotations_attribute("RuntimeVisibleParameterAnnotations", param_anno); write_annotations_attribute("RuntimeVisibleParameterAnnotations", param_anno);
} }
if (type_anno != NULL) {
write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno);
}
} }
// Write the class attributes portion of ClassFile structure // Write the class attributes portion of ClassFile structure
...@@ -618,6 +633,7 @@ void JvmtiClassFileReconstituter::write_class_attributes() { ...@@ -618,6 +633,7 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
u2 inner_classes_length = inner_classes_attribute_length(); u2 inner_classes_length = inner_classes_attribute_length();
Symbol* generic_signature = ikh()->generic_signature(); Symbol* generic_signature = ikh()->generic_signature();
AnnotationArray* anno = ikh()->class_annotations(); AnnotationArray* anno = ikh()->class_annotations();
AnnotationArray* type_anno = ikh()->class_type_annotations();
int attr_count = 0; int attr_count = 0;
if (generic_signature != NULL) { if (generic_signature != NULL) {
...@@ -635,6 +651,9 @@ void JvmtiClassFileReconstituter::write_class_attributes() { ...@@ -635,6 +651,9 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
if (anno != NULL) { if (anno != NULL) {
++attr_count; // has RuntimeVisibleAnnotations attribute ++attr_count; // has RuntimeVisibleAnnotations attribute
} }
if (type_anno != NULL) {
++attr_count; // has RuntimeVisibleTypeAnnotations attribute
}
if (cpool()->operands() != NULL) { if (cpool()->operands() != NULL) {
++attr_count; ++attr_count;
} }
...@@ -656,6 +675,9 @@ void JvmtiClassFileReconstituter::write_class_attributes() { ...@@ -656,6 +675,9 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
if (anno != NULL) { if (anno != NULL) {
write_annotations_attribute("RuntimeVisibleAnnotations", anno); write_annotations_attribute("RuntimeVisibleAnnotations", anno);
} }
if (type_anno != NULL) {
write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno);
}
if (cpool()->operands() != NULL) { if (cpool()->operands() != NULL) {
write_bootstrapmethod_attribute(); write_bootstrapmethod_attribute();
} }
......
...@@ -457,6 +457,17 @@ class VM_RedefineClasses: public VM_Operation { ...@@ -457,6 +457,17 @@ class VM_RedefineClasses: public VM_Operation {
instanceKlassHandle scratch_class, TRAPS); instanceKlassHandle scratch_class, TRAPS);
bool rewrite_cp_refs_in_element_value( bool rewrite_cp_refs_in_element_value(
AnnotationArray* class_annotations, int &byte_i_ref, TRAPS); 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( bool rewrite_cp_refs_in_fields_annotations(
instanceKlassHandle scratch_class, TRAPS); instanceKlassHandle scratch_class, TRAPS);
void rewrite_cp_refs_in_method(methodHandle method, void rewrite_cp_refs_in_method(methodHandle method,
...@@ -468,6 +479,12 @@ class VM_RedefineClasses: public VM_Operation { ...@@ -468,6 +479,12 @@ class VM_RedefineClasses: public VM_Operation {
instanceKlassHandle scratch_class, TRAPS); instanceKlassHandle scratch_class, TRAPS);
bool rewrite_cp_refs_in_methods_parameter_annotations( bool rewrite_cp_refs_in_methods_parameter_annotations(
instanceKlassHandle scratch_class, TRAPS); 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_stack_map_table(methodHandle method, TRAPS);
void rewrite_cp_refs_in_verification_type_info( void rewrite_cp_refs_in_verification_type_info(
address& stackmap_addr_ref, address stackmap_end, u2 frame_i, address& stackmap_addr_ref, address stackmap_end, u2 frame_i,
......
/*
* 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.
先完成此消息的编辑!
想要评论请 注册