0N/A/*
3412N/A * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation.
0N/A *
2362N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
0N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
0N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
2362N/A */
2362N/A
0N/A/*
0N/A * @test
0N/A * @bug 6889255
0N/A * @summary ClassReader does not read parameter names correctly
0N/A */
0N/A
0N/Aimport java.io.*;
0N/Aimport java.util.*;
0N/Aimport javax.tools.StandardLocation;
0N/Aimport com.sun.tools.javac.code.Flags;
0N/Aimport com.sun.tools.javac.code.Kinds;
0N/Aimport com.sun.tools.javac.code.Scope;
0N/Aimport com.sun.tools.javac.code.Symbol.*;
0N/Aimport com.sun.tools.javac.code.Type;
0N/Aimport com.sun.tools.javac.code.Type.ClassType;
0N/Aimport com.sun.tools.javac.code.TypeTags;
0N/Aimport com.sun.tools.javac.file.JavacFileManager;
0N/Aimport com.sun.tools.javac.jvm.ClassReader;
0N/Aimport com.sun.tools.javac.util.Context;
0N/Aimport com.sun.tools.javac.util.Names;
0N/A
0N/Apublic class T6889255 {
0N/A boolean testInterfaces = true;
0N/A boolean testSyntheticMethods = true;
0N/A
0N/A // The following enums control the generation of the test methods to be compiled.
0N/A enum GenericKind {
0N/A NOT_GENERIC,
0N/A GENERIC
0N/A };
0N/A
0N/A enum ClassKind {
0N/A CLASS("Clss"),
0N/A INTERFACE("Intf"),
3412N/A ENUM("Enum");
4235N/A final String base;
4235N/A ClassKind(String base) { this.base = base; }
0N/A };
0N/A
0N/A enum NestedKind {
0N/A /** Declare methods inside the outermost container. */
178N/A NONE,
0N/A /** Declare methods inside a container with a 'static' modifier. */
0N/A NESTED,
0N/A /** Declare methods inside a container without a 'static' modifier. */
0N/A INNER,
0N/A /** Declare methods inside a local class in an initializer. */
0N/A INIT_LOCAL,
0N/A /** Declare methods inside an anonymous class in an initializer. */
0N/A INIT_ANON,
0N/A /** Declare methods inside a local class in a method. */
0N/A METHOD_LOCAL,
0N/A /** Declare methods inside an anonymous class in a method. */
0N/A METHOD_ANON
0N/A };
0N/A
0N/A enum MethodKind {
0N/A ABSTRACT,
0N/A CONSTRUCTOR,
0N/A METHOD,
0N/A STATIC_METHOD,
0N/A BRIDGE_METHOD
0N/A };
0N/A
0N/A enum FinalKind {
0N/A /** Method body does not reference external final variables. */
0N/A NO_FINAL,
0N/A /** Method body references external final variables. */
0N/A USE_FINAL
0N/A };
0N/A
0N/A public static void main(String... args) throws Exception {
0N/A new T6889255().run();
0N/A }
0N/A
0N/A void run() throws Exception {
0N/A genTest();
3412N/A
3412N/A test("no-args", false);
3412N/A test("g", true, "-g");
3412N/A
4235N/A if (errors > 0)
4235N/A throw new Exception(errors + " errors found");
4235N/A }
4235N/A
0N/A /**
0N/A * Create a file containing lots of method definitions to be tested.
3412N/A * There are 3 sets of nested loops that generate the methods.
0N/A * 1. The outermost set declares [generic] (class | interface | enum)
0N/A * 2. The middle set declares [(nested | inner | anon | local)] class
0N/A * 3. The innermost set declares
0N/A * [generic] (constructor|method|static-method|bridge-method) [using final variables in outer scope]
0N/A * Invalid combinations are filtered out.
0N/A */
0N/A void genTest() throws Exception {
0N/A BufferedWriter out = new BufferedWriter(new FileWriter("Test.java"));
0N/A
0N/A // This interface is used to force bridge methods to be generated, by
0N/A // implementing its methods with subtypes of Object
0N/A out.write("interface Base {\n");
0N/A out.write(" Object base_m1(int i1);\n");
0N/A out.write(" Object base_m2(int i1);\n");
0N/A out.write("}\n");
0N/A
0N/A int outerNum = 0;
0N/A // Outermost set of loops, to generate a top level container
0N/A for (GenericKind outerGenericKind: GenericKind.values()) {
0N/A for (ClassKind outerClassKind: ClassKind.values()) {
0N/A if (outerGenericKind == GenericKind.GENERIC && outerClassKind == ClassKind.ENUM)
0N/A continue;
0N/A String outerClassName = outerClassKind.base + (outerNum++);
0N/A String outerTypeArg = outerClassKind.toString().charAt(0) + "T";
0N/A if (outerClassKind == ClassKind.CLASS)
0N/A out.write("abstract ");
0N/A out.write(outerClassKind.toString().toLowerCase() + " " + outerClassName);
0N/A if (outerGenericKind == GenericKind.GENERIC)
0N/A out.write("<" + outerTypeArg + ">");
0N/A if (outerClassKind == ClassKind.INTERFACE)
0N/A out.write(" extends Base");
0N/A else
0N/A out.write(" implements Base");
0N/A out.write(" {\n");
0N/A if (outerClassKind == ClassKind.ENUM) {
0N/A out.write(" E1(0,0,0), E2(0,0,0), E3(0,0,0);\n");
0N/A out.write(" " + outerClassName + "(int i1, int i2, int i3) { }\n");
0N/A }
0N/A // Middle set of loops, to generate an optional nested container
0N/A int nestedNum = 0;
0N/A int methodNum = 0;
0N/A for (GenericKind nestedGenericKind: GenericKind.values()) {
0N/A nextNestedKind:
0N/A for (NestedKind nestedKind: NestedKind.values()) {
0N/A // if the nested kind is none, there is no point iterating over all
0N/A // nested generic kinds, so arbitarily limit it to just one kind
0N/A if (nestedKind == NestedKind.NONE && nestedGenericKind != GenericKind.NOT_GENERIC)
0N/A continue;
0N/A if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON)
0N/A && nestedGenericKind == GenericKind.GENERIC)
0N/A continue;
0N/A String indent = " ";
0N/A boolean haveFinal = false;
0N/A switch (nestedKind) {
0N/A case METHOD_ANON: case METHOD_LOCAL:
0N/A if (outerClassKind == ClassKind.INTERFACE)
0N/A continue nextNestedKind;
0N/A out.write(indent + "void m" + + (nestedNum++) + "() {\n");
0N/A indent += " ";
0N/A out.write(indent + "final int fi1 = 0;\n");
0N/A haveFinal = true;
0N/A break;
0N/A case INIT_ANON: case INIT_LOCAL:
0N/A if (outerClassKind == ClassKind.INTERFACE)
0N/A continue nextNestedKind;
0N/A out.write(indent + "{\n");
0N/A indent += " ";
0N/A break;
0N/A }
0N/A for (ClassKind nestedClassKind: ClassKind.values()) {
0N/A if ((nestedGenericKind == GenericKind.GENERIC)
0N/A && (nestedClassKind == ClassKind.ENUM))
0N/A continue;
0N/A if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.METHOD_LOCAL
0N/A || nestedKind == NestedKind.INIT_ANON || nestedKind == NestedKind.INIT_LOCAL)
0N/A && nestedClassKind != ClassKind.CLASS)
0N/A continue;
0N/A // if the nested kind is none, there is no point iterating over all
0N/A // nested class kinds, so arbitarily limit it to just one kind
0N/A if (nestedKind == NestedKind.NONE && nestedClassKind != ClassKind.CLASS)
0N/A continue;
0N/A
0N/A ClassKind methodClassKind;
0N/A String methodClassName;
0N/A boolean allowAbstractMethods;
0N/A boolean allowStaticMethods;
0N/A switch (nestedKind) {
0N/A case NONE:
0N/A methodClassKind = outerClassKind;
0N/A methodClassName = outerClassName;
0N/A allowAbstractMethods = (outerClassKind == ClassKind.CLASS);
0N/A allowStaticMethods = (outerClassKind != ClassKind.INTERFACE);
0N/A break;
0N/A case METHOD_ANON:
0N/A case INIT_ANON:
0N/A out.write(indent + "new Base() {\n");
0N/A indent += " ";
0N/A methodClassKind = ClassKind.CLASS;
0N/A methodClassName = null;
0N/A allowAbstractMethods = false;
0N/A allowStaticMethods = false;
0N/A break;
0N/A default: { // INNER, NESTED, LOCAL
0N/A String nestedClassName = "N" + nestedClassKind.base + (nestedNum++);
0N/A String nestedTypeArg = nestedClassKind.toString().charAt(0) + "T";
0N/A out.write(indent);
0N/A if (nestedKind == NestedKind.NESTED)
0N/A out.write("static ");
0N/A if (nestedClassKind == ClassKind.CLASS)
0N/A out.write("abstract ");
0N/A out.write(nestedClassKind.toString().toLowerCase() + " " + nestedClassName);
0N/A if (nestedGenericKind == GenericKind.GENERIC)
0N/A out.write("<" + nestedTypeArg + ">");
0N/A if (nestedClassKind == ClassKind.INTERFACE)
0N/A out.write(" extends Base ");
0N/A else
0N/A out.write(" implements Base ");
0N/A out.write(" {\n");
0N/A indent += " ";
0N/A if (nestedClassKind == ClassKind.ENUM) {
0N/A out.write(indent + "E1(0,0,0), E2(0,0,0), E3(0,0,0);\n");
0N/A out.write(indent + nestedClassName + "(int i1, int i2, int i3) { }\n");
0N/A }
0N/A methodClassKind = nestedClassKind;
0N/A methodClassName = nestedClassName;
0N/A allowAbstractMethods = (nestedClassKind == ClassKind.CLASS);
0N/A allowStaticMethods = (nestedKind == NestedKind.NESTED && nestedClassKind != ClassKind.INTERFACE);
0N/A break;
0N/A }
0N/A }
0N/A
0N/A // Innermost loops, to generate methods
0N/A for (GenericKind methodGenericKind: GenericKind.values()) {
0N/A for (FinalKind finalKind: FinalKind.values()) {
0N/A for (MethodKind methodKind: MethodKind.values()) {
0N/A// out.write("// " + outerGenericKind
0N/A// + " " + outerClassKind
0N/A// + " " + nestedKind
0N/A// + " " + nestedGenericKind
0N/A// + " " + nestedClassKind
0N/A// + " " + methodGenericKind
0N/A// + " " + finalKind
0N/A// + " " + methodKind
0N/A// + "\n");
0N/A switch (methodKind) {
0N/A case CONSTRUCTOR:
178N/A if (nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON)
0N/A break;
0N/A if (methodClassKind != ClassKind.CLASS)
0N/A break;
0N/A if (finalKind == FinalKind.USE_FINAL && !haveFinal)
0N/A break;
0N/A out.write(indent);
0N/A if (methodGenericKind == GenericKind.GENERIC) {
0N/A out.write("<CT> " + methodClassName + "(CT c1, CT c2");
0N/A } else {
0N/A out.write(methodClassName + "(boolean b1, char c2");
0N/A }
0N/A if (finalKind == FinalKind.USE_FINAL) {
0N/A // 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("<MT> ");
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("<MT> ");
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<String> classes = getTopLevelClasses(outDir);
Deque<String> work = new LinkedList<String>(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 xN
if ((v.owner.flags() & Flags.BRIDGE) != 0)
return "x" + (i - 1);
// The rest of this method assumes the local conventions in the test program
Type t = v.type;
String s;
if (t.tag == TypeTags.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<String> args = new ArrayList<String>();
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<String> getTopLevelClasses(File outDir) {
Set<String> classes = new HashSet<String>();
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;
}