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 }