test/tools/javac/6889255/T6889255.java

Mon, 19 Oct 2009 13:38:09 -0700

author
jjg
date
Mon, 19 Oct 2009 13:38:09 -0700
changeset 428
2485f5641ed0
child 554
9d9f26857129
permissions
-rw-r--r--

6889255: javac MethodSymbol throws NPE if ClassReader does not read parameter names correctly
Reviewed-by: darcy

     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  */
    24 /*
    25  * @test
    26  * @bug 6889255
    27  * @summary ClassReader does not read parameter names correctly
    28  */
    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;
    45 public class T6889255 {
    46     boolean testInterfaces = true;
    47     boolean testSyntheticMethods = true;
    49     // The following enums control the generation of the test methods to be compiled.
    50     enum GenericKind {
    51         NOT_GENERIC,
    52         GENERIC
    53     };
    55     enum ClassKind {
    56         CLASS("Clss"),
    57         INTERFACE("Intf"),
    58         ENUM("Enum");
    59         final String base;
    60         ClassKind(String base) { this.base = base; }
    61     };
    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     };
    80     enum MethodKind {
    81         ABSTRACT,
    82         CONSTRUCTOR,
    83         METHOD,
    84         STATIC_METHOD,
    85         BRIDGE_METHOD
    86     };
    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     };
    95     public static void main(String... args) throws Exception {
    96         new T6889255().run();
    97     }
    99     void run() throws Exception {
   100         genTest();
   102         test("no-args", false);
   103         test("g",       true,  "-g");
   105         if (errors > 0)
   106             throw new Exception(errors + " errors found");
   107     }
   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"));
   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");
   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;
   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                             }
   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                                         }
   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     }
   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));
   359         File outDir = new File(testName);
   360         outDir.mkdirs();
   361         compile(outDir, opts);
   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);
   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();
   378             if ((sym.flags() & Flags.INTERFACE) != 0 && !testInterfaces)
   379                 continue;
   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                 }
   397             }
   398         }
   399     }
   401     void verify(MethodSymbol m, boolean expectNames) {
   402         if ((m.flags() & Flags.SYNTHETIC) != 0 && !testSyntheticMethods)
   403             return;
   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     }
   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);
   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     }
   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     }
   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     }
   473     void checkEqual(String expect, String found) {
   474         if (!expect.equals(found))
   475             error("mismatch: expected:" + expect + " found:" + found);
   476     }
   478     void error(String msg) {
   479         System.err.println(msg);
   480         errors++;
   481         throw new Error();
   482     }
   484     int errors;
   485 }

mercurial