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