Mon, 19 Oct 2009 13:38:09 -0700
6889255: javac MethodSymbol throws NPE if ClassReader does not read parameter names correctly
Reviewed-by: darcy
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Oct 16 12:56:50 2009 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Mon Oct 19 13:38:09 2009 -0700 1.3 @@ -1212,25 +1212,58 @@ 1.4 public List<VarSymbol> params() { 1.5 owner.complete(); 1.6 if (params == null) { 1.7 - List<Name> names = savedParameterNames; 1.8 + // If ClassReader.saveParameterNames has been set true, then 1.9 + // savedParameterNames will be set to a list of names that 1.10 + // matches the types in type.getParameterTypes(). If any names 1.11 + // were not found in the class file, those names in the list will 1.12 + // be set to the empty name. 1.13 + // If ClassReader.saveParameterNames has been set false, then 1.14 + // savedParameterNames will be null. 1.15 + List<Name> paramNames = savedParameterNames; 1.16 savedParameterNames = null; 1.17 - if (names == null) { 1.18 - names = List.nil(); 1.19 - int i = 0; 1.20 - for (Type t : type.getParameterTypes()) 1.21 - names = names.prepend(name.table.fromString("arg" + i++)); 1.22 - names = names.reverse(); 1.23 - } 1.24 + // discard the provided names if the list of names is the wrong size. 1.25 + if (paramNames == null || paramNames.size() != type.getParameterTypes().size()) 1.26 + paramNames = List.nil(); 1.27 ListBuffer<VarSymbol> buf = new ListBuffer<VarSymbol>(); 1.28 + List<Name> remaining = paramNames; 1.29 + // assert: remaining and paramNames are both empty or both 1.30 + // have same cardinality as type.getParameterTypes() 1.31 + int i = 0; 1.32 for (Type t : type.getParameterTypes()) { 1.33 - buf.append(new VarSymbol(PARAMETER, names.head, t, this)); 1.34 - names = names.tail; 1.35 + Name paramName; 1.36 + if (remaining.isEmpty()) { 1.37 + // no names for any parameters available 1.38 + paramName = createArgName(i, paramNames); 1.39 + } else { 1.40 + paramName = remaining.head; 1.41 + remaining = remaining.tail; 1.42 + if (paramName.isEmpty()) { 1.43 + // no name for this specific parameter 1.44 + paramName = createArgName(i, paramNames); 1.45 + } 1.46 + } 1.47 + buf.append(new VarSymbol(PARAMETER, paramName, t, this)); 1.48 + i++; 1.49 } 1.50 params = buf.toList(); 1.51 } 1.52 return params; 1.53 } 1.54 1.55 + // Create a name for the argument at position 'index' that is not in 1.56 + // the exclude list. In normal use, either no names will have been 1.57 + // provided, in which case the exclude list is empty, or all the names 1.58 + // will have been provided, in which case this method will not be called. 1.59 + private Name createArgName(int index, List<Name> exclude) { 1.60 + String prefix = "arg"; 1.61 + while (true) { 1.62 + Name argName = name.table.fromString(prefix + index); 1.63 + if (!exclude.contains(argName)) 1.64 + return argName; 1.65 + prefix += "$"; 1.66 + } 1.67 + } 1.68 + 1.69 public Symbol asMemberOf(Type site, Types types) { 1.70 return new MethodSymbol(flags_field, name, types.memberType(site, this), owner); 1.71 }
2.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Fri Oct 16 12:56:50 2009 -0700 2.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Mon Oct 19 13:38:09 2009 -0700 2.3 @@ -29,6 +29,7 @@ 2.4 import java.net.URI; 2.5 import java.net.URISyntaxException; 2.6 import java.nio.CharBuffer; 2.7 +import java.util.Arrays; 2.8 import java.util.EnumSet; 2.9 import java.util.HashMap; 2.10 import java.util.Map; 2.11 @@ -191,6 +192,16 @@ 2.12 */ 2.13 boolean debugJSR308; 2.14 2.15 + /** A table to hold the constant pool indices for method parameter 2.16 + * names, as given in LocalVariableTable attributes. 2.17 + */ 2.18 + int[] parameterNameIndices; 2.19 + 2.20 + /** 2.21 + * Whether or not any parameter names have been found. 2.22 + */ 2.23 + boolean haveParameterNameIndices; 2.24 + 2.25 /** Get the ClassReader instance for this invocation. */ 2.26 public static ClassReader instance(Context context) { 2.27 ClassReader instance = context.get(classReaderKey); 2.28 @@ -922,32 +933,33 @@ 2.29 void read(Symbol sym, int attrLen) { 2.30 int newbp = bp + attrLen; 2.31 if (saveParameterNames) { 2.32 - // pick up parameter names from the variable table 2.33 - List<Name> parameterNames = List.nil(); 2.34 - int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0; 2.35 - int endParam = firstParam + Code.width(sym.type.getParameterTypes()); 2.36 + // Pick up parameter names from the variable table. 2.37 + // Parameter names are not explicitly identified as such, 2.38 + // but all parameter name entries in the LocalVariableTable 2.39 + // have a start_pc of 0. Therefore, we record the name 2.40 + // indicies of all slots with a start_pc of zero in the 2.41 + // parameterNameIndicies array. 2.42 + // Note that this implicitly honors the JVMS spec that 2.43 + // there may be more than one LocalVariableTable, and that 2.44 + // there is no specified ordering for the entries. 2.45 int numEntries = nextChar(); 2.46 - for (int i=0; i<numEntries; i++) { 2.47 + for (int i = 0; i < numEntries; i++) { 2.48 int start_pc = nextChar(); 2.49 int length = nextChar(); 2.50 int nameIndex = nextChar(); 2.51 int sigIndex = nextChar(); 2.52 int register = nextChar(); 2.53 - if (start_pc == 0 && 2.54 - firstParam <= register && 2.55 - register < endParam) { 2.56 - int index = firstParam; 2.57 - for (Type t : sym.type.getParameterTypes()) { 2.58 - if (index == register) { 2.59 - parameterNames = parameterNames.prepend(readName(nameIndex)); 2.60 - break; 2.61 - } 2.62 - index += Code.width(t); 2.63 + if (start_pc == 0) { 2.64 + // ensure array large enough 2.65 + if (register >= parameterNameIndices.length) { 2.66 + int newSize = Math.max(register, parameterNameIndices.length + 8); 2.67 + parameterNameIndices = 2.68 + Arrays.copyOf(parameterNameIndices, newSize); 2.69 } 2.70 + parameterNameIndices[register] = nameIndex; 2.71 + haveParameterNameIndices = true; 2.72 } 2.73 } 2.74 - parameterNames = parameterNames.reverse(); 2.75 - ((MethodSymbol)sym).savedParameterNames = parameterNames; 2.76 } 2.77 bp = newbp; 2.78 } 2.79 @@ -1839,6 +1851,8 @@ 2.80 syms.methodClass); 2.81 } 2.82 MethodSymbol m = new MethodSymbol(flags, name, type, currentOwner); 2.83 + if (saveParameterNames) 2.84 + initParameterNames(m); 2.85 Symbol prevOwner = currentOwner; 2.86 currentOwner = m; 2.87 try { 2.88 @@ -1846,9 +1860,90 @@ 2.89 } finally { 2.90 currentOwner = prevOwner; 2.91 } 2.92 + if (saveParameterNames) 2.93 + setParameterNames(m, type); 2.94 return m; 2.95 } 2.96 2.97 + /** 2.98 + * Init the parameter names array. 2.99 + * Parameter names are currently inferred from the names in the 2.100 + * LocalVariableTable attributes of a Code attribute. 2.101 + * (Note: this means parameter names are currently not available for 2.102 + * methods without a Code attribute.) 2.103 + * This method initializes an array in which to store the name indexes 2.104 + * of parameter names found in LocalVariableTable attributes. It is 2.105 + * slightly supersized to allow for additional slots with a start_pc of 0. 2.106 + */ 2.107 + void initParameterNames(MethodSymbol sym) { 2.108 + // make allowance for synthetic parameters. 2.109 + final int excessSlots = 4; 2.110 + int expectedParameterSlots = 2.111 + Code.width(sym.type.getParameterTypes()) + excessSlots; 2.112 + if (parameterNameIndices == null 2.113 + || parameterNameIndices.length < expectedParameterSlots) { 2.114 + parameterNameIndices = new int[expectedParameterSlots]; 2.115 + } else 2.116 + Arrays.fill(parameterNameIndices, 0); 2.117 + haveParameterNameIndices = false; 2.118 + } 2.119 + 2.120 + /** 2.121 + * Set the parameter names for a symbol from the name index in the 2.122 + * parameterNameIndicies array. The type of the symbol may have changed 2.123 + * while reading the method attributes (see the Signature attribute). 2.124 + * This may be because of generic information or because anonymous 2.125 + * synthetic parameters were added. The original type (as read from 2.126 + * the method descriptor) is used to help guess the existence of 2.127 + * anonymous synthetic parameters. 2.128 + * On completion, sym.savedParameter names will either be null (if 2.129 + * no parameter names were found in the class file) or will be set to a 2.130 + * list of names, one per entry in sym.type.getParameterTypes, with 2.131 + * any missing names represented by the empty name. 2.132 + */ 2.133 + void setParameterNames(MethodSymbol sym, Type jvmType) { 2.134 + // if no names were found in the class file, there's nothing more to do 2.135 + if (!haveParameterNameIndices) 2.136 + return; 2.137 + 2.138 + int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0; 2.139 + // the code in readMethod may have skipped the first parameter when 2.140 + // setting up the MethodType. If so, we make a corresponding allowance 2.141 + // here for the position of the first parameter. Note that this 2.142 + // assumes the skipped parameter has a width of 1 -- i.e. it is not 2.143 + // a double width type (long or double.) 2.144 + if (sym.name == names.init && currentOwner.hasOuterInstance()) { 2.145 + // Sometimes anonymous classes don't have an outer 2.146 + // instance, however, there is no reliable way to tell so 2.147 + // we never strip this$n 2.148 + if (!currentOwner.name.isEmpty()) 2.149 + firstParam += 1; 2.150 + } 2.151 + 2.152 + if (sym.type != jvmType) { 2.153 + // reading the method attributes has caused the symbol's type to 2.154 + // be changed. (i.e. the Signature attribute.) This may happen if 2.155 + // there are hidden (synthetic) parameters in the descriptor, but 2.156 + // not in the Signature. The position of these hidden parameters 2.157 + // is unspecified; for now, assume they are at the beginning, and 2.158 + // so skip over them. The primary case for this is two hidden 2.159 + // parameters passed into Enum constructors. 2.160 + int skip = Code.width(jvmType.getParameterTypes()) 2.161 + - Code.width(sym.type.getParameterTypes()); 2.162 + firstParam += skip; 2.163 + } 2.164 + List<Name> paramNames = List.nil(); 2.165 + int index = firstParam; 2.166 + for (Type t: sym.type.getParameterTypes()) { 2.167 + int nameIdx = (index < parameterNameIndices.length 2.168 + ? parameterNameIndices[index] : 0); 2.169 + Name name = nameIdx == 0 ? names.empty : readName(nameIdx); 2.170 + paramNames = paramNames.prepend(name); 2.171 + index += Code.width(t); 2.172 + } 2.173 + sym.savedParameterNames = paramNames.reverse(); 2.174 + } 2.175 + 2.176 /** Skip a field or method 2.177 */ 2.178 void skipMember() {
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/tools/javac/6889255/T6889255.java Mon Oct 19 13:38:09 2009 -0700 3.3 @@ -0,0 +1,485 @@ 3.4 +/* 3.5 + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. 3.11 + * 3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.15 + * version 2 for more details (a copy is included in the LICENSE file that 3.16 + * accompanied this code). 3.17 + * 3.18 + * You should have received a copy of the GNU General Public License version 3.19 + * 2 along with this work; if not, write to the Free Software Foundation, 3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.21 + * 3.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 3.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 3.24 + * have any questions. 3.25 + */ 3.26 + 3.27 +/* 3.28 + * @test 3.29 + * @bug 6889255 3.30 + * @summary ClassReader does not read parameter names correctly 3.31 + */ 3.32 + 3.33 +import java.io.*; 3.34 +import java.util.*; 3.35 +import javax.tools.StandardLocation; 3.36 +import com.sun.tools.javac.code.Flags; 3.37 +import com.sun.tools.javac.code.Kinds; 3.38 +import com.sun.tools.javac.code.Scope; 3.39 +import com.sun.tools.javac.code.Symbol.*; 3.40 +import com.sun.tools.javac.code.Type; 3.41 +import com.sun.tools.javac.code.Type.ClassType; 3.42 +import com.sun.tools.javac.code.TypeTags; 3.43 +import com.sun.tools.javac.file.JavacFileManager; 3.44 +import com.sun.tools.javac.jvm.ClassReader; 3.45 +import com.sun.tools.javac.util.Context; 3.46 +import com.sun.tools.javac.util.Names; 3.47 + 3.48 +public class T6889255 { 3.49 + boolean testInterfaces = true; 3.50 + boolean testSyntheticMethods = true; 3.51 + 3.52 + // The following enums control the generation of the test methods to be compiled. 3.53 + enum GenericKind { 3.54 + NOT_GENERIC, 3.55 + GENERIC 3.56 + }; 3.57 + 3.58 + enum ClassKind { 3.59 + CLASS("Clss"), 3.60 + INTERFACE("Intf"), 3.61 + ENUM("Enum"); 3.62 + final String base; 3.63 + ClassKind(String base) { this.base = base; } 3.64 + }; 3.65 + 3.66 + enum NestedKind { 3.67 + /** Declare methods inside the outermost container. */ 3.68 + NONE, 3.69 + /** Declare methods inside a container with a 'static' modifier. */ 3.70 + NESTED, 3.71 + /** Declare methods inside a container without a 'static' modifier. */ 3.72 + INNER, 3.73 + /** Declare methods inside a local class in an initializer. */ 3.74 + INIT_LOCAL, 3.75 + /** Declare methods inside an anonymous class in an initializer. */ 3.76 + INIT_ANON, 3.77 + /** Declare methods inside a local class in a method. */ 3.78 + METHOD_LOCAL, 3.79 + /** Declare methods inside an anonymous class in a method. */ 3.80 + METHOD_ANON 3.81 + }; 3.82 + 3.83 + enum MethodKind { 3.84 + ABSTRACT, 3.85 + CONSTRUCTOR, 3.86 + METHOD, 3.87 + STATIC_METHOD, 3.88 + BRIDGE_METHOD 3.89 + }; 3.90 + 3.91 + enum FinalKind { 3.92 + /** Method body does not reference external final variables. */ 3.93 + NO_FINAL, 3.94 + /** Method body references external final variables. */ 3.95 + USE_FINAL 3.96 + }; 3.97 + 3.98 + public static void main(String... args) throws Exception { 3.99 + new T6889255().run(); 3.100 + } 3.101 + 3.102 + void run() throws Exception { 3.103 + genTest(); 3.104 + 3.105 + test("no-args", false); 3.106 + test("g", true, "-g"); 3.107 + 3.108 + if (errors > 0) 3.109 + throw new Exception(errors + " errors found"); 3.110 + } 3.111 + 3.112 + /** 3.113 + * Create a file containing lots of method definitions to be tested. 3.114 + * There are 3 sets of nested loops that generate the methods. 3.115 + * 1. The outermost set declares [generic] (class | interface | enum) 3.116 + * 2. The middle set declares [(nested | inner | anon | local)] class 3.117 + * 3. The innermost set declares 3.118 + * [generic] (constructor|method|static-method|bridge-method) [using final variables in outer scope] 3.119 + * Invalid combinations are filtered out. 3.120 + */ 3.121 + void genTest() throws Exception { 3.122 + BufferedWriter out = new BufferedWriter(new FileWriter("Test.java")); 3.123 + 3.124 + // This interface is used to force bridge methods to be generated, by 3.125 + // implementing its methods with subtypes of Object 3.126 + out.write("interface Base {\n"); 3.127 + out.write(" Object base_m1(int i1);\n"); 3.128 + out.write(" Object base_m2(int i1);\n"); 3.129 + out.write("}\n"); 3.130 + 3.131 + int outerNum = 0; 3.132 + // Outermost set of loops, to generate a top level container 3.133 + for (GenericKind outerGenericKind: GenericKind.values()) { 3.134 + for (ClassKind outerClassKind: ClassKind.values()) { 3.135 + if (outerGenericKind == GenericKind.GENERIC && outerClassKind == ClassKind.ENUM) 3.136 + continue; 3.137 + String outerClassName = outerClassKind.base + (outerNum++); 3.138 + String outerTypeArg = outerClassKind.toString().charAt(0) + "T"; 3.139 + if (outerClassKind == ClassKind.CLASS) 3.140 + out.write("abstract "); 3.141 + out.write(outerClassKind.toString().toLowerCase() + " " + outerClassName); 3.142 + if (outerGenericKind == GenericKind.GENERIC) 3.143 + out.write("<" + outerTypeArg + ">"); 3.144 + if (outerClassKind == ClassKind.INTERFACE) 3.145 + out.write(" extends Base"); 3.146 + else 3.147 + out.write(" implements Base"); 3.148 + out.write(" {\n"); 3.149 + if (outerClassKind == ClassKind.ENUM) { 3.150 + out.write(" E1(0,0,0), E2(0,0,0), E3(0,0,0);\n"); 3.151 + out.write(" " + outerClassName + "(int i1, int i2, int i3) { }\n"); 3.152 + } 3.153 + // Middle set of loops, to generate an optional nested container 3.154 + int nestedNum = 0; 3.155 + int methodNum = 0; 3.156 + for (GenericKind nestedGenericKind: GenericKind.values()) { 3.157 + nextNestedKind: 3.158 + for (NestedKind nestedKind: NestedKind.values()) { 3.159 + // if the nested kind is none, there is no point iterating over all 3.160 + // nested generic kinds, so arbitarily limit it to just one kind 3.161 + if (nestedKind == NestedKind.NONE && nestedGenericKind != GenericKind.NOT_GENERIC) 3.162 + continue; 3.163 + if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON) 3.164 + && nestedGenericKind == GenericKind.GENERIC) 3.165 + continue; 3.166 + String indent = " "; 3.167 + boolean haveFinal = false; 3.168 + switch (nestedKind) { 3.169 + case METHOD_ANON: case METHOD_LOCAL: 3.170 + if (outerClassKind == ClassKind.INTERFACE) 3.171 + continue nextNestedKind; 3.172 + out.write(indent + "void m" + + (nestedNum++) + "() {\n"); 3.173 + indent += " "; 3.174 + out.write(indent + "final int fi1 = 0;\n"); 3.175 + haveFinal = true; 3.176 + break; 3.177 + case INIT_ANON: case INIT_LOCAL: 3.178 + if (outerClassKind == ClassKind.INTERFACE) 3.179 + continue nextNestedKind; 3.180 + out.write(indent + "{\n"); 3.181 + indent += " "; 3.182 + break; 3.183 + } 3.184 + for (ClassKind nestedClassKind: ClassKind.values()) { 3.185 + if ((nestedGenericKind == GenericKind.GENERIC) 3.186 + && (nestedClassKind == ClassKind.ENUM)) 3.187 + continue; 3.188 + if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.METHOD_LOCAL 3.189 + || nestedKind == NestedKind.INIT_ANON || nestedKind == NestedKind.INIT_LOCAL) 3.190 + && nestedClassKind != ClassKind.CLASS) 3.191 + continue; 3.192 + // if the nested kind is none, there is no point iterating over all 3.193 + // nested class kinds, so arbitarily limit it to just one kind 3.194 + if (nestedKind == NestedKind.NONE && nestedClassKind != ClassKind.CLASS) 3.195 + continue; 3.196 + 3.197 + ClassKind methodClassKind; 3.198 + String methodClassName; 3.199 + boolean allowAbstractMethods; 3.200 + boolean allowStaticMethods; 3.201 + switch (nestedKind) { 3.202 + case NONE: 3.203 + methodClassKind = outerClassKind; 3.204 + methodClassName = outerClassName; 3.205 + allowAbstractMethods = (outerClassKind == ClassKind.CLASS); 3.206 + allowStaticMethods = (outerClassKind != ClassKind.INTERFACE); 3.207 + break; 3.208 + case METHOD_ANON: 3.209 + case INIT_ANON: 3.210 + out.write(indent + "new Base() {\n"); 3.211 + indent += " "; 3.212 + methodClassKind = ClassKind.CLASS; 3.213 + methodClassName = null; 3.214 + allowAbstractMethods = false; 3.215 + allowStaticMethods = false; 3.216 + break; 3.217 + default: { // INNER, NESTED, LOCAL 3.218 + String nestedClassName = "N" + nestedClassKind.base + (nestedNum++); 3.219 + String nestedTypeArg = nestedClassKind.toString().charAt(0) + "T"; 3.220 + out.write(indent); 3.221 + if (nestedKind == NestedKind.NESTED) 3.222 + out.write("static "); 3.223 + if (nestedClassKind == ClassKind.CLASS) 3.224 + out.write("abstract "); 3.225 + out.write(nestedClassKind.toString().toLowerCase() + " " + nestedClassName); 3.226 + if (nestedGenericKind == GenericKind.GENERIC) 3.227 + out.write("<" + nestedTypeArg + ">"); 3.228 + if (nestedClassKind == ClassKind.INTERFACE) 3.229 + out.write(" extends Base "); 3.230 + else 3.231 + out.write(" implements Base "); 3.232 + out.write(" {\n"); 3.233 + indent += " "; 3.234 + if (nestedClassKind == ClassKind.ENUM) { 3.235 + out.write(indent + "E1(0,0,0), E2(0,0,0), E3(0,0,0);\n"); 3.236 + out.write(indent + nestedClassName + "(int i1, int i2, int i3) { }\n"); 3.237 + } 3.238 + methodClassKind = nestedClassKind; 3.239 + methodClassName = nestedClassName; 3.240 + allowAbstractMethods = (nestedClassKind == ClassKind.CLASS); 3.241 + allowStaticMethods = (nestedKind == NestedKind.NESTED && nestedClassKind != ClassKind.INTERFACE); 3.242 + break; 3.243 + } 3.244 + } 3.245 + 3.246 + // Innermost loops, to generate methods 3.247 + for (GenericKind methodGenericKind: GenericKind.values()) { 3.248 + for (FinalKind finalKind: FinalKind.values()) { 3.249 + for (MethodKind methodKind: MethodKind.values()) { 3.250 +// out.write("// " + outerGenericKind 3.251 +// + " " + outerClassKind 3.252 +// + " " + nestedKind 3.253 +// + " " + nestedGenericKind 3.254 +// + " " + nestedClassKind 3.255 +// + " " + methodGenericKind 3.256 +// + " " + finalKind 3.257 +// + " " + methodKind 3.258 +// + "\n"); 3.259 + switch (methodKind) { 3.260 + case CONSTRUCTOR: 3.261 + if (nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON) 3.262 + break; 3.263 + if (methodClassKind != ClassKind.CLASS) 3.264 + break; 3.265 + if (finalKind == FinalKind.USE_FINAL && !haveFinal) 3.266 + break; 3.267 + out.write(indent); 3.268 + if (methodGenericKind == GenericKind.GENERIC) { 3.269 + out.write("<CT> " + methodClassName + "(CT c1, CT c2"); 3.270 + } else { 3.271 + out.write(methodClassName + "(boolean b1, char c2"); 3.272 + } 3.273 + if (finalKind == FinalKind.USE_FINAL) { 3.274 + // add a dummy parameter to avoid duplicate declaration 3.275 + out.write(", int i3) { int i = fi1; }\n"); 3.276 + } else 3.277 + out.write(") { }\n"); 3.278 + break; 3.279 + case ABSTRACT: 3.280 + if (!allowAbstractMethods) 3.281 + continue; 3.282 + // fallthrough 3.283 + case METHOD: 3.284 + if (finalKind == FinalKind.USE_FINAL && !haveFinal) 3.285 + break; 3.286 + out.write(indent); 3.287 + if (methodKind == MethodKind.ABSTRACT) 3.288 + out.write("abstract "); 3.289 + if (methodGenericKind == GenericKind.GENERIC) 3.290 + out.write("<MT> "); 3.291 + out.write("void m" + (methodNum++) + "(int i1, long l2, float f3)"); 3.292 + if (methodKind == MethodKind.ABSTRACT || methodClassKind == ClassKind.INTERFACE) 3.293 + out.write(";\n"); 3.294 + else { 3.295 + out.write(" {"); 3.296 + if (finalKind == FinalKind.USE_FINAL) 3.297 + out.write(" int i = fi1;"); 3.298 + out.write(" }\n"); 3.299 + } 3.300 + break; 3.301 + case BRIDGE_METHOD: 3.302 + if (methodGenericKind == GenericKind.GENERIC) 3.303 + break; 3.304 + out.write(indent); 3.305 + // methods Base.base_m1 and Base.base_m2 are declared for the 3.306 + // benefit of bridge methods. They need to be implemented 3.307 + // whether or not a final variable is used. 3.308 + String methodName = (finalKind == FinalKind.NO_FINAL ? "base_m1" : "base_m2"); 3.309 + out.write("public String " + methodName + "(int i1)"); 3.310 + if (methodClassKind == ClassKind.INTERFACE) 3.311 + out.write(";\n"); 3.312 + else { 3.313 + out.write(" {"); 3.314 + if (finalKind == FinalKind.USE_FINAL && haveFinal) 3.315 + out.write(" int i = fi1;"); 3.316 + out.write(" return null; }\n"); 3.317 + } 3.318 + break; 3.319 + case STATIC_METHOD: 3.320 + if (!allowStaticMethods) 3.321 + break; 3.322 + if (finalKind == FinalKind.USE_FINAL && !haveFinal) 3.323 + break; 3.324 + out.write(indent + "static "); 3.325 + if (methodGenericKind == GenericKind.GENERIC) 3.326 + out.write("<MT> "); 3.327 + out.write("void m" + (methodNum++) + "(int i1, long l2, float f3) {"); 3.328 + if (finalKind == FinalKind.USE_FINAL) 3.329 + out.write(" int i = fi1;"); 3.330 + out.write(" }\n"); 3.331 + break; 3.332 + } 3.333 + 3.334 + } 3.335 + } 3.336 + } 3.337 + if (nestedKind != NestedKind.NONE) { 3.338 + indent = indent.substring(0, indent.length() - 4); 3.339 + out.write(indent + "};\n"); 3.340 + } 3.341 + } 3.342 + switch (nestedKind) { 3.343 + case METHOD_ANON: case METHOD_LOCAL: 3.344 + case INIT_ANON: case INIT_LOCAL: 3.345 + indent = indent.substring(0, indent.length() - 4); 3.346 + out.write(indent + "}\n\n"); 3.347 + } 3.348 + } 3.349 + } 3.350 + out.write("}\n\n"); 3.351 + } 3.352 + } 3.353 + out.close(); 3.354 + } 3.355 + 3.356 + 3.357 + void test(String testName, boolean expectNames, String... opts) throws Exception { 3.358 + System.err.println("Test " + testName 3.359 + + ": expectNames:" + expectNames 3.360 + + " javacOpts:" + Arrays.asList(opts)); 3.361 + 3.362 + File outDir = new File(testName); 3.363 + outDir.mkdirs(); 3.364 + compile(outDir, opts); 3.365 + 3.366 + Context ctx = new Context(); 3.367 + JavacFileManager fm = new JavacFileManager(ctx, true, null); 3.368 + fm.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(outDir)); 3.369 + ClassReader cr = ClassReader.instance(ctx); 3.370 + cr.saveParameterNames = true; 3.371 + Names names = Names.instance(ctx); 3.372 + 3.373 + Set<String> classes = getTopLevelClasses(outDir); 3.374 + Deque<String> work = new LinkedList<String>(classes); 3.375 + String classname; 3.376 + while ((classname = work.poll()) != null) { 3.377 + System.err.println("Checking class " + classname); 3.378 + ClassSymbol sym = cr.enterClass(names.table.fromString(classname)); 3.379 + sym.complete(); 3.380 + 3.381 + if ((sym.flags() & Flags.INTERFACE) != 0 && !testInterfaces) 3.382 + continue; 3.383 + 3.384 + for (Scope.Entry e = sym.members_field.elems; e != null; e = e.sibling) { 3.385 + System.err.println("Checking member " + e.sym); 3.386 + switch (e.sym.kind) { 3.387 + case Kinds.TYP: { 3.388 + String name = e.sym.flatName().toString(); 3.389 + if (!classes.contains(name)) { 3.390 + classes.add(name); 3.391 + work.add(name); 3.392 + } 3.393 + break; 3.394 + } 3.395 + case Kinds.MTH: 3.396 + verify((MethodSymbol) e.sym, expectNames); 3.397 + break; 3.398 + } 3.399 + 3.400 + } 3.401 + } 3.402 + } 3.403 + 3.404 + void verify(MethodSymbol m, boolean expectNames) { 3.405 + if ((m.flags() & Flags.SYNTHETIC) != 0 && !testSyntheticMethods) 3.406 + return; 3.407 + 3.408 + //System.err.println("verify: " + m.params()); 3.409 + int i = 1; 3.410 + for (VarSymbol v: m.params()) { 3.411 + String expectName; 3.412 + if (expectNames) 3.413 + expectName = getExpectedName(v, i); 3.414 + else 3.415 + expectName = "arg" + (i - 1); 3.416 + checkEqual(expectName, v.name.toString()); 3.417 + i++; 3.418 + } 3.419 + } 3.420 + 3.421 + String getExpectedName(VarSymbol v, int i) { 3.422 + // special cases: 3.423 + // synthetic method 3.424 + if (((v.owner.owner.flags() & Flags.ENUM) != 0) 3.425 + && v.owner.name.toString().equals("valueOf")) 3.426 + return "name"; 3.427 + // interfaces don't have saved names 3.428 + // -- no Code attribute for the LocalVariableTable attribute 3.429 + if ((v.owner.owner.flags() & Flags.INTERFACE) != 0) 3.430 + return "arg" + (i - 1); 3.431 + // abstract methods don't have saved names 3.432 + // -- no Code attribute for the LocalVariableTable attribute 3.433 + if ((v.owner.flags() & Flags.ABSTRACT) != 0) 3.434 + return "arg" + (i - 1); 3.435 + // bridge methods use xN 3.436 + if ((v.owner.flags() & Flags.BRIDGE) != 0) 3.437 + return "x" + (i - 1); 3.438 + 3.439 + // The rest of this method assumes the local conventions in the test program 3.440 + Type t = v.type; 3.441 + String s; 3.442 + if (t.tag == TypeTags.CLASS) 3.443 + s = ((ClassType) t).tsym.name.toString(); 3.444 + else 3.445 + s = t.toString(); 3.446 + return String.valueOf(Character.toLowerCase(s.charAt(0))) + i; 3.447 + } 3.448 + 3.449 + void compile(File outDir, String... opts) throws Exception { 3.450 + //File testSrc = new File(System.getProperty("test.src"), "."); 3.451 + List<String> args = new ArrayList<String>(); 3.452 + args.add("-d"); 3.453 + args.add(outDir.getPath()); 3.454 + args.addAll(Arrays.asList(opts)); 3.455 + //args.add(new File(testSrc, "Test.java").getPath()); 3.456 + args.add("Test.java"); 3.457 + StringWriter sw = new StringWriter(); 3.458 + PrintWriter pw = new PrintWriter(sw); 3.459 + int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw); 3.460 + pw.close(); 3.461 + if (rc != 0) { 3.462 + System.err.println(sw.toString()); 3.463 + throw new Exception("compilation failed unexpectedly"); 3.464 + } 3.465 + } 3.466 + 3.467 + Set<String> getTopLevelClasses(File outDir) { 3.468 + Set<String> classes = new HashSet<String>(); 3.469 + for (String f: outDir.list()) { 3.470 + if (f.endsWith(".class") && !f.contains("$")) 3.471 + classes.add(f.replace(".class", "")); 3.472 + } 3.473 + return classes; 3.474 + } 3.475 + 3.476 + void checkEqual(String expect, String found) { 3.477 + if (!expect.equals(found)) 3.478 + error("mismatch: expected:" + expect + " found:" + found); 3.479 + } 3.480 + 3.481 + void error(String msg) { 3.482 + System.err.println(msg); 3.483 + errors++; 3.484 + throw new Error(); 3.485 + } 3.486 + 3.487 + int errors; 3.488 +}