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 }