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