|
1 /* |
|
2 * Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
21 * have any questions. |
|
22 */ |
|
23 |
|
24 import java.io.*; |
|
25 import java.net.*; |
|
26 import java.util.*; |
|
27 import com.sun.tools.classfile.*; |
|
28 import com.sun.tools.classfile.Type.ArrayType; |
|
29 import com.sun.tools.classfile.Type.ClassSigType; |
|
30 import com.sun.tools.classfile.Type.ClassType; |
|
31 import com.sun.tools.classfile.Type.MethodType; |
|
32 import com.sun.tools.classfile.Type.SimpleType; |
|
33 import com.sun.tools.classfile.Type.TypeParamType; |
|
34 import com.sun.tools.classfile.Type.WildcardType; |
|
35 |
|
36 /* |
|
37 * @test |
|
38 * @bug 6888367 |
|
39 * @summary classfile library parses signature attributes incorrectly |
|
40 */ |
|
41 |
|
42 /* |
|
43 * This test is a pretty detailed test both of javac signature generation and classfile |
|
44 * signature parsing. The first part of the test tests all the examples given in the |
|
45 * second part of the test. Each example comes with one or two annotations, @Desc, @Sig, |
|
46 * for the descriptor and signature of the annotated declaration. Annotations are |
|
47 * provided whenever the annotated item is expected to have a corresponding value. |
|
48 * Each annotation has two argument values. The first arg is the expected value of the |
|
49 * descriptor/signature as found in the class file. This value is mostly for documentation |
|
50 * purposes in reading the test. The second value is the rendering of the descriptor or |
|
51 * signature using a custom Type visitor that explicitly includes an indication of the |
|
52 * Type classes being used to represent the descriptor/signature. Thus we test |
|
53 * that the descriptor/signature is being parsed into the expected type tree structure. |
|
54 */ |
|
55 public class T6888367 { |
|
56 |
|
57 public static void main(String... args) throws Exception { |
|
58 new T6888367().run(); |
|
59 } |
|
60 |
|
61 public void run() throws Exception { |
|
62 ClassFile cf = getClassFile("Test"); |
|
63 |
|
64 testFields(cf); |
|
65 testMethods(cf); |
|
66 testInnerClasses(cf); // recursive |
|
67 |
|
68 if (errors > 0) |
|
69 throw new Exception(errors + " errors found"); |
|
70 } |
|
71 |
|
72 void testFields(ClassFile cf) throws Exception { |
|
73 String cn = cf.getName(); |
|
74 ConstantPool cp = cf.constant_pool; |
|
75 for (Field f: cf.fields) { |
|
76 test("field " + cn + "." + f.getName(cp), f.descriptor, f.attributes, cp); |
|
77 } |
|
78 } |
|
79 |
|
80 void testMethods(ClassFile cf) throws Exception { |
|
81 String cn = cf.getName(); |
|
82 ConstantPool cp = cf.constant_pool; |
|
83 for (Method m: cf.methods) { |
|
84 test("method " + cn + "." + m.getName(cp), m.descriptor, m.attributes, cp); |
|
85 } |
|
86 } |
|
87 |
|
88 void testInnerClasses(ClassFile cf) throws Exception { |
|
89 ConstantPool cp = cf.constant_pool; |
|
90 InnerClasses_attribute ic = |
|
91 (InnerClasses_attribute) cf.attributes.get(Attribute.InnerClasses); |
|
92 for (InnerClasses_attribute.Info info: ic.classes) { |
|
93 String outerClassName = cp.getClassInfo(info.outer_class_info_index).getName(); |
|
94 if (!outerClassName.equals(cf.getName())) { |
|
95 continue; |
|
96 } |
|
97 String innerClassName = cp.getClassInfo(info.inner_class_info_index).getName(); |
|
98 ClassFile icf = getClassFile(innerClassName); |
|
99 test("class " + innerClassName, null, icf.attributes, icf.constant_pool); |
|
100 testInnerClasses(icf); |
|
101 } |
|
102 } |
|
103 |
|
104 void test(String name, Descriptor desc, Attributes attrs, ConstantPool cp) |
|
105 throws Exception { |
|
106 AnnotValues d = getDescValue(attrs, cp); |
|
107 AnnotValues s = getSigValue(attrs, cp); |
|
108 if (d == null && s == null) // not a test field or method if no @Desc or @Sig given |
|
109 return; |
|
110 |
|
111 System.err.println(name); |
|
112 |
|
113 if (desc != null) { |
|
114 System.err.println(" descriptor: " + desc.getValue(cp)); |
|
115 checkEqual(d.raw, desc.getValue(cp)); |
|
116 Type dt = new Signature(desc.index).getType(cp); |
|
117 checkEqual(d.type, tp.print(dt)); |
|
118 } |
|
119 |
|
120 Signature_attribute sa = (Signature_attribute) attrs.get(Attribute.Signature); |
|
121 if (sa != null) |
|
122 System.err.println(" signature: " + sa.getSignature(cp)); |
|
123 |
|
124 if (s != null || sa != null) { |
|
125 if (s != null && sa != null) { |
|
126 checkEqual(s.raw, sa.getSignature(cp)); |
|
127 Type st = new Signature(sa.signature_index).getType(cp); |
|
128 checkEqual(s.type, tp.print(st)); |
|
129 } else if (s != null) |
|
130 error("@Sig annotation found but not Signature attribute"); |
|
131 else |
|
132 error("Signature attribute found but no @Sig annotation"); |
|
133 } |
|
134 |
|
135 System.err.println(); |
|
136 } |
|
137 |
|
138 |
|
139 ClassFile getClassFile(String name) throws IOException, ConstantPoolException { |
|
140 URL url = getClass().getResource(name + ".class"); |
|
141 InputStream in = url.openStream(); |
|
142 try { |
|
143 return ClassFile.read(in); |
|
144 } finally { |
|
145 in.close(); |
|
146 } |
|
147 } |
|
148 |
|
149 AnnotValues getDescValue(Attributes attrs, ConstantPool cp) throws Exception { |
|
150 return getAnnotValues(Desc.class.getName(), attrs, cp); |
|
151 } |
|
152 |
|
153 AnnotValues getSigValue(Attributes attrs, ConstantPool cp) throws Exception { |
|
154 return getAnnotValues(Sig.class.getName(), attrs, cp); |
|
155 } |
|
156 |
|
157 static class AnnotValues { |
|
158 AnnotValues(String raw, String type) { |
|
159 this.raw = raw; |
|
160 this.type = type; |
|
161 } |
|
162 final String raw; |
|
163 final String type; |
|
164 } |
|
165 |
|
166 AnnotValues getAnnotValues(String annotName, Attributes attrs, ConstantPool cp) |
|
167 throws Exception { |
|
168 RuntimeInvisibleAnnotations_attribute annots = |
|
169 (RuntimeInvisibleAnnotations_attribute)attrs.get(Attribute.RuntimeInvisibleAnnotations); |
|
170 if (annots != null) { |
|
171 for (Annotation a: annots.annotations) { |
|
172 if (cp.getUTF8Value(a.type_index).equals("L" + annotName + ";")) { |
|
173 Annotation.Primitive_element_value pv0 = |
|
174 (Annotation.Primitive_element_value) a.element_value_pairs[0].value; |
|
175 Annotation.Primitive_element_value pv1 = |
|
176 (Annotation.Primitive_element_value) a.element_value_pairs[1].value; |
|
177 return new AnnotValues( |
|
178 cp.getUTF8Value(pv0.const_value_index), |
|
179 cp.getUTF8Value(pv1.const_value_index)); |
|
180 } |
|
181 } |
|
182 } |
|
183 return null; |
|
184 |
|
185 } |
|
186 |
|
187 void checkEqual(String expect, String found) { |
|
188 if (!(expect == null ? found == null : expect.equals(found))) { |
|
189 System.err.println("expected: " + expect); |
|
190 System.err.println(" found: " + found); |
|
191 error("unexpected values found"); |
|
192 } |
|
193 } |
|
194 |
|
195 void error(String msg) { |
|
196 System.err.println("error: " + msg); |
|
197 errors++; |
|
198 } |
|
199 |
|
200 int errors; |
|
201 |
|
202 TypePrinter tp = new TypePrinter(); |
|
203 |
|
204 class TypePrinter implements Type.Visitor<String,Void> { |
|
205 String print(Type t) { |
|
206 return t == null ? null : t.accept(this, null); |
|
207 } |
|
208 String print(String pre, List<? extends Type> ts, String post) { |
|
209 if (ts == null) |
|
210 return null; |
|
211 StringBuilder sb = new StringBuilder(); |
|
212 sb.append(pre); |
|
213 String sep = ""; |
|
214 for (Type t: ts) { |
|
215 sb.append(sep); |
|
216 sb.append(print(t)); |
|
217 sep = ","; |
|
218 } |
|
219 sb.append(post); |
|
220 return sb.toString(); |
|
221 } |
|
222 |
|
223 public String visitSimpleType(SimpleType type, Void p) { |
|
224 return "S{" + type.name + "}"; |
|
225 } |
|
226 |
|
227 public String visitArrayType(ArrayType type, Void p) { |
|
228 return "A{" + print(type.elemType) + "}"; |
|
229 } |
|
230 |
|
231 public String visitMethodType(MethodType type, Void p) { |
|
232 StringBuilder sb = new StringBuilder(); |
|
233 sb.append("M{"); |
|
234 if (type.typeParamTypes != null) |
|
235 sb.append(print("<", type.typeParamTypes, ">")); |
|
236 sb.append(print(type.returnType)); |
|
237 sb.append(print("(", type.paramTypes, ")")); |
|
238 if (type.throwsTypes != null) |
|
239 sb.append(print("", type.throwsTypes, "")); |
|
240 sb.append("}"); |
|
241 return sb.toString(); |
|
242 } |
|
243 |
|
244 public String visitClassSigType(ClassSigType type, Void p) { |
|
245 StringBuilder sb = new StringBuilder(); |
|
246 sb.append("CS{"); |
|
247 if (type.typeParamTypes != null) |
|
248 sb.append(print("<", type.typeParamTypes, ">")); |
|
249 sb.append(print(type.superclassType)); |
|
250 if (type.superinterfaceTypes != null) |
|
251 sb.append(print("i(", type.superinterfaceTypes, ")")); |
|
252 sb.append("}"); |
|
253 return sb.toString(); |
|
254 } |
|
255 |
|
256 public String visitClassType(ClassType type, Void p) { |
|
257 StringBuilder sb = new StringBuilder(); |
|
258 sb.append("C{"); |
|
259 if (type.outerType != null) { |
|
260 sb.append(print(type.outerType)); |
|
261 sb.append("."); |
|
262 } |
|
263 sb.append(type.name); |
|
264 if (type.typeArgs != null) |
|
265 sb.append(print("<", type.typeArgs, ">")); |
|
266 sb.append("}"); |
|
267 return sb.toString(); |
|
268 } |
|
269 |
|
270 public String visitTypeParamType(TypeParamType type, Void p) { |
|
271 StringBuilder sb = new StringBuilder(); |
|
272 sb.append("TA{"); |
|
273 sb.append(type.name); |
|
274 if (type.classBound != null) { |
|
275 sb.append(":c"); |
|
276 sb.append(print(type.classBound)); |
|
277 } |
|
278 if (type.interfaceBounds != null) |
|
279 sb.append(print(":i", type.interfaceBounds, "")); |
|
280 sb.append("}"); |
|
281 return sb.toString(); |
|
282 } |
|
283 |
|
284 public String visitWildcardType(WildcardType type, Void p) { |
|
285 switch (type.kind) { |
|
286 case UNBOUNDED: |
|
287 return "W{?}"; |
|
288 case EXTENDS: |
|
289 return "W{e," + print(type.boundType) + "}"; |
|
290 case SUPER: |
|
291 return "W{s," + print(type.boundType) + "}"; |
|
292 default: |
|
293 throw new AssertionError(); |
|
294 } |
|
295 } |
|
296 |
|
297 }; |
|
298 } |
|
299 |
|
300 |
|
301 @interface Desc { |
|
302 String d(); |
|
303 String t(); |
|
304 } |
|
305 |
|
306 @interface Sig { |
|
307 String s(); |
|
308 String t(); |
|
309 } |
|
310 |
|
311 class Clss { } |
|
312 interface Intf { } |
|
313 class GenClss<T> { } |
|
314 |
|
315 class Test { |
|
316 // fields |
|
317 |
|
318 @Desc(d="Z", t="S{boolean}") |
|
319 boolean z; |
|
320 |
|
321 @Desc(d="B", t="S{byte}") |
|
322 byte b; |
|
323 |
|
324 @Desc(d="C", t="S{char}") |
|
325 char c; |
|
326 |
|
327 @Desc(d="D", t="S{double}") |
|
328 double d; |
|
329 |
|
330 @Desc(d="F", t="S{float}") |
|
331 float f; |
|
332 |
|
333 @Desc(d="I", t="S{int}") |
|
334 int i; |
|
335 |
|
336 @Desc(d="J", t="S{long}") |
|
337 long l; |
|
338 |
|
339 @Desc(d="S", t="S{short}") |
|
340 short s; |
|
341 |
|
342 @Desc(d="LClss;", t="C{Clss}") |
|
343 Clss clss; |
|
344 |
|
345 @Desc(d="LIntf;", t="C{Intf}") |
|
346 Intf intf; |
|
347 |
|
348 @Desc(d="[I", t="A{S{int}}") |
|
349 int[] ai; |
|
350 |
|
351 @Desc(d="[LClss;", t="A{C{Clss}}") |
|
352 Clss[] aClss; |
|
353 |
|
354 @Desc(d="LGenClss;", t="C{GenClss}") |
|
355 @Sig(s="LGenClss<LClss;>;", t="C{GenClss<C{Clss}>}") |
|
356 GenClss<Clss> genClass; |
|
357 |
|
358 // methods, return types |
|
359 |
|
360 @Desc(d="()V", t="M{S{void}()}") |
|
361 void mv0() { } |
|
362 |
|
363 @Desc(d="()I", t="M{S{int}()}") |
|
364 int mi0() { return 0; } |
|
365 |
|
366 @Desc(d="()LClss;", t="M{C{Clss}()}") |
|
367 Clss mclss0() { return null; } |
|
368 |
|
369 @Desc(d="()[I", t="M{A{S{int}}()}") |
|
370 int[] mai0() { return null; } |
|
371 |
|
372 @Desc(d="()[LClss;", t="M{A{C{Clss}}()}") |
|
373 Clss[] maClss0() { return null; } |
|
374 |
|
375 @Desc(d="()LGenClss;", t="M{C{GenClss}()}") |
|
376 @Sig(s="()LGenClss<LClss;>;", t="M{C{GenClss<C{Clss}>}()}") |
|
377 GenClss<Clss> mgenClss0() { return null; } |
|
378 |
|
379 @Desc(d="()LGenClss;", t="M{C{GenClss}()}") |
|
380 @Sig(s="()LGenClss<*>;", t="M{C{GenClss<W{?}>}()}") |
|
381 GenClss<?> mgenClssW0() { return null; } |
|
382 |
|
383 @Desc(d="()LGenClss;", t="M{C{GenClss}()}") |
|
384 @Sig(s="()LGenClss<+LClss;>;", t="M{C{GenClss<W{e,C{Clss}}>}()}") |
|
385 GenClss<? extends Clss> mgenClssWExtClss0() { return null; } |
|
386 |
|
387 @Desc(d="()LGenClss;", t="M{C{GenClss}()}") |
|
388 @Sig(s="()LGenClss<-LClss;>;", t="M{C{GenClss<W{s,C{Clss}}>}()}") |
|
389 GenClss<? super Clss> mgenClssWSupClss0() { return null; } |
|
390 |
|
391 @Desc(d="()Ljava/lang/Object;", t="M{C{java/lang/Object}()}") |
|
392 @Sig(s="<T:Ljava/lang/Object;>()TT;", t="M{<TA{T:cC{java/lang/Object}}>S{T}()}") |
|
393 <T> T mt0() { return null; } |
|
394 |
|
395 @Desc(d="()LGenClss;", t="M{C{GenClss}()}") |
|
396 @Sig(s="<T:Ljava/lang/Object;>()LGenClss<+TT;>;", |
|
397 t="M{<TA{T:cC{java/lang/Object}}>C{GenClss<W{e,S{T}}>}()}") |
|
398 <T> GenClss<? extends T> mgenClssWExtT0() { return null; } |
|
399 |
|
400 @Desc(d="()LGenClss;", t="M{C{GenClss}()}") |
|
401 @Sig(s="<T:Ljava/lang/Object;>()LGenClss<-TT;>;", t="M{<TA{T:cC{java/lang/Object}}>C{GenClss<W{s,S{T}}>}()}") |
|
402 <T> GenClss<? super T> mgenClssWSupT0() { return null; } |
|
403 |
|
404 // methods, arg types |
|
405 |
|
406 @Desc(d="(I)V", t="M{S{void}(S{int})}") |
|
407 void mi1(int arg) { } |
|
408 |
|
409 @Desc(d="(LClss;)V", t="M{S{void}(C{Clss})}") |
|
410 void mclss1(Clss arg) { } |
|
411 |
|
412 @Desc(d="([I)V", t="M{S{void}(A{S{int}})}") |
|
413 void mai1(int[] arg) { } |
|
414 |
|
415 @Desc(d="([LClss;)V", t="M{S{void}(A{C{Clss}})}") |
|
416 void maClss1(Clss[] arg) { } |
|
417 |
|
418 @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") |
|
419 @Sig(s="(LGenClss<LClss;>;)V", t="M{S{void}(C{GenClss<C{Clss}>})}") |
|
420 void mgenClss1(GenClss<Clss> arg) { } |
|
421 |
|
422 @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") |
|
423 @Sig(s="(LGenClss<*>;)V", t="M{S{void}(C{GenClss<W{?}>})}") |
|
424 void mgenClssW1(GenClss<?> arg) { } |
|
425 |
|
426 @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") |
|
427 @Sig(s="(LGenClss<+LClss;>;)V", t="M{S{void}(C{GenClss<W{e,C{Clss}}>})}") |
|
428 void mgenClssWExtClss1(GenClss<? extends Clss> arg) { } |
|
429 |
|
430 @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") |
|
431 @Sig(s="(LGenClss<-LClss;>;)V", t="M{S{void}(C{GenClss<W{s,C{Clss}}>})}") |
|
432 void mgenClssWSupClss1(GenClss<? super Clss> arg) { } |
|
433 |
|
434 @Desc(d="(Ljava/lang/Object;)V", t="M{S{void}(C{java/lang/Object})}") |
|
435 @Sig(s="<T:Ljava/lang/Object;>(TT;)V", |
|
436 t="M{<TA{T:cC{java/lang/Object}}>S{void}(S{T})}") |
|
437 <T> void mt1(T arg) { } |
|
438 |
|
439 @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") |
|
440 @Sig(s="<T:Ljava/lang/Object;>(LGenClss<+TT;>;)V", |
|
441 t="M{<TA{T:cC{java/lang/Object}}>S{void}(C{GenClss<W{e,S{T}}>})}") |
|
442 <T> void mgenClssWExtT1(GenClss<? extends T> arg) { } |
|
443 |
|
444 @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") |
|
445 @Sig(s="<T:Ljava/lang/Object;>(LGenClss<-TT;>;)V", |
|
446 t="M{<TA{T:cC{java/lang/Object}}>S{void}(C{GenClss<W{s,S{T}}>})}") |
|
447 <T> void mgenClssWSupT1(GenClss<? super T> arg) { } |
|
448 |
|
449 // methods, throws |
|
450 |
|
451 @Desc(d="()V", t="M{S{void}()}") |
|
452 void m_E() throws Exception { } |
|
453 |
|
454 @Desc(d="()V", t="M{S{void}()}") |
|
455 @Sig(s="<T:Ljava/lang/Throwable;>()V^TT;", |
|
456 t="M{<TA{T:cC{java/lang/Throwable}}>S{void}()S{T}}") |
|
457 <T extends Throwable> void m_T() throws T { } |
|
458 |
|
459 // inner classes |
|
460 |
|
461 static class X { |
|
462 // no sig |
|
463 class P { } |
|
464 |
|
465 @Sig(s="<TQ:Ljava/lang/Object;>LTest$X$P;", |
|
466 t="CS{<TA{TQ:cC{java/lang/Object}}>C{Test$X$P}}") |
|
467 class Q<TQ> extends P { } |
|
468 |
|
469 @Sig(s="<TR:Ljava/lang/Object;>LTest$X$Q<TTR;>;", |
|
470 t="CS{<TA{TR:cC{java/lang/Object}}>C{Test$X$Q<S{TR}>}}") |
|
471 class R<TR> extends Q<TR> { } |
|
472 } |
|
473 |
|
474 @Sig(s="<TY:Ljava/lang/Object;>Ljava/lang/Object;", |
|
475 t="CS{<TA{TY:cC{java/lang/Object}}>C{java/lang/Object}}") |
|
476 static class Y<TY> { |
|
477 // no sig |
|
478 class P { } |
|
479 |
|
480 @Sig(s="<TQ:Ljava/lang/Object;>LTest$Y<TTY;>.P;", |
|
481 t="CS{<TA{TQ:cC{java/lang/Object}}>C{C{Test$Y<S{TY}>}.P}}") |
|
482 class Q<TQ> extends P { } |
|
483 |
|
484 @Sig(s="<TR:Ljava/lang/Object;>LTest$Y<TTY;>.Q<TTR;>;", |
|
485 t="CS{<TA{TR:cC{java/lang/Object}}>C{C{Test$Y<S{TY}>}.Q<S{TR}>}}") |
|
486 class R<TR> extends Q<TR> { |
|
487 // no sig |
|
488 class R1 { } |
|
489 |
|
490 @Sig(s="<TR2:Ljava/lang/Object;>LTest$Y<TTY;>.R<TTR;>.R1;", |
|
491 t="CS{<TA{TR2:cC{java/lang/Object}}>C{C{C{Test$Y<S{TY}>}.R<S{TR}>}.R1}}") |
|
492 class R2<TR2> extends R1 { } |
|
493 } |
|
494 |
|
495 @Sig(s="LTest$Y<TTY;>.Q<TTY;>;", t="C{C{Test$Y<S{TY}>}.Q<S{TY}>}") |
|
496 class S extends Q<TY> { |
|
497 // no sig |
|
498 class S1 { } |
|
499 |
|
500 @Sig(s="<TS2:Ljava/lang/Object;>LTest$Y<TTY;>.S.S1;", |
|
501 t="CS{<TA{TS2:cC{java/lang/Object}}>C{C{C{Test$Y<S{TY}>}.S}.S1}}") |
|
502 class S2<TS2> extends S1 { } |
|
503 |
|
504 @Sig(s="LTest$Y<TTY;>.S.S2<TTY;>;", |
|
505 t="C{C{C{Test$Y<S{TY}>}.S}.S2<S{TY}>}") |
|
506 class S3 extends S2<TY> { } |
|
507 } |
|
508 } |
|
509 } |
|
510 |
|
511 |