|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.reflect.generics.parser; |
|
|
|
import java.lang.reflect.GenericSignatureFormatError; |
|
import java.util.*; |
|
import sun.reflect.generics.tree.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class SignatureParser { |
|
// The input is conceptually a character stream (though currently it's |
|
// a string). This is slightly different than traditional parsers, |
|
// because there is no lexical scanner performing tokenization. |
|
// Having a separate tokenizer does not fit with the nature of the |
|
// input format. |
|
// Other than the absence of a tokenizer, this parser is a classic |
|
// recursive descent parser. Its structure corresponds as closely |
|
// as possible to the grammar in the JVMS. |
|
// |
|
// A note on asserts vs. errors: The code contains assertions |
|
// in situations that should never occur. An assertion failure |
|
// indicates a failure of the parser logic. A common pattern |
|
// is an assertion that the current input is a particular |
|
// character. This is often paired with a separate check |
|
// that this is the case, which seems redundant. For example: |
|
// |
|
// assert(current() != x); |
|
// if (current != x {error("expected an x"); |
|
// |
|
// where x is some character constant. |
|
// The assertion indicates, that, as currently written, |
|
// the code should never reach this point unless the input is an |
|
// x. On the other hand, the test is there to check the legality |
|
// of the input wrt to a given production. It may be that at a later |
|
// time the code might be called directly, and if the input is |
|
// invalid, the parser should flag an error in accordance |
|
// with its logic. |
|
|
|
private String input; |
|
private int index; |
|
private int mark; |
|
|
|
private static final char EOI = ':'; |
|
private static final boolean DEBUG = false; |
|
|
|
|
|
private SignatureParser(){} |
|
|
|
|
|
private void init(String s) { |
|
input = s; |
|
mark = index = 0; |
|
} |
|
|
|
// Utility methods. |
|
|
|
// Most parsing routines use the following routines to access the |
|
// input stream, and advance it as necessary. |
|
// This makes it easy to adapt the parser to operate on streams |
|
// of various kinds as well as strings. |
|
|
|
|
|
private char current(){ |
|
assert(index <= input.length()); |
|
return index < input.length() ? input.charAt(index) : EOI; |
|
} |
|
|
|
|
|
private void advance(){ |
|
assert(index <= input.length()); |
|
if (index < input.length()) index++; |
|
} |
|
|
|
|
|
private void mark() { |
|
mark = index; |
|
} |
|
|
|
|
|
private String remainder() { |
|
return input.substring(index); |
|
} |
|
|
|
// returns a substring of input from mark (inclusive) |
|
|
|
private String markToCurrent() { |
|
return input.substring(mark, index); |
|
} |
|
|
|
// Error handling routine. Encapsulates error handling. |
|
// Takes a string error message as argument. |
|
// Currently throws a GenericSignatureFormatError. |
|
|
|
private Error error(String errorMsg) { |
|
return new GenericSignatureFormatError("Signature Parse error: " + errorMsg + |
|
"\n\tRemaining input: " + remainder()); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void progress(int startingPosition) { |
|
if (index <= startingPosition) |
|
throw error("Failure to make progress!"); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static SignatureParser make() { |
|
return new SignatureParser(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public ClassSignature parseClassSig(String s) { |
|
if (DEBUG) System.out.println("Parsing class sig:" + s); |
|
init(s); |
|
return parseClassSignature(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MethodTypeSignature parseMethodSig(String s) { |
|
if (DEBUG) System.out.println("Parsing method sig:" + s); |
|
init(s); |
|
return parseMethodTypeSignature(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public TypeSignature parseTypeSig(String s) { |
|
if (DEBUG) System.out.println("Parsing type sig:" + s); |
|
init(s); |
|
return parseTypeSignature(); |
|
} |
|
|
|
// Parsing routines. |
|
// As a rule, the parsing routines access the input using the |
|
// utilities current() and advance(). |
|
// The convention is that when a parsing routine is invoked |
|
// it expects the current input to be the first character it should parse |
|
// and when it completes parsing, it leaves the input at the first |
|
// character after the input parses. |
|
|
|
/* |
|
* Note on grammar conventions: a trailing "*" matches zero or |
|
* more occurrences, a trailing "+" matches one or more occurrences, |
|
* "_opt" indicates an optional component. |
|
*/ |
|
|
|
|
|
|
|
|
|
*/ |
|
private ClassSignature parseClassSignature() { |
|
|
|
assert(index == 0); |
|
return ClassSignature.make(parseZeroOrMoreFormalTypeParameters(), |
|
parseClassTypeSignature(), |
|
parseSuperInterfaces()); |
|
} |
|
|
|
private FormalTypeParameter[] parseZeroOrMoreFormalTypeParameters(){ |
|
if (current() == '<') { |
|
return parseFormalTypeParameters(); |
|
} else { |
|
return new FormalTypeParameter[0]; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private FormalTypeParameter[] parseFormalTypeParameters(){ |
|
List<FormalTypeParameter> ftps = new ArrayList<>(3); |
|
assert(current() == '<'); |
|
if (current() != '<') { throw error("expected '<'");} |
|
advance(); |
|
ftps.add(parseFormalTypeParameter()); |
|
while (current() != '>') { |
|
int startingPosition = index; |
|
ftps.add(parseFormalTypeParameter()); |
|
progress(startingPosition); |
|
} |
|
advance(); |
|
return ftps.toArray(new FormalTypeParameter[ftps.size()]); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private FormalTypeParameter parseFormalTypeParameter(){ |
|
String id = parseIdentifier(); |
|
FieldTypeSignature[] bs = parseBounds(); |
|
return FormalTypeParameter.make(id, bs); |
|
} |
|
|
|
private String parseIdentifier() { |
|
mark(); |
|
skipIdentifier(); |
|
return markToCurrent(); |
|
} |
|
|
|
private void skipIdentifier() { |
|
char c = current(); |
|
while (c != ';' && c != '.' && c != '/' && |
|
c != '[' && c != ':' && c != '>' && |
|
c != '<' && !Character.isWhitespace(c)) { |
|
advance(); |
|
c = current(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private FieldTypeSignature parseFieldTypeSignature() { |
|
return parseFieldTypeSignature(true); |
|
} |
|
|
|
private FieldTypeSignature parseFieldTypeSignature(boolean allowArrays) { |
|
switch(current()) { |
|
case 'L': |
|
return parseClassTypeSignature(); |
|
case 'T': |
|
return parseTypeVariableSignature(); |
|
case '[': |
|
if (allowArrays) |
|
return parseArrayTypeSignature(); |
|
else |
|
throw error("Array signature not allowed here."); |
|
default: throw error("Expected Field Type Signature"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private ClassTypeSignature parseClassTypeSignature(){ |
|
assert(current() == 'L'); |
|
if (current() != 'L') { throw error("expected a class type");} |
|
advance(); |
|
List<SimpleClassTypeSignature> scts = new ArrayList<>(5); |
|
scts.add(parsePackageNameAndSimpleClassTypeSignature()); |
|
|
|
parseClassTypeSignatureSuffix(scts); |
|
if (current() != ';') |
|
throw error("expected ';' got '" + current() + "'"); |
|
|
|
advance(); |
|
return ClassTypeSignature.make(scts); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private SimpleClassTypeSignature parsePackageNameAndSimpleClassTypeSignature() { |
|
// Parse both any optional leading PackageSpecifier as well as |
|
// the following SimpleClassTypeSignature. |
|
|
|
mark(); |
|
skipIdentifier(); |
|
while (current() == '/') { |
|
advance(); |
|
skipIdentifier(); |
|
} |
|
String id = markToCurrent().replace('/', '.'); |
|
|
|
switch (current()) { |
|
case ';': |
|
return SimpleClassTypeSignature.make(id, false, new TypeArgument[0]); |
|
case '<': |
|
if (DEBUG) System.out.println("\t remainder: " + remainder()); |
|
return SimpleClassTypeSignature.make(id, false, parseTypeArguments()); |
|
default: |
|
throw error("expected '<' or ';' but got " + current()); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private SimpleClassTypeSignature parseSimpleClassTypeSignature(boolean dollar){ |
|
String id = parseIdentifier(); |
|
char c = current(); |
|
|
|
switch (c) { |
|
case ';': |
|
case '.': |
|
return SimpleClassTypeSignature.make(id, dollar, new TypeArgument[0]) ; |
|
case '<': |
|
return SimpleClassTypeSignature.make(id, dollar, parseTypeArguments()); |
|
default: |
|
throw error("expected '<' or ';' or '.', got '" + c + "'."); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void parseClassTypeSignatureSuffix(List<SimpleClassTypeSignature> scts) { |
|
while (current() == '.') { |
|
advance(); |
|
scts.add(parseSimpleClassTypeSignature(true)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private TypeArgument[] parseTypeArguments() { |
|
List<TypeArgument> tas = new ArrayList<>(3); |
|
assert(current() == '<'); |
|
if (current() != '<') { throw error("expected '<'");} |
|
advance(); |
|
tas.add(parseTypeArgument()); |
|
while (current() != '>') { |
|
|
|
tas.add(parseTypeArgument()); |
|
} |
|
advance(); |
|
return tas.toArray(new TypeArgument[tas.size()]); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private TypeArgument parseTypeArgument() { |
|
FieldTypeSignature[] ub, lb; |
|
ub = new FieldTypeSignature[1]; |
|
lb = new FieldTypeSignature[1]; |
|
TypeArgument[] ta = new TypeArgument[0]; |
|
char c = current(); |
|
switch (c) { |
|
case '+': { |
|
advance(); |
|
ub[0] = parseFieldTypeSignature(); |
|
lb[0] = BottomSignature.make(); |
|
return Wildcard.make(ub, lb); |
|
} |
|
case '*':{ |
|
advance(); |
|
ub[0] = SimpleClassTypeSignature.make("java.lang.Object", false, ta); |
|
lb[0] = BottomSignature.make(); |
|
return Wildcard.make(ub, lb); |
|
} |
|
case '-': { |
|
advance(); |
|
lb[0] = parseFieldTypeSignature(); |
|
ub[0] = SimpleClassTypeSignature.make("java.lang.Object", false, ta); |
|
return Wildcard.make(ub, lb); |
|
} |
|
default: |
|
return parseFieldTypeSignature(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private TypeVariableSignature parseTypeVariableSignature() { |
|
assert(current() == 'T'); |
|
if (current() != 'T') { throw error("expected a type variable usage");} |
|
advance(); |
|
TypeVariableSignature ts = TypeVariableSignature.make(parseIdentifier()); |
|
if (current() != ';') { |
|
throw error("; expected in signature of type variable named" + |
|
ts.getIdentifier()); |
|
} |
|
advance(); |
|
return ts; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private ArrayTypeSignature parseArrayTypeSignature() { |
|
if (current() != '[') {throw error("expected array type signature");} |
|
advance(); |
|
return ArrayTypeSignature.make(parseTypeSignature()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private TypeSignature parseTypeSignature() { |
|
switch (current()) { |
|
case 'B': |
|
case 'C': |
|
case 'D': |
|
case 'F': |
|
case 'I': |
|
case 'J': |
|
case 'S': |
|
case 'Z': |
|
return parseBaseType(); |
|
|
|
default: |
|
return parseFieldTypeSignature(); |
|
} |
|
} |
|
|
|
private BaseType parseBaseType() { |
|
switch(current()) { |
|
case 'B': |
|
advance(); |
|
return ByteSignature.make(); |
|
case 'C': |
|
advance(); |
|
return CharSignature.make(); |
|
case 'D': |
|
advance(); |
|
return DoubleSignature.make(); |
|
case 'F': |
|
advance(); |
|
return FloatSignature.make(); |
|
case 'I': |
|
advance(); |
|
return IntSignature.make(); |
|
case 'J': |
|
advance(); |
|
return LongSignature.make(); |
|
case 'S': |
|
advance(); |
|
return ShortSignature.make(); |
|
case 'Z': |
|
advance(); |
|
return BooleanSignature.make(); |
|
default: { |
|
assert(false); |
|
throw error("expected primitive type"); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private FieldTypeSignature[] parseBounds() { |
|
List<FieldTypeSignature> fts = new ArrayList<>(3); |
|
|
|
if (current() == ':') { |
|
advance(); |
|
switch(current()) { |
|
case ':': |
|
break; |
|
|
|
default: |
|
fts.add(parseFieldTypeSignature()); |
|
} |
|
|
|
|
|
while (current() == ':') { |
|
advance(); |
|
fts.add(parseFieldTypeSignature()); |
|
} |
|
} else |
|
error("Bound expected"); |
|
|
|
return fts.toArray(new FieldTypeSignature[fts.size()]); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private ClassTypeSignature[] parseSuperInterfaces() { |
|
List<ClassTypeSignature> cts = new ArrayList<>(5); |
|
while(current() == 'L') { |
|
cts.add(parseClassTypeSignature()); |
|
} |
|
return cts.toArray(new ClassTypeSignature[cts.size()]); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private MethodTypeSignature parseMethodTypeSignature() { |
|
|
|
FieldTypeSignature[] ets; |
|
|
|
assert(index == 0); |
|
return MethodTypeSignature.make(parseZeroOrMoreFormalTypeParameters(), |
|
parseFormalParameters(), |
|
parseReturnType(), |
|
parseZeroOrMoreThrowsSignatures()); |
|
} |
|
|
|
|
|
private TypeSignature[] parseFormalParameters() { |
|
if (current() != '(') {throw error("expected '('");} |
|
advance(); |
|
TypeSignature[] pts = parseZeroOrMoreTypeSignatures(); |
|
if (current() != ')') {throw error("expected ')'");} |
|
advance(); |
|
return pts; |
|
} |
|
|
|
|
|
private TypeSignature[] parseZeroOrMoreTypeSignatures() { |
|
List<TypeSignature> ts = new ArrayList<>(); |
|
boolean stop = false; |
|
while (!stop) { |
|
switch(current()) { |
|
case 'B': |
|
case 'C': |
|
case 'D': |
|
case 'F': |
|
case 'I': |
|
case 'J': |
|
case 'S': |
|
case 'Z': |
|
case 'L': |
|
case 'T': |
|
case '[': { |
|
ts.add(parseTypeSignature()); |
|
break; |
|
} |
|
default: stop = true; |
|
} |
|
} |
|
return ts.toArray(new TypeSignature[ts.size()]); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private ReturnType parseReturnType(){ |
|
if (current() == 'V') { |
|
advance(); |
|
return VoidDescriptor.make(); |
|
} else |
|
return parseTypeSignature(); |
|
} |
|
|
|
|
|
private FieldTypeSignature[] parseZeroOrMoreThrowsSignatures(){ |
|
List<FieldTypeSignature> ets = new ArrayList<>(3); |
|
while( current() == '^') { |
|
ets.add(parseThrowsSignature()); |
|
} |
|
return ets.toArray(new FieldTypeSignature[ets.size()]); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private FieldTypeSignature parseThrowsSignature() { |
|
assert(current() == '^'); |
|
if (current() != '^') { throw error("expected throws signature");} |
|
advance(); |
|
return parseFieldTypeSignature(false); |
|
} |
|
} |