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