jjg@428: /* jjg@1374: * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. jjg@428: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@428: * jjg@428: * This code is free software; you can redistribute it and/or modify it jjg@428: * under the terms of the GNU General Public License version 2 only, as jjg@428: * published by the Free Software Foundation. jjg@428: * jjg@428: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@428: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@428: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@428: * version 2 for more details (a copy is included in the LICENSE file that jjg@428: * accompanied this code). jjg@428: * jjg@428: * You should have received a copy of the GNU General Public License version jjg@428: * 2 along with this work; if not, write to the Free Software Foundation, jjg@428: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@428: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. jjg@428: */ jjg@428: jjg@428: /* jjg@428: * @test jjg@428: * @bug 6889255 jjg@428: * @summary ClassReader does not read parameter names correctly jjg@428: */ jjg@428: jjg@428: import java.io.*; jjg@428: import java.util.*; jjg@428: import javax.tools.StandardLocation; jjg@428: import com.sun.tools.javac.code.Flags; jjg@428: import com.sun.tools.javac.code.Kinds; jjg@428: import com.sun.tools.javac.code.Scope; jjg@428: import com.sun.tools.javac.code.Symbol.*; jjg@428: import com.sun.tools.javac.code.Type; jjg@428: import com.sun.tools.javac.code.Type.ClassType; jjg@1374: import com.sun.tools.javac.code.TypeTag; jjg@428: import com.sun.tools.javac.file.JavacFileManager; jjg@428: import com.sun.tools.javac.jvm.ClassReader; jjg@428: import com.sun.tools.javac.util.Context; jjg@428: import com.sun.tools.javac.util.Names; jjg@428: jjg@428: public class T6889255 { jjg@428: boolean testInterfaces = true; jjg@428: boolean testSyntheticMethods = true; jjg@428: jjg@428: // The following enums control the generation of the test methods to be compiled. jjg@428: enum GenericKind { jjg@428: NOT_GENERIC, jjg@428: GENERIC jjg@428: }; jjg@428: jjg@428: enum ClassKind { jjg@428: CLASS("Clss"), jjg@428: INTERFACE("Intf"), jjg@428: ENUM("Enum"); jjg@428: final String base; jjg@428: ClassKind(String base) { this.base = base; } jjg@428: }; jjg@428: jjg@428: enum NestedKind { jjg@428: /** Declare methods inside the outermost container. */ jjg@428: NONE, jjg@428: /** Declare methods inside a container with a 'static' modifier. */ jjg@428: NESTED, jjg@428: /** Declare methods inside a container without a 'static' modifier. */ jjg@428: INNER, jjg@428: /** Declare methods inside a local class in an initializer. */ jjg@428: INIT_LOCAL, jjg@428: /** Declare methods inside an anonymous class in an initializer. */ jjg@428: INIT_ANON, jjg@428: /** Declare methods inside a local class in a method. */ jjg@428: METHOD_LOCAL, jjg@428: /** Declare methods inside an anonymous class in a method. */ jjg@428: METHOD_ANON jjg@428: }; jjg@428: jjg@428: enum MethodKind { jjg@428: ABSTRACT, jjg@428: CONSTRUCTOR, jjg@428: METHOD, jjg@428: STATIC_METHOD, jjg@428: BRIDGE_METHOD jjg@428: }; jjg@428: jjg@428: enum FinalKind { jjg@428: /** Method body does not reference external final variables. */ jjg@428: NO_FINAL, jjg@428: /** Method body references external final variables. */ jjg@428: USE_FINAL jjg@428: }; jjg@428: jjg@428: public static void main(String... args) throws Exception { jjg@428: new T6889255().run(); jjg@428: } jjg@428: jjg@428: void run() throws Exception { jjg@428: genTest(); jjg@428: jjg@428: test("no-args", false); jjg@428: test("g", true, "-g"); jjg@428: jjg@428: if (errors > 0) jjg@428: throw new Exception(errors + " errors found"); jjg@428: } jjg@428: jjg@428: /** jjg@428: * Create a file containing lots of method definitions to be tested. jjg@428: * There are 3 sets of nested loops that generate the methods. jjg@428: * 1. The outermost set declares [generic] (class | interface | enum) jjg@428: * 2. The middle set declares [(nested | inner | anon | local)] class jjg@428: * 3. The innermost set declares jjg@428: * [generic] (constructor|method|static-method|bridge-method) [using final variables in outer scope] jjg@428: * Invalid combinations are filtered out. jjg@428: */ jjg@428: void genTest() throws Exception { jjg@428: BufferedWriter out = new BufferedWriter(new FileWriter("Test.java")); jjg@428: jjg@428: // This interface is used to force bridge methods to be generated, by jjg@428: // implementing its methods with subtypes of Object jjg@428: out.write("interface Base {\n"); jjg@428: out.write(" Object base_m1(int i1);\n"); jjg@428: out.write(" Object base_m2(int i1);\n"); jjg@428: out.write("}\n"); jjg@428: jjg@428: int outerNum = 0; jjg@428: // Outermost set of loops, to generate a top level container jjg@428: for (GenericKind outerGenericKind: GenericKind.values()) { jjg@428: for (ClassKind outerClassKind: ClassKind.values()) { jjg@428: if (outerGenericKind == GenericKind.GENERIC && outerClassKind == ClassKind.ENUM) jjg@428: continue; jjg@428: String outerClassName = outerClassKind.base + (outerNum++); jjg@428: String outerTypeArg = outerClassKind.toString().charAt(0) + "T"; jjg@428: if (outerClassKind == ClassKind.CLASS) jjg@428: out.write("abstract "); jjg@428: out.write(outerClassKind.toString().toLowerCase() + " " + outerClassName); jjg@428: if (outerGenericKind == GenericKind.GENERIC) jjg@428: out.write("<" + outerTypeArg + ">"); jjg@428: if (outerClassKind == ClassKind.INTERFACE) jjg@428: out.write(" extends Base"); jjg@428: else jjg@428: out.write(" implements Base"); jjg@428: out.write(" {\n"); jjg@428: if (outerClassKind == ClassKind.ENUM) { jjg@428: out.write(" E1(0,0,0), E2(0,0,0), E3(0,0,0);\n"); jjg@428: out.write(" " + outerClassName + "(int i1, int i2, int i3) { }\n"); jjg@428: } jjg@428: // Middle set of loops, to generate an optional nested container jjg@428: int nestedNum = 0; jjg@428: int methodNum = 0; jjg@428: for (GenericKind nestedGenericKind: GenericKind.values()) { jjg@428: nextNestedKind: jjg@428: for (NestedKind nestedKind: NestedKind.values()) { jjg@428: // if the nested kind is none, there is no point iterating over all jjg@428: // nested generic kinds, so arbitarily limit it to just one kind jjg@428: if (nestedKind == NestedKind.NONE && nestedGenericKind != GenericKind.NOT_GENERIC) jjg@428: continue; jjg@428: if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON) jjg@428: && nestedGenericKind == GenericKind.GENERIC) jjg@428: continue; jjg@428: String indent = " "; jjg@428: boolean haveFinal = false; jjg@428: switch (nestedKind) { jjg@428: case METHOD_ANON: case METHOD_LOCAL: jjg@428: if (outerClassKind == ClassKind.INTERFACE) jjg@428: continue nextNestedKind; jjg@428: out.write(indent + "void m" + + (nestedNum++) + "() {\n"); jjg@428: indent += " "; jjg@428: out.write(indent + "final int fi1 = 0;\n"); jjg@428: haveFinal = true; jjg@428: break; jjg@428: case INIT_ANON: case INIT_LOCAL: jjg@428: if (outerClassKind == ClassKind.INTERFACE) jjg@428: continue nextNestedKind; jjg@428: out.write(indent + "{\n"); jjg@428: indent += " "; jjg@428: break; jjg@428: } jjg@428: for (ClassKind nestedClassKind: ClassKind.values()) { jjg@428: if ((nestedGenericKind == GenericKind.GENERIC) jjg@428: && (nestedClassKind == ClassKind.ENUM)) jjg@428: continue; jjg@428: if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.METHOD_LOCAL jjg@428: || nestedKind == NestedKind.INIT_ANON || nestedKind == NestedKind.INIT_LOCAL) jjg@428: && nestedClassKind != ClassKind.CLASS) jjg@428: continue; jjg@428: // if the nested kind is none, there is no point iterating over all jjg@428: // nested class kinds, so arbitarily limit it to just one kind jjg@428: if (nestedKind == NestedKind.NONE && nestedClassKind != ClassKind.CLASS) jjg@428: continue; jjg@428: jjg@428: ClassKind methodClassKind; jjg@428: String methodClassName; jjg@428: boolean allowAbstractMethods; jjg@428: boolean allowStaticMethods; jjg@428: switch (nestedKind) { jjg@428: case NONE: jjg@428: methodClassKind = outerClassKind; jjg@428: methodClassName = outerClassName; jjg@428: allowAbstractMethods = (outerClassKind == ClassKind.CLASS); jjg@428: allowStaticMethods = (outerClassKind != ClassKind.INTERFACE); jjg@428: break; jjg@428: case METHOD_ANON: jjg@428: case INIT_ANON: jjg@428: out.write(indent + "new Base() {\n"); jjg@428: indent += " "; jjg@428: methodClassKind = ClassKind.CLASS; jjg@428: methodClassName = null; jjg@428: allowAbstractMethods = false; jjg@428: allowStaticMethods = false; jjg@428: break; jjg@428: default: { // INNER, NESTED, LOCAL jjg@428: String nestedClassName = "N" + nestedClassKind.base + (nestedNum++); jjg@428: String nestedTypeArg = nestedClassKind.toString().charAt(0) + "T"; jjg@428: out.write(indent); jjg@428: if (nestedKind == NestedKind.NESTED) jjg@428: out.write("static "); jjg@428: if (nestedClassKind == ClassKind.CLASS) jjg@428: out.write("abstract "); jjg@428: out.write(nestedClassKind.toString().toLowerCase() + " " + nestedClassName); jjg@428: if (nestedGenericKind == GenericKind.GENERIC) jjg@428: out.write("<" + nestedTypeArg + ">"); jjg@428: if (nestedClassKind == ClassKind.INTERFACE) jjg@428: out.write(" extends Base "); jjg@428: else jjg@428: out.write(" implements Base "); jjg@428: out.write(" {\n"); jjg@428: indent += " "; jjg@428: if (nestedClassKind == ClassKind.ENUM) { jjg@428: out.write(indent + "E1(0,0,0), E2(0,0,0), E3(0,0,0);\n"); jjg@428: out.write(indent + nestedClassName + "(int i1, int i2, int i3) { }\n"); jjg@428: } jjg@428: methodClassKind = nestedClassKind; jjg@428: methodClassName = nestedClassName; jjg@428: allowAbstractMethods = (nestedClassKind == ClassKind.CLASS); jjg@428: allowStaticMethods = (nestedKind == NestedKind.NESTED && nestedClassKind != ClassKind.INTERFACE); jjg@428: break; jjg@428: } jjg@428: } jjg@428: jjg@428: // Innermost loops, to generate methods jjg@428: for (GenericKind methodGenericKind: GenericKind.values()) { jjg@428: for (FinalKind finalKind: FinalKind.values()) { jjg@428: for (MethodKind methodKind: MethodKind.values()) { jjg@428: // out.write("// " + outerGenericKind jjg@428: // + " " + outerClassKind jjg@428: // + " " + nestedKind jjg@428: // + " " + nestedGenericKind jjg@428: // + " " + nestedClassKind jjg@428: // + " " + methodGenericKind jjg@428: // + " " + finalKind jjg@428: // + " " + methodKind jjg@428: // + "\n"); jjg@428: switch (methodKind) { jjg@428: case CONSTRUCTOR: jjg@428: if (nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON) jjg@428: break; jjg@428: if (methodClassKind != ClassKind.CLASS) jjg@428: break; jjg@428: if (finalKind == FinalKind.USE_FINAL && !haveFinal) jjg@428: break; jjg@428: out.write(indent); jjg@428: if (methodGenericKind == GenericKind.GENERIC) { jjg@428: out.write(" " + methodClassName + "(CT c1, CT c2"); jjg@428: } else { jjg@428: out.write(methodClassName + "(boolean b1, char c2"); jjg@428: } jjg@428: if (finalKind == FinalKind.USE_FINAL) { jjg@428: // add a dummy parameter to avoid duplicate declaration jjg@428: out.write(", int i3) { int i = fi1; }\n"); jjg@428: } else jjg@428: out.write(") { }\n"); jjg@428: break; jjg@428: case ABSTRACT: jjg@428: if (!allowAbstractMethods) jjg@428: continue; jjg@428: // fallthrough jjg@428: case METHOD: jjg@428: if (finalKind == FinalKind.USE_FINAL && !haveFinal) jjg@428: break; jjg@428: out.write(indent); jjg@428: if (methodKind == MethodKind.ABSTRACT) jjg@428: out.write("abstract "); jjg@428: if (methodGenericKind == GenericKind.GENERIC) jjg@428: out.write(" "); jjg@428: out.write("void m" + (methodNum++) + "(int i1, long l2, float f3)"); jjg@428: if (methodKind == MethodKind.ABSTRACT || methodClassKind == ClassKind.INTERFACE) jjg@428: out.write(";\n"); jjg@428: else { jjg@428: out.write(" {"); jjg@428: if (finalKind == FinalKind.USE_FINAL) jjg@428: out.write(" int i = fi1;"); jjg@428: out.write(" }\n"); jjg@428: } jjg@428: break; jjg@428: case BRIDGE_METHOD: jjg@428: if (methodGenericKind == GenericKind.GENERIC) jjg@428: break; jjg@428: out.write(indent); jjg@428: // methods Base.base_m1 and Base.base_m2 are declared for the jjg@428: // benefit of bridge methods. They need to be implemented jjg@428: // whether or not a final variable is used. jjg@428: String methodName = (finalKind == FinalKind.NO_FINAL ? "base_m1" : "base_m2"); jjg@428: out.write("public String " + methodName + "(int i1)"); jjg@428: if (methodClassKind == ClassKind.INTERFACE) jjg@428: out.write(";\n"); jjg@428: else { jjg@428: out.write(" {"); jjg@428: if (finalKind == FinalKind.USE_FINAL && haveFinal) jjg@428: out.write(" int i = fi1;"); jjg@428: out.write(" return null; }\n"); jjg@428: } jjg@428: break; jjg@428: case STATIC_METHOD: jjg@428: if (!allowStaticMethods) jjg@428: break; jjg@428: if (finalKind == FinalKind.USE_FINAL && !haveFinal) jjg@428: break; jjg@428: out.write(indent + "static "); jjg@428: if (methodGenericKind == GenericKind.GENERIC) jjg@428: out.write(" "); jjg@428: out.write("void m" + (methodNum++) + "(int i1, long l2, float f3) {"); jjg@428: if (finalKind == FinalKind.USE_FINAL) jjg@428: out.write(" int i = fi1;"); jjg@428: out.write(" }\n"); jjg@428: break; jjg@428: } jjg@428: jjg@428: } jjg@428: } jjg@428: } jjg@428: if (nestedKind != NestedKind.NONE) { jjg@428: indent = indent.substring(0, indent.length() - 4); jjg@428: out.write(indent + "};\n"); jjg@428: } jjg@428: } jjg@428: switch (nestedKind) { jjg@428: case METHOD_ANON: case METHOD_LOCAL: jjg@428: case INIT_ANON: case INIT_LOCAL: jjg@428: indent = indent.substring(0, indent.length() - 4); jjg@428: out.write(indent + "}\n\n"); jjg@428: } jjg@428: } jjg@428: } jjg@428: out.write("}\n\n"); jjg@428: } jjg@428: } jjg@428: out.close(); jjg@428: } jjg@428: jjg@428: jjg@428: void test(String testName, boolean expectNames, String... opts) throws Exception { jjg@428: System.err.println("Test " + testName jjg@428: + ": expectNames:" + expectNames jjg@428: + " javacOpts:" + Arrays.asList(opts)); jjg@428: jjg@428: File outDir = new File(testName); jjg@428: outDir.mkdirs(); jjg@428: compile(outDir, opts); jjg@428: jjg@428: Context ctx = new Context(); jjg@428: JavacFileManager fm = new JavacFileManager(ctx, true, null); jjg@428: fm.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(outDir)); jjg@428: ClassReader cr = ClassReader.instance(ctx); jjg@428: cr.saveParameterNames = true; jjg@428: Names names = Names.instance(ctx); jjg@428: jjg@428: Set classes = getTopLevelClasses(outDir); jjg@428: Deque work = new LinkedList(classes); jjg@428: String classname; jjg@428: while ((classname = work.poll()) != null) { jjg@428: System.err.println("Checking class " + classname); jjg@428: ClassSymbol sym = cr.enterClass(names.table.fromString(classname)); jjg@428: sym.complete(); jjg@428: jjg@428: if ((sym.flags() & Flags.INTERFACE) != 0 && !testInterfaces) jjg@428: continue; jjg@428: jjg@428: for (Scope.Entry e = sym.members_field.elems; e != null; e = e.sibling) { jjg@428: System.err.println("Checking member " + e.sym); jjg@428: switch (e.sym.kind) { jjg@428: case Kinds.TYP: { jjg@428: String name = e.sym.flatName().toString(); jjg@428: if (!classes.contains(name)) { jjg@428: classes.add(name); jjg@428: work.add(name); jjg@428: } jjg@428: break; jjg@428: } jjg@428: case Kinds.MTH: jjg@428: verify((MethodSymbol) e.sym, expectNames); jjg@428: break; jjg@428: } jjg@428: jjg@428: } jjg@428: } jjg@428: } jjg@428: jjg@428: void verify(MethodSymbol m, boolean expectNames) { jjg@428: if ((m.flags() & Flags.SYNTHETIC) != 0 && !testSyntheticMethods) jjg@428: return; jjg@428: jjg@428: //System.err.println("verify: " + m.params()); jjg@428: int i = 1; jjg@428: for (VarSymbol v: m.params()) { jjg@428: String expectName; jjg@428: if (expectNames) jjg@428: expectName = getExpectedName(v, i); jjg@428: else jjg@428: expectName = "arg" + (i - 1); jjg@428: checkEqual(expectName, v.name.toString()); jjg@428: i++; jjg@428: } jjg@428: } jjg@428: jjg@428: String getExpectedName(VarSymbol v, int i) { jjg@428: // special cases: jjg@428: // synthetic method jjg@428: if (((v.owner.owner.flags() & Flags.ENUM) != 0) jjg@428: && v.owner.name.toString().equals("valueOf")) jjg@428: return "name"; jjg@428: // interfaces don't have saved names jjg@428: // -- no Code attribute for the LocalVariableTable attribute jjg@428: if ((v.owner.owner.flags() & Flags.INTERFACE) != 0) jjg@428: return "arg" + (i - 1); jjg@428: // abstract methods don't have saved names jjg@428: // -- no Code attribute for the LocalVariableTable attribute jjg@428: if ((v.owner.flags() & Flags.ABSTRACT) != 0) jjg@428: return "arg" + (i - 1); jjg@428: // bridge methods use xN jjg@428: if ((v.owner.flags() & Flags.BRIDGE) != 0) jjg@428: return "x" + (i - 1); jjg@428: jjg@428: // The rest of this method assumes the local conventions in the test program jjg@428: Type t = v.type; jjg@428: String s; jjg@1374: if (t.hasTag(TypeTag.CLASS)) jjg@428: s = ((ClassType) t).tsym.name.toString(); jjg@428: else jjg@428: s = t.toString(); jjg@428: return String.valueOf(Character.toLowerCase(s.charAt(0))) + i; jjg@428: } jjg@428: jjg@428: void compile(File outDir, String... opts) throws Exception { jjg@428: //File testSrc = new File(System.getProperty("test.src"), "."); jjg@428: List args = new ArrayList(); jjg@428: args.add("-d"); jjg@428: args.add(outDir.getPath()); jjg@428: args.addAll(Arrays.asList(opts)); jjg@428: //args.add(new File(testSrc, "Test.java").getPath()); jjg@428: args.add("Test.java"); jjg@428: StringWriter sw = new StringWriter(); jjg@428: PrintWriter pw = new PrintWriter(sw); jjg@428: int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw); jjg@428: pw.close(); jjg@428: if (rc != 0) { jjg@428: System.err.println(sw.toString()); jjg@428: throw new Exception("compilation failed unexpectedly"); jjg@428: } jjg@428: } jjg@428: jjg@428: Set getTopLevelClasses(File outDir) { jjg@428: Set classes = new HashSet(); jjg@428: for (String f: outDir.list()) { jjg@428: if (f.endsWith(".class") && !f.contains("$")) jjg@428: classes.add(f.replace(".class", "")); jjg@428: } jjg@428: return classes; jjg@428: } jjg@428: jjg@428: void checkEqual(String expect, String found) { jjg@428: if (!expect.equals(found)) jjg@428: error("mismatch: expected:" + expect + " found:" + found); jjg@428: } jjg@428: jjg@428: void error(String msg) { jjg@428: System.err.println(msg); jjg@428: errors++; jjg@428: throw new Error(); jjg@428: } jjg@428: jjg@428: int errors; jjg@428: }