|
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 /* |
|
25 * @test |
|
26 * @bug 6889255 |
|
27 * @summary ClassReader does not read parameter names correctly |
|
28 */ |
|
29 |
|
30 import java.io.*; |
|
31 import java.util.*; |
|
32 import javax.tools.StandardLocation; |
|
33 import com.sun.tools.javac.code.Flags; |
|
34 import com.sun.tools.javac.code.Kinds; |
|
35 import com.sun.tools.javac.code.Scope; |
|
36 import com.sun.tools.javac.code.Symbol.*; |
|
37 import com.sun.tools.javac.code.Type; |
|
38 import com.sun.tools.javac.code.Type.ClassType; |
|
39 import com.sun.tools.javac.code.TypeTags; |
|
40 import com.sun.tools.javac.file.JavacFileManager; |
|
41 import com.sun.tools.javac.jvm.ClassReader; |
|
42 import com.sun.tools.javac.util.Context; |
|
43 import com.sun.tools.javac.util.Names; |
|
44 |
|
45 public class T6889255 { |
|
46 boolean testInterfaces = true; |
|
47 boolean testSyntheticMethods = true; |
|
48 |
|
49 // The following enums control the generation of the test methods to be compiled. |
|
50 enum GenericKind { |
|
51 NOT_GENERIC, |
|
52 GENERIC |
|
53 }; |
|
54 |
|
55 enum ClassKind { |
|
56 CLASS("Clss"), |
|
57 INTERFACE("Intf"), |
|
58 ENUM("Enum"); |
|
59 final String base; |
|
60 ClassKind(String base) { this.base = base; } |
|
61 }; |
|
62 |
|
63 enum NestedKind { |
|
64 /** Declare methods inside the outermost container. */ |
|
65 NONE, |
|
66 /** Declare methods inside a container with a 'static' modifier. */ |
|
67 NESTED, |
|
68 /** Declare methods inside a container without a 'static' modifier. */ |
|
69 INNER, |
|
70 /** Declare methods inside a local class in an initializer. */ |
|
71 INIT_LOCAL, |
|
72 /** Declare methods inside an anonymous class in an initializer. */ |
|
73 INIT_ANON, |
|
74 /** Declare methods inside a local class in a method. */ |
|
75 METHOD_LOCAL, |
|
76 /** Declare methods inside an anonymous class in a method. */ |
|
77 METHOD_ANON |
|
78 }; |
|
79 |
|
80 enum MethodKind { |
|
81 ABSTRACT, |
|
82 CONSTRUCTOR, |
|
83 METHOD, |
|
84 STATIC_METHOD, |
|
85 BRIDGE_METHOD |
|
86 }; |
|
87 |
|
88 enum FinalKind { |
|
89 /** Method body does not reference external final variables. */ |
|
90 NO_FINAL, |
|
91 /** Method body references external final variables. */ |
|
92 USE_FINAL |
|
93 }; |
|
94 |
|
95 public static void main(String... args) throws Exception { |
|
96 new T6889255().run(); |
|
97 } |
|
98 |
|
99 void run() throws Exception { |
|
100 genTest(); |
|
101 |
|
102 test("no-args", false); |
|
103 test("g", true, "-g"); |
|
104 |
|
105 if (errors > 0) |
|
106 throw new Exception(errors + " errors found"); |
|
107 } |
|
108 |
|
109 /** |
|
110 * Create a file containing lots of method definitions to be tested. |
|
111 * There are 3 sets of nested loops that generate the methods. |
|
112 * 1. The outermost set declares [generic] (class | interface | enum) |
|
113 * 2. The middle set declares [(nested | inner | anon | local)] class |
|
114 * 3. The innermost set declares |
|
115 * [generic] (constructor|method|static-method|bridge-method) [using final variables in outer scope] |
|
116 * Invalid combinations are filtered out. |
|
117 */ |
|
118 void genTest() throws Exception { |
|
119 BufferedWriter out = new BufferedWriter(new FileWriter("Test.java")); |
|
120 |
|
121 // This interface is used to force bridge methods to be generated, by |
|
122 // implementing its methods with subtypes of Object |
|
123 out.write("interface Base {\n"); |
|
124 out.write(" Object base_m1(int i1);\n"); |
|
125 out.write(" Object base_m2(int i1);\n"); |
|
126 out.write("}\n"); |
|
127 |
|
128 int outerNum = 0; |
|
129 // Outermost set of loops, to generate a top level container |
|
130 for (GenericKind outerGenericKind: GenericKind.values()) { |
|
131 for (ClassKind outerClassKind: ClassKind.values()) { |
|
132 if (outerGenericKind == GenericKind.GENERIC && outerClassKind == ClassKind.ENUM) |
|
133 continue; |
|
134 String outerClassName = outerClassKind.base + (outerNum++); |
|
135 String outerTypeArg = outerClassKind.toString().charAt(0) + "T"; |
|
136 if (outerClassKind == ClassKind.CLASS) |
|
137 out.write("abstract "); |
|
138 out.write(outerClassKind.toString().toLowerCase() + " " + outerClassName); |
|
139 if (outerGenericKind == GenericKind.GENERIC) |
|
140 out.write("<" + outerTypeArg + ">"); |
|
141 if (outerClassKind == ClassKind.INTERFACE) |
|
142 out.write(" extends Base"); |
|
143 else |
|
144 out.write(" implements Base"); |
|
145 out.write(" {\n"); |
|
146 if (outerClassKind == ClassKind.ENUM) { |
|
147 out.write(" E1(0,0,0), E2(0,0,0), E3(0,0,0);\n"); |
|
148 out.write(" " + outerClassName + "(int i1, int i2, int i3) { }\n"); |
|
149 } |
|
150 // Middle set of loops, to generate an optional nested container |
|
151 int nestedNum = 0; |
|
152 int methodNum = 0; |
|
153 for (GenericKind nestedGenericKind: GenericKind.values()) { |
|
154 nextNestedKind: |
|
155 for (NestedKind nestedKind: NestedKind.values()) { |
|
156 // if the nested kind is none, there is no point iterating over all |
|
157 // nested generic kinds, so arbitarily limit it to just one kind |
|
158 if (nestedKind == NestedKind.NONE && nestedGenericKind != GenericKind.NOT_GENERIC) |
|
159 continue; |
|
160 if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON) |
|
161 && nestedGenericKind == GenericKind.GENERIC) |
|
162 continue; |
|
163 String indent = " "; |
|
164 boolean haveFinal = false; |
|
165 switch (nestedKind) { |
|
166 case METHOD_ANON: case METHOD_LOCAL: |
|
167 if (outerClassKind == ClassKind.INTERFACE) |
|
168 continue nextNestedKind; |
|
169 out.write(indent + "void m" + + (nestedNum++) + "() {\n"); |
|
170 indent += " "; |
|
171 out.write(indent + "final int fi1 = 0;\n"); |
|
172 haveFinal = true; |
|
173 break; |
|
174 case INIT_ANON: case INIT_LOCAL: |
|
175 if (outerClassKind == ClassKind.INTERFACE) |
|
176 continue nextNestedKind; |
|
177 out.write(indent + "{\n"); |
|
178 indent += " "; |
|
179 break; |
|
180 } |
|
181 for (ClassKind nestedClassKind: ClassKind.values()) { |
|
182 if ((nestedGenericKind == GenericKind.GENERIC) |
|
183 && (nestedClassKind == ClassKind.ENUM)) |
|
184 continue; |
|
185 if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.METHOD_LOCAL |
|
186 || nestedKind == NestedKind.INIT_ANON || nestedKind == NestedKind.INIT_LOCAL) |
|
187 && nestedClassKind != ClassKind.CLASS) |
|
188 continue; |
|
189 // if the nested kind is none, there is no point iterating over all |
|
190 // nested class kinds, so arbitarily limit it to just one kind |
|
191 if (nestedKind == NestedKind.NONE && nestedClassKind != ClassKind.CLASS) |
|
192 continue; |
|
193 |
|
194 ClassKind methodClassKind; |
|
195 String methodClassName; |
|
196 boolean allowAbstractMethods; |
|
197 boolean allowStaticMethods; |
|
198 switch (nestedKind) { |
|
199 case NONE: |
|
200 methodClassKind = outerClassKind; |
|
201 methodClassName = outerClassName; |
|
202 allowAbstractMethods = (outerClassKind == ClassKind.CLASS); |
|
203 allowStaticMethods = (outerClassKind != ClassKind.INTERFACE); |
|
204 break; |
|
205 case METHOD_ANON: |
|
206 case INIT_ANON: |
|
207 out.write(indent + "new Base() {\n"); |
|
208 indent += " "; |
|
209 methodClassKind = ClassKind.CLASS; |
|
210 methodClassName = null; |
|
211 allowAbstractMethods = false; |
|
212 allowStaticMethods = false; |
|
213 break; |
|
214 default: { // INNER, NESTED, LOCAL |
|
215 String nestedClassName = "N" + nestedClassKind.base + (nestedNum++); |
|
216 String nestedTypeArg = nestedClassKind.toString().charAt(0) + "T"; |
|
217 out.write(indent); |
|
218 if (nestedKind == NestedKind.NESTED) |
|
219 out.write("static "); |
|
220 if (nestedClassKind == ClassKind.CLASS) |
|
221 out.write("abstract "); |
|
222 out.write(nestedClassKind.toString().toLowerCase() + " " + nestedClassName); |
|
223 if (nestedGenericKind == GenericKind.GENERIC) |
|
224 out.write("<" + nestedTypeArg + ">"); |
|
225 if (nestedClassKind == ClassKind.INTERFACE) |
|
226 out.write(" extends Base "); |
|
227 else |
|
228 out.write(" implements Base "); |
|
229 out.write(" {\n"); |
|
230 indent += " "; |
|
231 if (nestedClassKind == ClassKind.ENUM) { |
|
232 out.write(indent + "E1(0,0,0), E2(0,0,0), E3(0,0,0);\n"); |
|
233 out.write(indent + nestedClassName + "(int i1, int i2, int i3) { }\n"); |
|
234 } |
|
235 methodClassKind = nestedClassKind; |
|
236 methodClassName = nestedClassName; |
|
237 allowAbstractMethods = (nestedClassKind == ClassKind.CLASS); |
|
238 allowStaticMethods = (nestedKind == NestedKind.NESTED && nestedClassKind != ClassKind.INTERFACE); |
|
239 break; |
|
240 } |
|
241 } |
|
242 |
|
243 // Innermost loops, to generate methods |
|
244 for (GenericKind methodGenericKind: GenericKind.values()) { |
|
245 for (FinalKind finalKind: FinalKind.values()) { |
|
246 for (MethodKind methodKind: MethodKind.values()) { |
|
247 // out.write("// " + outerGenericKind |
|
248 // + " " + outerClassKind |
|
249 // + " " + nestedKind |
|
250 // + " " + nestedGenericKind |
|
251 // + " " + nestedClassKind |
|
252 // + " " + methodGenericKind |
|
253 // + " " + finalKind |
|
254 // + " " + methodKind |
|
255 // + "\n"); |
|
256 switch (methodKind) { |
|
257 case CONSTRUCTOR: |
|
258 if (nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON) |
|
259 break; |
|
260 if (methodClassKind != ClassKind.CLASS) |
|
261 break; |
|
262 if (finalKind == FinalKind.USE_FINAL && !haveFinal) |
|
263 break; |
|
264 out.write(indent); |
|
265 if (methodGenericKind == GenericKind.GENERIC) { |
|
266 out.write("<CT> " + methodClassName + "(CT c1, CT c2"); |
|
267 } else { |
|
268 out.write(methodClassName + "(boolean b1, char c2"); |
|
269 } |
|
270 if (finalKind == FinalKind.USE_FINAL) { |
|
271 // add a dummy parameter to avoid duplicate declaration |
|
272 out.write(", int i3) { int i = fi1; }\n"); |
|
273 } else |
|
274 out.write(") { }\n"); |
|
275 break; |
|
276 case ABSTRACT: |
|
277 if (!allowAbstractMethods) |
|
278 continue; |
|
279 // fallthrough |
|
280 case METHOD: |
|
281 if (finalKind == FinalKind.USE_FINAL && !haveFinal) |
|
282 break; |
|
283 out.write(indent); |
|
284 if (methodKind == MethodKind.ABSTRACT) |
|
285 out.write("abstract "); |
|
286 if (methodGenericKind == GenericKind.GENERIC) |
|
287 out.write("<MT> "); |
|
288 out.write("void m" + (methodNum++) + "(int i1, long l2, float f3)"); |
|
289 if (methodKind == MethodKind.ABSTRACT || methodClassKind == ClassKind.INTERFACE) |
|
290 out.write(";\n"); |
|
291 else { |
|
292 out.write(" {"); |
|
293 if (finalKind == FinalKind.USE_FINAL) |
|
294 out.write(" int i = fi1;"); |
|
295 out.write(" }\n"); |
|
296 } |
|
297 break; |
|
298 case BRIDGE_METHOD: |
|
299 if (methodGenericKind == GenericKind.GENERIC) |
|
300 break; |
|
301 out.write(indent); |
|
302 // methods Base.base_m1 and Base.base_m2 are declared for the |
|
303 // benefit of bridge methods. They need to be implemented |
|
304 // whether or not a final variable is used. |
|
305 String methodName = (finalKind == FinalKind.NO_FINAL ? "base_m1" : "base_m2"); |
|
306 out.write("public String " + methodName + "(int i1)"); |
|
307 if (methodClassKind == ClassKind.INTERFACE) |
|
308 out.write(";\n"); |
|
309 else { |
|
310 out.write(" {"); |
|
311 if (finalKind == FinalKind.USE_FINAL && haveFinal) |
|
312 out.write(" int i = fi1;"); |
|
313 out.write(" return null; }\n"); |
|
314 } |
|
315 break; |
|
316 case STATIC_METHOD: |
|
317 if (!allowStaticMethods) |
|
318 break; |
|
319 if (finalKind == FinalKind.USE_FINAL && !haveFinal) |
|
320 break; |
|
321 out.write(indent + "static "); |
|
322 if (methodGenericKind == GenericKind.GENERIC) |
|
323 out.write("<MT> "); |
|
324 out.write("void m" + (methodNum++) + "(int i1, long l2, float f3) {"); |
|
325 if (finalKind == FinalKind.USE_FINAL) |
|
326 out.write(" int i = fi1;"); |
|
327 out.write(" }\n"); |
|
328 break; |
|
329 } |
|
330 |
|
331 } |
|
332 } |
|
333 } |
|
334 if (nestedKind != NestedKind.NONE) { |
|
335 indent = indent.substring(0, indent.length() - 4); |
|
336 out.write(indent + "};\n"); |
|
337 } |
|
338 } |
|
339 switch (nestedKind) { |
|
340 case METHOD_ANON: case METHOD_LOCAL: |
|
341 case INIT_ANON: case INIT_LOCAL: |
|
342 indent = indent.substring(0, indent.length() - 4); |
|
343 out.write(indent + "}\n\n"); |
|
344 } |
|
345 } |
|
346 } |
|
347 out.write("}\n\n"); |
|
348 } |
|
349 } |
|
350 out.close(); |
|
351 } |
|
352 |
|
353 |
|
354 void test(String testName, boolean expectNames, String... opts) throws Exception { |
|
355 System.err.println("Test " + testName |
|
356 + ": expectNames:" + expectNames |
|
357 + " javacOpts:" + Arrays.asList(opts)); |
|
358 |
|
359 File outDir = new File(testName); |
|
360 outDir.mkdirs(); |
|
361 compile(outDir, opts); |
|
362 |
|
363 Context ctx = new Context(); |
|
364 JavacFileManager fm = new JavacFileManager(ctx, true, null); |
|
365 fm.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(outDir)); |
|
366 ClassReader cr = ClassReader.instance(ctx); |
|
367 cr.saveParameterNames = true; |
|
368 Names names = Names.instance(ctx); |
|
369 |
|
370 Set<String> classes = getTopLevelClasses(outDir); |
|
371 Deque<String> work = new LinkedList<String>(classes); |
|
372 String classname; |
|
373 while ((classname = work.poll()) != null) { |
|
374 System.err.println("Checking class " + classname); |
|
375 ClassSymbol sym = cr.enterClass(names.table.fromString(classname)); |
|
376 sym.complete(); |
|
377 |
|
378 if ((sym.flags() & Flags.INTERFACE) != 0 && !testInterfaces) |
|
379 continue; |
|
380 |
|
381 for (Scope.Entry e = sym.members_field.elems; e != null; e = e.sibling) { |
|
382 System.err.println("Checking member " + e.sym); |
|
383 switch (e.sym.kind) { |
|
384 case Kinds.TYP: { |
|
385 String name = e.sym.flatName().toString(); |
|
386 if (!classes.contains(name)) { |
|
387 classes.add(name); |
|
388 work.add(name); |
|
389 } |
|
390 break; |
|
391 } |
|
392 case Kinds.MTH: |
|
393 verify((MethodSymbol) e.sym, expectNames); |
|
394 break; |
|
395 } |
|
396 |
|
397 } |
|
398 } |
|
399 } |
|
400 |
|
401 void verify(MethodSymbol m, boolean expectNames) { |
|
402 if ((m.flags() & Flags.SYNTHETIC) != 0 && !testSyntheticMethods) |
|
403 return; |
|
404 |
|
405 //System.err.println("verify: " + m.params()); |
|
406 int i = 1; |
|
407 for (VarSymbol v: m.params()) { |
|
408 String expectName; |
|
409 if (expectNames) |
|
410 expectName = getExpectedName(v, i); |
|
411 else |
|
412 expectName = "arg" + (i - 1); |
|
413 checkEqual(expectName, v.name.toString()); |
|
414 i++; |
|
415 } |
|
416 } |
|
417 |
|
418 String getExpectedName(VarSymbol v, int i) { |
|
419 // special cases: |
|
420 // synthetic method |
|
421 if (((v.owner.owner.flags() & Flags.ENUM) != 0) |
|
422 && v.owner.name.toString().equals("valueOf")) |
|
423 return "name"; |
|
424 // interfaces don't have saved names |
|
425 // -- no Code attribute for the LocalVariableTable attribute |
|
426 if ((v.owner.owner.flags() & Flags.INTERFACE) != 0) |
|
427 return "arg" + (i - 1); |
|
428 // abstract methods don't have saved names |
|
429 // -- no Code attribute for the LocalVariableTable attribute |
|
430 if ((v.owner.flags() & Flags.ABSTRACT) != 0) |
|
431 return "arg" + (i - 1); |
|
432 // bridge methods use xN |
|
433 if ((v.owner.flags() & Flags.BRIDGE) != 0) |
|
434 return "x" + (i - 1); |
|
435 |
|
436 // The rest of this method assumes the local conventions in the test program |
|
437 Type t = v.type; |
|
438 String s; |
|
439 if (t.tag == TypeTags.CLASS) |
|
440 s = ((ClassType) t).tsym.name.toString(); |
|
441 else |
|
442 s = t.toString(); |
|
443 return String.valueOf(Character.toLowerCase(s.charAt(0))) + i; |
|
444 } |
|
445 |
|
446 void compile(File outDir, String... opts) throws Exception { |
|
447 //File testSrc = new File(System.getProperty("test.src"), "."); |
|
448 List<String> args = new ArrayList<String>(); |
|
449 args.add("-d"); |
|
450 args.add(outDir.getPath()); |
|
451 args.addAll(Arrays.asList(opts)); |
|
452 //args.add(new File(testSrc, "Test.java").getPath()); |
|
453 args.add("Test.java"); |
|
454 StringWriter sw = new StringWriter(); |
|
455 PrintWriter pw = new PrintWriter(sw); |
|
456 int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw); |
|
457 pw.close(); |
|
458 if (rc != 0) { |
|
459 System.err.println(sw.toString()); |
|
460 throw new Exception("compilation failed unexpectedly"); |
|
461 } |
|
462 } |
|
463 |
|
464 Set<String> getTopLevelClasses(File outDir) { |
|
465 Set<String> classes = new HashSet<String>(); |
|
466 for (String f: outDir.list()) { |
|
467 if (f.endsWith(".class") && !f.contains("$")) |
|
468 classes.add(f.replace(".class", "")); |
|
469 } |
|
470 return classes; |
|
471 } |
|
472 |
|
473 void checkEqual(String expect, String found) { |
|
474 if (!expect.equals(found)) |
|
475 error("mismatch: expected:" + expect + " found:" + found); |
|
476 } |
|
477 |
|
478 void error(String msg) { |
|
479 System.err.println(msg); |
|
480 errors++; |
|
481 throw new Error(); |
|
482 } |
|
483 |
|
484 int errors; |
|
485 } |