comparison test/runtime/RedefineTests/RedefineAnnotations.java @ 20613:50054b63f0aa

8057043: Type annotations not retained during class redefine / retransform Reviewed-by: coleenp, sspitsyn, jfranck
author aeriksso
date Wed, 22 Oct 2014 13:59:56 +0200
parents
children
comparison
equal deleted inserted replaced
20611:3c87c13918fb 20613:50054b63f0aa
1 /*
2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /*
25 * @test
26 * @library /testlibrary
27 * @summary Test that type annotations are retained after a retransform
28 * @run main RedefineAnnotations buildagent
29 * @run main/othervm -javaagent:redefineagent.jar RedefineAnnotations
30 */
31
32 import static com.oracle.java.testlibrary.Asserts.assertTrue;
33 import java.io.FileNotFoundException;
34 import java.io.PrintWriter;
35 import java.lang.NoSuchFieldException;
36 import java.lang.NoSuchMethodException;
37 import java.lang.RuntimeException;
38 import java.lang.annotation.Annotation;
39 import java.lang.annotation.ElementType;
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 import java.lang.annotation.Target;
43 import java.lang.instrument.ClassFileTransformer;
44 import java.lang.instrument.IllegalClassFormatException;
45 import java.lang.instrument.Instrumentation;
46 import java.lang.instrument.UnmodifiableClassException;
47 import java.lang.reflect.AnnotatedArrayType;
48 import java.lang.reflect.AnnotatedParameterizedType;
49 import java.lang.reflect.AnnotatedType;
50 import java.lang.reflect.AnnotatedWildcardType;
51 import java.lang.reflect.Executable;
52 import java.lang.reflect.TypeVariable;
53 import java.security.ProtectionDomain;
54 import java.util.Arrays;
55 import java.util.LinkedList;
56 import java.util.List;
57 import java.util.Map;
58 import jdk.internal.org.objectweb.asm.ClassReader;
59 import jdk.internal.org.objectweb.asm.ClassVisitor;
60 import jdk.internal.org.objectweb.asm.ClassWriter;
61 import jdk.internal.org.objectweb.asm.FieldVisitor;
62 import static jdk.internal.org.objectweb.asm.Opcodes.ASM5;
63
64 @Retention(RetentionPolicy.RUNTIME)
65 @Target(ElementType.TYPE_USE)
66 @interface TestAnn {
67 String site();
68 }
69
70 public class RedefineAnnotations {
71 static Instrumentation inst;
72 public static void premain(String agentArgs, Instrumentation inst) {
73 RedefineAnnotations.inst = inst;
74 }
75
76 static class Transformer implements ClassFileTransformer {
77
78 public byte[] asm(ClassLoader loader, String className,
79 Class<?> classBeingRedefined,
80 ProtectionDomain protectionDomain, byte[] classfileBuffer)
81 throws IllegalClassFormatException {
82
83 ClassWriter cw = new ClassWriter(0);
84 ClassVisitor cv = new ReAddDummyFieldsClassVisitor(ASM5, cw) { };
85 ClassReader cr = new ClassReader(classfileBuffer);
86 cr.accept(cv, 0);
87 return cw.toByteArray();
88 }
89
90 public class ReAddDummyFieldsClassVisitor extends ClassVisitor {
91
92 LinkedList<F> fields = new LinkedList<>();
93
94 public ReAddDummyFieldsClassVisitor(int api, ClassVisitor cv) {
95 super(api, cv);
96 }
97
98 @Override public FieldVisitor visitField(int access, String name,
99 String desc, String signature, Object value) {
100 if (name.startsWith("dummy")) {
101 // Remove dummy field
102 fields.addLast(new F(access, name, desc, signature, value));
103 return null;
104 }
105 return cv.visitField(access, name, desc, signature, value);
106 }
107
108 @Override public void visitEnd() {
109 F f;
110 while ((f = fields.pollFirst()) != null) {
111 // Re-add dummy fields
112 cv.visitField(f.access, f.name, f.desc, f.signature, f.value);
113 }
114 }
115
116 private class F {
117 private int access;
118 private String name;
119 private String desc;
120 private String signature;
121 private Object value;
122 F(int access, String name, String desc, String signature, Object value) {
123 this.access = access;
124 this.name = name;
125 this.desc = desc;
126 this.signature = signature;
127 this.value = value;
128 }
129 }
130 }
131
132 @Override public byte[] transform(ClassLoader loader, String className,
133 Class<?> classBeingRedefined,
134 ProtectionDomain protectionDomain, byte[] classfileBuffer)
135 throws IllegalClassFormatException {
136
137 if (className.contains("TypeAnnotatedTestClass")) {
138 try {
139 // Here we remove and re-add the dummy fields. This shuffles the constant pool
140 return asm(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
141 } catch (Throwable e) {
142 // The retransform native code that called this method does not propagate
143 // exceptions. Instead of getting an uninformative generic error, catch
144 // problems here and print it, then exit.
145 e.printStackTrace();
146 System.exit(1);
147 }
148 }
149 return null;
150 }
151 }
152
153 private static void buildAgent() {
154 try {
155 ClassFileInstaller.main("RedefineAnnotations");
156 } catch (Exception e) {
157 throw new RuntimeException("Could not write agent classfile", e);
158 }
159
160 try {
161 PrintWriter pw = new PrintWriter("MANIFEST.MF");
162 pw.println("Premain-Class: RedefineAnnotations");
163 pw.println("Agent-Class: RedefineAnnotations");
164 pw.println("Can-Retransform-Classes: true");
165 pw.close();
166 } catch (FileNotFoundException e) {
167 throw new RuntimeException("Could not write manifest file for the agent", e);
168 }
169
170 sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar");
171 if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "RedefineAnnotations.class" })) {
172 throw new RuntimeException("Could not write the agent jar file");
173 }
174 }
175
176 public static void main(String argv[]) throws NoSuchFieldException, NoSuchMethodException {
177 if (argv.length == 1 && argv[0].equals("buildagent")) {
178 buildAgent();
179 return;
180 }
181
182 if (inst == null) {
183 throw new RuntimeException("Instrumentation object was null");
184 }
185
186 RedefineAnnotations test = new RedefineAnnotations();
187 test.testTransformAndVerify();
188 }
189
190 // Class type annotations
191 private Annotation classTypeParameterTA;
192 private Annotation extendsTA;
193 private Annotation implementsTA;
194
195 // Field type annotations
196 private Annotation fieldTA;
197 private Annotation innerTA;
198 private Annotation[] arrayTA = new Annotation[4];
199 private Annotation[] mapTA = new Annotation[5];
200
201 // Method type annotations
202 private Annotation returnTA, methodTypeParameterTA, formalParameterTA, throwsTA;
203
204 private void testTransformAndVerify()
205 throws NoSuchFieldException, NoSuchMethodException {
206
207 Class<TypeAnnotatedTestClass> c = TypeAnnotatedTestClass.class;
208 Class<?> myClass = c;
209
210 /*
211 * Verify that the expected annotations are where they should be before transform.
212 */
213 verifyClassTypeAnnotations(c);
214 verifyFieldTypeAnnotations(c);
215 verifyMethodTypeAnnotations(c);
216
217 try {
218 inst.addTransformer(new Transformer(), true);
219 inst.retransformClasses(myClass);
220 } catch (UnmodifiableClassException e) {
221 throw new RuntimeException(e);
222 }
223
224 /*
225 * Verify that the expected annotations are where they should be after transform.
226 * Also verify that before and after are equal.
227 */
228 verifyClassTypeAnnotations(c);
229 verifyFieldTypeAnnotations(c);
230 verifyMethodTypeAnnotations(c);
231 }
232
233 private void verifyClassTypeAnnotations(Class c) {
234 Annotation anno;
235
236 anno = c.getTypeParameters()[0].getAnnotations()[0];
237 verifyTestAnn(classTypeParameterTA, anno, "classTypeParameter");
238 classTypeParameterTA = anno;
239
240 anno = c.getAnnotatedSuperclass().getAnnotations()[0];
241 verifyTestAnn(extendsTA, anno, "extends");
242 extendsTA = anno;
243
244 anno = c.getAnnotatedInterfaces()[0].getAnnotations()[0];
245 verifyTestAnn(implementsTA, anno, "implements");
246 implementsTA = anno;
247 }
248
249 private void verifyFieldTypeAnnotations(Class c)
250 throws NoSuchFieldException, NoSuchMethodException {
251
252 verifyBasicFieldTypeAnnotations(c);
253 verifyInnerFieldTypeAnnotations(c);
254 verifyArrayFieldTypeAnnotations(c);
255 verifyMapFieldTypeAnnotations(c);
256 }
257
258 private void verifyBasicFieldTypeAnnotations(Class c)
259 throws NoSuchFieldException, NoSuchMethodException {
260
261 Annotation anno = c.getDeclaredField("typeAnnotatedBoolean").getAnnotatedType().getAnnotations()[0];
262 verifyTestAnn(fieldTA, anno, "field");
263 fieldTA = anno;
264 }
265
266 private void verifyInnerFieldTypeAnnotations(Class c)
267 throws NoSuchFieldException, NoSuchMethodException {
268
269 AnnotatedType at = c.getDeclaredField("typeAnnotatedInner").getAnnotatedType();
270 Annotation anno = at.getAnnotations()[0];
271 verifyTestAnn(innerTA, anno, "inner");
272 innerTA = anno;
273 }
274
275 private void verifyArrayFieldTypeAnnotations(Class c)
276 throws NoSuchFieldException, NoSuchMethodException {
277
278 Annotation anno;
279 AnnotatedType at;
280
281 at = c.getDeclaredField("typeAnnotatedArray").getAnnotatedType();
282 anno = at.getAnnotations()[0];
283 verifyTestAnn(arrayTA[0], anno, "array1");
284 arrayTA[0] = anno;
285
286 for (int i = 1; i <= 3; i++) {
287 at = ((AnnotatedArrayType) at).getAnnotatedGenericComponentType();
288 anno = at.getAnnotations()[0];
289 verifyTestAnn(arrayTA[i], anno, "array" + (i + 1));
290 arrayTA[i] = anno;
291 }
292 }
293
294 private void verifyMapFieldTypeAnnotations(Class c)
295 throws NoSuchFieldException, NoSuchMethodException {
296
297 Annotation anno;
298 AnnotatedType atBase;
299 AnnotatedType atParameter;
300 atBase = c.getDeclaredField("typeAnnotatedMap").getAnnotatedType();
301
302 anno = atBase.getAnnotations()[0];
303 verifyTestAnn(mapTA[0], anno, "map1");
304 mapTA[0] = anno;
305
306 atParameter =
307 ((AnnotatedParameterizedType) atBase).
308 getAnnotatedActualTypeArguments()[0];
309 anno = ((AnnotatedWildcardType) atParameter).getAnnotations()[0];
310 verifyTestAnn(mapTA[1], anno, "map2");
311 mapTA[1] = anno;
312
313 anno =
314 ((AnnotatedWildcardType) atParameter).
315 getAnnotatedUpperBounds()[0].getAnnotations()[0];
316 verifyTestAnn(mapTA[2], anno, "map3");
317 mapTA[2] = anno;
318
319 atParameter =
320 ((AnnotatedParameterizedType) atBase).
321 getAnnotatedActualTypeArguments()[1];
322 anno = ((AnnotatedParameterizedType) atParameter).getAnnotations()[0];
323 verifyTestAnn(mapTA[3], anno, "map4");
324 mapTA[3] = anno;
325
326 anno =
327 ((AnnotatedParameterizedType) atParameter).
328 getAnnotatedActualTypeArguments()[0].getAnnotations()[0];
329 verifyTestAnn(mapTA[4], anno, "map5");
330 mapTA[4] = anno;
331 }
332
333 private void verifyMethodTypeAnnotations(Class c)
334 throws NoSuchFieldException, NoSuchMethodException {
335 Annotation anno;
336 Executable typeAnnotatedMethod =
337 c.getDeclaredMethod("typeAnnotatedMethod", TypeAnnotatedTestClass.class);
338
339 anno = typeAnnotatedMethod.getAnnotatedReturnType().getAnnotations()[0];
340 verifyTestAnn(returnTA, anno, "return");
341 returnTA = anno;
342
343 anno = typeAnnotatedMethod.getTypeParameters()[0].getAnnotations()[0];
344 verifyTestAnn(methodTypeParameterTA, anno, "methodTypeParameter");
345 methodTypeParameterTA = anno;
346
347 anno = typeAnnotatedMethod.getAnnotatedParameterTypes()[0].getAnnotations()[0];
348 verifyTestAnn(formalParameterTA, anno, "formalParameter");
349 formalParameterTA = anno;
350
351 anno = typeAnnotatedMethod.getAnnotatedExceptionTypes()[0].getAnnotations()[0];
352 verifyTestAnn(throwsTA, anno, "throws");
353 throwsTA = anno;
354 }
355
356 private static void verifyTestAnn(Annotation verifyAgainst, Annotation anno, String expectedSite) {
357 verifyTestAnnSite(anno, expectedSite);
358
359 // When called before transform verifyAgainst will be null, when called
360 // after transform it will be the annotation from before the transform
361 if (verifyAgainst != null) {
362 assertTrue(anno.equals(verifyAgainst),
363 "Annotations do not match before and after." +
364 " Before: \"" + verifyAgainst + "\", After: \"" + anno + "\"");
365 }
366 }
367
368 private static void verifyTestAnnSite(Annotation testAnn, String expectedSite) {
369 String expectedAnn = "@TestAnn(site=" + expectedSite + ")";
370 assertTrue(testAnn.toString().equals(expectedAnn),
371 "Expected \"" + expectedAnn + "\", got \"" + testAnn + "\"");
372 }
373
374 public static class TypeAnnotatedTestClass <@TestAnn(site="classTypeParameter") S,T>
375 extends @TestAnn(site="extends") Thread
376 implements @TestAnn(site="implements") Runnable {
377
378 public @TestAnn(site="field") boolean typeAnnotatedBoolean;
379
380 public
381 RedefineAnnotations.
382 @TestAnn(site="inner") TypeAnnotatedTestClass
383 typeAnnotatedInner;
384
385 public
386 @TestAnn(site="array4") boolean
387 @TestAnn(site="array1") []
388 @TestAnn(site="array2") []
389 @TestAnn(site="array3") []
390 typeAnnotatedArray;
391
392 public @TestAnn(site="map1") Map
393 <@TestAnn(site="map2") ? extends @TestAnn(site="map3") String,
394 @TestAnn(site="map4") List<@TestAnn(site="map5") Object>> typeAnnotatedMap;
395
396 public int dummy1;
397 public int dummy2;
398 public int dummy3;
399
400 @TestAnn(site="return") <@TestAnn(site="methodTypeParameter") U,V> Class
401 typeAnnotatedMethod(@TestAnn(site="formalParameter") TypeAnnotatedTestClass arg)
402 throws @TestAnn(site="throws") ClassNotFoundException {
403
404 @TestAnn(site="local_variable_type") int foo = 0;
405 throw new ClassNotFoundException();
406 }
407
408 public void run() {}
409 }
410 }