mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 19:14:38 +02:00
6889255: javac MethodSymbol throws NPE if ClassReader does not read parameter names correctly
Reviewed-by: darcy
This commit is contained in:
parent
e634c0ee8e
commit
613eae59d0
3 changed files with 640 additions and 27 deletions
|
@ -1212,25 +1212,58 @@ public abstract class Symbol implements Element {
|
||||||
public List<VarSymbol> params() {
|
public List<VarSymbol> params() {
|
||||||
owner.complete();
|
owner.complete();
|
||||||
if (params == null) {
|
if (params == null) {
|
||||||
List<Name> names = savedParameterNames;
|
// If ClassReader.saveParameterNames has been set true, then
|
||||||
|
// savedParameterNames will be set to a list of names that
|
||||||
|
// matches the types in type.getParameterTypes(). If any names
|
||||||
|
// were not found in the class file, those names in the list will
|
||||||
|
// be set to the empty name.
|
||||||
|
// If ClassReader.saveParameterNames has been set false, then
|
||||||
|
// savedParameterNames will be null.
|
||||||
|
List<Name> paramNames = savedParameterNames;
|
||||||
savedParameterNames = null;
|
savedParameterNames = null;
|
||||||
if (names == null) {
|
// discard the provided names if the list of names is the wrong size.
|
||||||
names = List.nil();
|
if (paramNames == null || paramNames.size() != type.getParameterTypes().size())
|
||||||
int i = 0;
|
paramNames = List.nil();
|
||||||
for (Type t : type.getParameterTypes())
|
|
||||||
names = names.prepend(name.table.fromString("arg" + i++));
|
|
||||||
names = names.reverse();
|
|
||||||
}
|
|
||||||
ListBuffer<VarSymbol> buf = new ListBuffer<VarSymbol>();
|
ListBuffer<VarSymbol> buf = new ListBuffer<VarSymbol>();
|
||||||
|
List<Name> remaining = paramNames;
|
||||||
|
// assert: remaining and paramNames are both empty or both
|
||||||
|
// have same cardinality as type.getParameterTypes()
|
||||||
|
int i = 0;
|
||||||
for (Type t : type.getParameterTypes()) {
|
for (Type t : type.getParameterTypes()) {
|
||||||
buf.append(new VarSymbol(PARAMETER, names.head, t, this));
|
Name paramName;
|
||||||
names = names.tail;
|
if (remaining.isEmpty()) {
|
||||||
|
// no names for any parameters available
|
||||||
|
paramName = createArgName(i, paramNames);
|
||||||
|
} else {
|
||||||
|
paramName = remaining.head;
|
||||||
|
remaining = remaining.tail;
|
||||||
|
if (paramName.isEmpty()) {
|
||||||
|
// no name for this specific parameter
|
||||||
|
paramName = createArgName(i, paramNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.append(new VarSymbol(PARAMETER, paramName, t, this));
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
params = buf.toList();
|
params = buf.toList();
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a name for the argument at position 'index' that is not in
|
||||||
|
// the exclude list. In normal use, either no names will have been
|
||||||
|
// provided, in which case the exclude list is empty, or all the names
|
||||||
|
// will have been provided, in which case this method will not be called.
|
||||||
|
private Name createArgName(int index, List<Name> exclude) {
|
||||||
|
String prefix = "arg";
|
||||||
|
while (true) {
|
||||||
|
Name argName = name.table.fromString(prefix + index);
|
||||||
|
if (!exclude.contains(argName))
|
||||||
|
return argName;
|
||||||
|
prefix += "$";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Symbol asMemberOf(Type site, Types types) {
|
public Symbol asMemberOf(Type site, Types types) {
|
||||||
return new MethodSymbol(flags_field, name, types.memberType(site, this), owner);
|
return new MethodSymbol(flags_field, name, types.memberType(site, this), owner);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.io.*;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -191,6 +192,16 @@ public class ClassReader implements Completer {
|
||||||
*/
|
*/
|
||||||
boolean debugJSR308;
|
boolean debugJSR308;
|
||||||
|
|
||||||
|
/** A table to hold the constant pool indices for method parameter
|
||||||
|
* names, as given in LocalVariableTable attributes.
|
||||||
|
*/
|
||||||
|
int[] parameterNameIndices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not any parameter names have been found.
|
||||||
|
*/
|
||||||
|
boolean haveParameterNameIndices;
|
||||||
|
|
||||||
/** Get the ClassReader instance for this invocation. */
|
/** Get the ClassReader instance for this invocation. */
|
||||||
public static ClassReader instance(Context context) {
|
public static ClassReader instance(Context context) {
|
||||||
ClassReader instance = context.get(classReaderKey);
|
ClassReader instance = context.get(classReaderKey);
|
||||||
|
@ -922,32 +933,33 @@ public class ClassReader implements Completer {
|
||||||
void read(Symbol sym, int attrLen) {
|
void read(Symbol sym, int attrLen) {
|
||||||
int newbp = bp + attrLen;
|
int newbp = bp + attrLen;
|
||||||
if (saveParameterNames) {
|
if (saveParameterNames) {
|
||||||
// pick up parameter names from the variable table
|
// Pick up parameter names from the variable table.
|
||||||
List<Name> parameterNames = List.nil();
|
// Parameter names are not explicitly identified as such,
|
||||||
int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0;
|
// but all parameter name entries in the LocalVariableTable
|
||||||
int endParam = firstParam + Code.width(sym.type.getParameterTypes());
|
// have a start_pc of 0. Therefore, we record the name
|
||||||
|
// indicies of all slots with a start_pc of zero in the
|
||||||
|
// parameterNameIndicies array.
|
||||||
|
// Note that this implicitly honors the JVMS spec that
|
||||||
|
// there may be more than one LocalVariableTable, and that
|
||||||
|
// there is no specified ordering for the entries.
|
||||||
int numEntries = nextChar();
|
int numEntries = nextChar();
|
||||||
for (int i=0; i<numEntries; i++) {
|
for (int i = 0; i < numEntries; i++) {
|
||||||
int start_pc = nextChar();
|
int start_pc = nextChar();
|
||||||
int length = nextChar();
|
int length = nextChar();
|
||||||
int nameIndex = nextChar();
|
int nameIndex = nextChar();
|
||||||
int sigIndex = nextChar();
|
int sigIndex = nextChar();
|
||||||
int register = nextChar();
|
int register = nextChar();
|
||||||
if (start_pc == 0 &&
|
if (start_pc == 0) {
|
||||||
firstParam <= register &&
|
// ensure array large enough
|
||||||
register < endParam) {
|
if (register >= parameterNameIndices.length) {
|
||||||
int index = firstParam;
|
int newSize = Math.max(register, parameterNameIndices.length + 8);
|
||||||
for (Type t : sym.type.getParameterTypes()) {
|
parameterNameIndices =
|
||||||
if (index == register) {
|
Arrays.copyOf(parameterNameIndices, newSize);
|
||||||
parameterNames = parameterNames.prepend(readName(nameIndex));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index += Code.width(t);
|
|
||||||
}
|
}
|
||||||
|
parameterNameIndices[register] = nameIndex;
|
||||||
|
haveParameterNameIndices = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parameterNames = parameterNames.reverse();
|
|
||||||
((MethodSymbol)sym).savedParameterNames = parameterNames;
|
|
||||||
}
|
}
|
||||||
bp = newbp;
|
bp = newbp;
|
||||||
}
|
}
|
||||||
|
@ -1839,6 +1851,8 @@ public class ClassReader implements Completer {
|
||||||
syms.methodClass);
|
syms.methodClass);
|
||||||
}
|
}
|
||||||
MethodSymbol m = new MethodSymbol(flags, name, type, currentOwner);
|
MethodSymbol m = new MethodSymbol(flags, name, type, currentOwner);
|
||||||
|
if (saveParameterNames)
|
||||||
|
initParameterNames(m);
|
||||||
Symbol prevOwner = currentOwner;
|
Symbol prevOwner = currentOwner;
|
||||||
currentOwner = m;
|
currentOwner = m;
|
||||||
try {
|
try {
|
||||||
|
@ -1846,9 +1860,90 @@ public class ClassReader implements Completer {
|
||||||
} finally {
|
} finally {
|
||||||
currentOwner = prevOwner;
|
currentOwner = prevOwner;
|
||||||
}
|
}
|
||||||
|
if (saveParameterNames)
|
||||||
|
setParameterNames(m, type);
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the parameter names array.
|
||||||
|
* Parameter names are currently inferred from the names in the
|
||||||
|
* LocalVariableTable attributes of a Code attribute.
|
||||||
|
* (Note: this means parameter names are currently not available for
|
||||||
|
* methods without a Code attribute.)
|
||||||
|
* This method initializes an array in which to store the name indexes
|
||||||
|
* of parameter names found in LocalVariableTable attributes. It is
|
||||||
|
* slightly supersized to allow for additional slots with a start_pc of 0.
|
||||||
|
*/
|
||||||
|
void initParameterNames(MethodSymbol sym) {
|
||||||
|
// make allowance for synthetic parameters.
|
||||||
|
final int excessSlots = 4;
|
||||||
|
int expectedParameterSlots =
|
||||||
|
Code.width(sym.type.getParameterTypes()) + excessSlots;
|
||||||
|
if (parameterNameIndices == null
|
||||||
|
|| parameterNameIndices.length < expectedParameterSlots) {
|
||||||
|
parameterNameIndices = new int[expectedParameterSlots];
|
||||||
|
} else
|
||||||
|
Arrays.fill(parameterNameIndices, 0);
|
||||||
|
haveParameterNameIndices = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the parameter names for a symbol from the name index in the
|
||||||
|
* parameterNameIndicies array. The type of the symbol may have changed
|
||||||
|
* while reading the method attributes (see the Signature attribute).
|
||||||
|
* This may be because of generic information or because anonymous
|
||||||
|
* synthetic parameters were added. The original type (as read from
|
||||||
|
* the method descriptor) is used to help guess the existence of
|
||||||
|
* anonymous synthetic parameters.
|
||||||
|
* On completion, sym.savedParameter names will either be null (if
|
||||||
|
* no parameter names were found in the class file) or will be set to a
|
||||||
|
* list of names, one per entry in sym.type.getParameterTypes, with
|
||||||
|
* any missing names represented by the empty name.
|
||||||
|
*/
|
||||||
|
void setParameterNames(MethodSymbol sym, Type jvmType) {
|
||||||
|
// if no names were found in the class file, there's nothing more to do
|
||||||
|
if (!haveParameterNameIndices)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0;
|
||||||
|
// the code in readMethod may have skipped the first parameter when
|
||||||
|
// setting up the MethodType. If so, we make a corresponding allowance
|
||||||
|
// here for the position of the first parameter. Note that this
|
||||||
|
// assumes the skipped parameter has a width of 1 -- i.e. it is not
|
||||||
|
// a double width type (long or double.)
|
||||||
|
if (sym.name == names.init && currentOwner.hasOuterInstance()) {
|
||||||
|
// Sometimes anonymous classes don't have an outer
|
||||||
|
// instance, however, there is no reliable way to tell so
|
||||||
|
// we never strip this$n
|
||||||
|
if (!currentOwner.name.isEmpty())
|
||||||
|
firstParam += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym.type != jvmType) {
|
||||||
|
// reading the method attributes has caused the symbol's type to
|
||||||
|
// be changed. (i.e. the Signature attribute.) This may happen if
|
||||||
|
// there are hidden (synthetic) parameters in the descriptor, but
|
||||||
|
// not in the Signature. The position of these hidden parameters
|
||||||
|
// is unspecified; for now, assume they are at the beginning, and
|
||||||
|
// so skip over them. The primary case for this is two hidden
|
||||||
|
// parameters passed into Enum constructors.
|
||||||
|
int skip = Code.width(jvmType.getParameterTypes())
|
||||||
|
- Code.width(sym.type.getParameterTypes());
|
||||||
|
firstParam += skip;
|
||||||
|
}
|
||||||
|
List<Name> paramNames = List.nil();
|
||||||
|
int index = firstParam;
|
||||||
|
for (Type t: sym.type.getParameterTypes()) {
|
||||||
|
int nameIdx = (index < parameterNameIndices.length
|
||||||
|
? parameterNameIndices[index] : 0);
|
||||||
|
Name name = nameIdx == 0 ? names.empty : readName(nameIdx);
|
||||||
|
paramNames = paramNames.prepend(name);
|
||||||
|
index += Code.width(t);
|
||||||
|
}
|
||||||
|
sym.savedParameterNames = paramNames.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
/** Skip a field or method
|
/** Skip a field or method
|
||||||
*/
|
*/
|
||||||
void skipMember() {
|
void skipMember() {
|
||||||
|
|
485
langtools/test/tools/javac/6889255/T6889255.java
Normal file
485
langtools/test/tools/javac/6889255/T6889255.java
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.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.TypeTags;
|
||||||
|
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("<CT> " + 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("<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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue