Path: blob/master/test/langtools/tools/javac/6889255/T6889255.java
41152 views
/*1* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 688925526* @summary ClassReader does not read parameter names correctly27* @modules jdk.compiler/com.sun.tools.javac.code28* jdk.compiler/com.sun.tools.javac.file29* jdk.compiler/com.sun.tools.javac.jvm30* jdk.compiler/com.sun.tools.javac.util31*/3233import java.io.*;34import java.util.*;35import javax.tools.StandardLocation;36import com.sun.tools.javac.code.Flags;37import com.sun.tools.javac.code.Symbol;38import com.sun.tools.javac.code.Symbol.*;39import com.sun.tools.javac.code.Symtab;40import com.sun.tools.javac.code.Type;41import com.sun.tools.javac.code.Type.ClassType;42import com.sun.tools.javac.code.TypeTag;43import com.sun.tools.javac.file.JavacFileManager;44import com.sun.tools.javac.jvm.ClassReader;45import com.sun.tools.javac.util.Context;46import com.sun.tools.javac.util.Names;4748import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;4950public class T6889255 {51boolean testInterfaces = true;52boolean testSyntheticMethods = true;5354// The following enums control the generation of the test methods to be compiled.55enum GenericKind {56NOT_GENERIC,57GENERIC58};5960enum ClassKind {61CLASS("Clss"),62INTERFACE("Intf"),63ENUM("Enum");64final String base;65ClassKind(String base) { this.base = base; }66};6768enum NestedKind {69/** Declare methods inside the outermost container. */70NONE,71/** Declare methods inside a container with a 'static' modifier. */72NESTED,73/** Declare methods inside a container without a 'static' modifier. */74INNER,75/** Declare methods inside a local class in an initializer. */76INIT_LOCAL,77/** Declare methods inside an anonymous class in an initializer. */78INIT_ANON,79/** Declare methods inside a local class in a method. */80METHOD_LOCAL,81/** Declare methods inside an anonymous class in a method. */82METHOD_ANON83};8485enum MethodKind {86ABSTRACT,87CONSTRUCTOR,88METHOD,89STATIC_METHOD,90BRIDGE_METHOD91};9293enum FinalKind {94/** Method body does not reference external final variables. */95NO_FINAL,96/** Method body references external final variables. */97USE_FINAL98};99100public static void main(String... args) throws Exception {101new T6889255().run();102}103104void run() throws Exception {105genTest();106107test("no-args", false);108test("g", true, "-g");109110if (errors > 0)111throw new Exception(errors + " errors found");112}113114/**115* Create a file containing lots of method definitions to be tested.116* There are 3 sets of nested loops that generate the methods.117* 1. The outermost set declares [generic] (class | interface | enum)118* 2. The middle set declares [(nested | inner | anon | local)] class119* 3. The innermost set declares120* [generic] (constructor|method|static-method|bridge-method) [using final variables in outer scope]121* Invalid combinations are filtered out.122*/123void genTest() throws Exception {124BufferedWriter out = new BufferedWriter(new FileWriter("Test.java"));125126// This interface is used to force bridge methods to be generated, by127// implementing its methods with subtypes of Object128out.write("interface Base {\n");129out.write(" Object base_m1(int i1);\n");130out.write(" Object base_m2(int i1);\n");131out.write("}\n");132133int outerNum = 0;134// Outermost set of loops, to generate a top level container135for (GenericKind outerGenericKind: GenericKind.values()) {136for (ClassKind outerClassKind: ClassKind.values()) {137if (outerGenericKind == GenericKind.GENERIC && outerClassKind == ClassKind.ENUM)138continue;139String outerClassName = outerClassKind.base + (outerNum++);140String outerTypeArg = outerClassKind.toString().charAt(0) + "T";141if (outerClassKind == ClassKind.CLASS)142out.write("abstract ");143out.write(outerClassKind.toString().toLowerCase() + " " + outerClassName);144if (outerGenericKind == GenericKind.GENERIC)145out.write("<" + outerTypeArg + ">");146if (outerClassKind == ClassKind.INTERFACE)147out.write(" extends Base");148else149out.write(" implements Base");150out.write(" {\n");151if (outerClassKind == ClassKind.ENUM) {152out.write(" E1(0,0,0), E2(0,0,0), E3(0,0,0);\n");153out.write(" " + outerClassName + "(int i1, int i2, int i3) { }\n");154}155// Middle set of loops, to generate an optional nested container156int nestedNum = 0;157int methodNum = 0;158for (GenericKind nestedGenericKind: GenericKind.values()) {159nextNestedKind:160for (NestedKind nestedKind: NestedKind.values()) {161// if the nested kind is none, there is no point iterating over all162// nested generic kinds, so arbitarily limit it to just one kind163if (nestedKind == NestedKind.NONE && nestedGenericKind != GenericKind.NOT_GENERIC)164continue;165if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON)166&& nestedGenericKind == GenericKind.GENERIC)167continue;168String indent = " ";169boolean haveFinal = false;170switch (nestedKind) {171case METHOD_ANON: case METHOD_LOCAL:172if (outerClassKind == ClassKind.INTERFACE)173continue nextNestedKind;174out.write(indent + "void m" + + (nestedNum++) + "() {\n");175indent += " ";176out.write(indent + "final int fi1 = 0;\n");177haveFinal = true;178break;179case INIT_ANON: case INIT_LOCAL:180if (outerClassKind == ClassKind.INTERFACE)181continue nextNestedKind;182out.write(indent + "{\n");183indent += " ";184break;185}186for (ClassKind nestedClassKind: ClassKind.values()) {187if ((nestedGenericKind == GenericKind.GENERIC)188&& (nestedClassKind == ClassKind.ENUM))189continue;190if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.METHOD_LOCAL191|| nestedKind == NestedKind.INIT_ANON || nestedKind == NestedKind.INIT_LOCAL)192&& nestedClassKind != ClassKind.CLASS)193continue;194// if the nested kind is none, there is no point iterating over all195// nested class kinds, so arbitarily limit it to just one kind196if (nestedKind == NestedKind.NONE && nestedClassKind != ClassKind.CLASS)197continue;198199ClassKind methodClassKind;200String methodClassName;201boolean allowAbstractMethods;202boolean allowStaticMethods;203switch (nestedKind) {204case NONE:205methodClassKind = outerClassKind;206methodClassName = outerClassName;207allowAbstractMethods = (outerClassKind == ClassKind.CLASS);208allowStaticMethods = (outerClassKind != ClassKind.INTERFACE);209break;210case METHOD_ANON:211case INIT_ANON:212out.write(indent + "new Base() {\n");213indent += " ";214methodClassKind = ClassKind.CLASS;215methodClassName = null;216allowAbstractMethods = false;217allowStaticMethods = false;218break;219default: { // INNER, NESTED, LOCAL220String nestedClassName = "N" + nestedClassKind.base + (nestedNum++);221String nestedTypeArg = nestedClassKind.toString().charAt(0) + "T";222out.write(indent);223if (nestedKind == NestedKind.NESTED)224out.write("static ");225if (nestedClassKind == ClassKind.CLASS)226out.write("abstract ");227out.write(nestedClassKind.toString().toLowerCase() + " " + nestedClassName);228if (nestedGenericKind == GenericKind.GENERIC)229out.write("<" + nestedTypeArg + ">");230if (nestedClassKind == ClassKind.INTERFACE)231out.write(" extends Base ");232else233out.write(" implements Base ");234out.write(" {\n");235indent += " ";236if (nestedClassKind == ClassKind.ENUM) {237out.write(indent + "E1(0,0,0), E2(0,0,0), E3(0,0,0);\n");238out.write(indent + nestedClassName + "(int i1, int i2, int i3) { }\n");239}240methodClassKind = nestedClassKind;241methodClassName = nestedClassName;242allowAbstractMethods = (nestedClassKind == ClassKind.CLASS);243allowStaticMethods = (nestedKind == NestedKind.NESTED && nestedClassKind != ClassKind.INTERFACE);244break;245}246}247248// Innermost loops, to generate methods249for (GenericKind methodGenericKind: GenericKind.values()) {250for (FinalKind finalKind: FinalKind.values()) {251for (MethodKind methodKind: MethodKind.values()) {252// out.write("// " + outerGenericKind253// + " " + outerClassKind254// + " " + nestedKind255// + " " + nestedGenericKind256// + " " + nestedClassKind257// + " " + methodGenericKind258// + " " + finalKind259// + " " + methodKind260// + "\n");261switch (methodKind) {262case CONSTRUCTOR:263if (nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON)264break;265if (methodClassKind != ClassKind.CLASS)266break;267if (finalKind == FinalKind.USE_FINAL && !haveFinal)268break;269out.write(indent);270if (methodGenericKind == GenericKind.GENERIC) {271out.write("<CT> " + methodClassName + "(CT c1, CT c2");272} else {273out.write(methodClassName + "(boolean b1, char c2");274}275if (finalKind == FinalKind.USE_FINAL) {276// add a dummy parameter to avoid duplicate declaration277out.write(", int i3) { int i = fi1; }\n");278} else279out.write(") { }\n");280break;281case ABSTRACT:282if (!allowAbstractMethods)283continue;284// fallthrough285case METHOD:286if (finalKind == FinalKind.USE_FINAL && !haveFinal)287break;288out.write(indent);289if (methodKind == MethodKind.ABSTRACT)290out.write("abstract ");291if (methodGenericKind == GenericKind.GENERIC)292out.write("<MT> ");293out.write("void m" + (methodNum++) + "(int i1, long l2, float f3)");294if (methodKind == MethodKind.ABSTRACT || methodClassKind == ClassKind.INTERFACE)295out.write(";\n");296else {297out.write(" {");298if (finalKind == FinalKind.USE_FINAL)299out.write(" int i = fi1;");300out.write(" }\n");301}302break;303case BRIDGE_METHOD:304if (methodGenericKind == GenericKind.GENERIC)305break;306out.write(indent);307// methods Base.base_m1 and Base.base_m2 are declared for the308// benefit of bridge methods. They need to be implemented309// whether or not a final variable is used.310String methodName = (finalKind == FinalKind.NO_FINAL ? "base_m1" : "base_m2");311out.write("public String " + methodName + "(int i1)");312if (methodClassKind == ClassKind.INTERFACE)313out.write(";\n");314else {315out.write(" {");316if (finalKind == FinalKind.USE_FINAL && haveFinal)317out.write(" int i = fi1;");318out.write(" return null; }\n");319}320break;321case STATIC_METHOD:322if (!allowStaticMethods)323break;324if (finalKind == FinalKind.USE_FINAL && !haveFinal)325break;326out.write(indent + "static ");327if (methodGenericKind == GenericKind.GENERIC)328out.write("<MT> ");329out.write("void m" + (methodNum++) + "(int i1, long l2, float f3) {");330if (finalKind == FinalKind.USE_FINAL)331out.write(" int i = fi1;");332out.write(" }\n");333break;334}335336}337}338}339if (nestedKind != NestedKind.NONE) {340indent = indent.substring(0, indent.length() - 4);341out.write(indent + "};\n");342}343}344switch (nestedKind) {345case METHOD_ANON: case METHOD_LOCAL:346case INIT_ANON: case INIT_LOCAL:347indent = indent.substring(0, indent.length() - 4);348out.write(indent + "}\n\n");349}350}351}352out.write("}\n\n");353}354}355out.close();356}357358359void test(String testName, boolean expectNames, String... opts) throws Exception {360System.err.println("Test " + testName361+ ": expectNames:" + expectNames362+ " javacOpts:" + Arrays.asList(opts));363364File outDir = new File(testName);365outDir.mkdirs();366compile(outDir, opts);367368Context ctx = new Context();369JavacFileManager fm = new JavacFileManager(ctx, true, null);370fm.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(outDir));371Symtab syms = Symtab.instance(ctx);372ClassReader cr = ClassReader.instance(ctx);373cr.saveParameterNames = true;374Names names = Names.instance(ctx);375376Set<String> classes = getTopLevelClasses(outDir);377Deque<String> work = new LinkedList<String>(classes);378String classname;379while ((classname = work.poll()) != null) {380System.err.println("Checking class " + classname);381ClassSymbol sym = syms.enterClass(syms.noModule, names.table.fromString(classname));382sym.complete();383384if ((sym.flags() & Flags.INTERFACE) != 0 && !testInterfaces)385continue;386387for (Symbol s : sym.members_field.getSymbols(NON_RECURSIVE)) {388System.err.println("Checking member " + s);389switch (s.kind) {390case TYP: {391String name = s.flatName().toString();392if (!classes.contains(name)) {393classes.add(name);394work.add(name);395}396break;397}398case MTH:399verify((MethodSymbol) s, expectNames);400break;401}402403}404}405}406407void verify(MethodSymbol m, boolean expectNames) {408if ((m.flags() & Flags.SYNTHETIC) != 0 && !testSyntheticMethods)409return;410411//System.err.println("verify: " + m.params());412int i = 1;413for (VarSymbol v: m.params()) {414String expectName;415if (expectNames)416expectName = getExpectedName(v, i);417else418expectName = "arg" + (i - 1);419checkEqual(expectName, v.name.toString());420i++;421}422}423424String getExpectedName(VarSymbol v, int i) {425// special cases:426// synthetic method427if (((v.owner.owner.flags() & Flags.ENUM) != 0)428&& v.owner.name.toString().equals("valueOf"))429return "name";430// interfaces don't have saved names431// -- no Code attribute for the LocalVariableTable attribute432if ((v.owner.owner.flags() & Flags.INTERFACE) != 0)433return "arg" + (i - 1);434// abstract methods don't have saved names435// -- no Code attribute for the LocalVariableTable attribute436if ((v.owner.flags() & Flags.ABSTRACT) != 0)437return "arg" + (i - 1);438// bridge methods use argN. No LVT for them anymore439if ((v.owner.flags() & Flags.BRIDGE) != 0)440return "arg" + (i - 1);441442// The rest of this method assumes the local conventions in the test program443Type t = v.type;444String s;445if (t.hasTag(TypeTag.CLASS))446s = ((ClassType) t).tsym.name.toString();447else448s = t.toString();449return String.valueOf(Character.toLowerCase(s.charAt(0))) + i;450}451452void compile(File outDir, String... opts) throws Exception {453//File testSrc = new File(System.getProperty("test.src"), ".");454List<String> args = new ArrayList<String>();455args.add("-d");456args.add(outDir.getPath());457args.addAll(Arrays.asList(opts));458//args.add(new File(testSrc, "Test.java").getPath());459args.add("Test.java");460StringWriter sw = new StringWriter();461PrintWriter pw = new PrintWriter(sw);462int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);463pw.close();464if (rc != 0) {465System.err.println(sw.toString());466throw new Exception("compilation failed unexpectedly");467}468}469470Set<String> getTopLevelClasses(File outDir) {471Set<String> classes = new HashSet<String>();472for (String f: outDir.list()) {473if (f.endsWith(".class") && !f.contains("$"))474classes.add(f.replace(".class", ""));475}476return classes;477}478479void checkEqual(String expect, String found) {480if (!expect.equals(found))481error("mismatch: expected:" + expect + " found:" + found);482}483484void error(String msg) {485System.err.println(msg);486errors++;487throw new Error();488}489490int errors;491}492493494