blob: 74b17d681d671aace9fec8c508039f2a08dc6fc3 [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming.signature;
import com.android.tools.r8.naming.signature.GenericSignatureAction.ParserPosition;
import java.lang.reflect.GenericSignatureFormatError;
import java.nio.CharBuffer;
/**
* Implements a parser for the generics signature attribute as defined by JVMS 7 $ 4.3.4.
* Uses a top-down, recursive descent parsing approach for the following grammar:
* <pre>
* ClassSignature ::=
* OptFormalTypeParams SuperclassSignature {SuperinterfaceSignature}.
* SuperclassSignature ::= ClassTypeSignature.
* SuperinterfaceSignature ::= ClassTypeSignature.
*
* OptFormalTypeParams ::=
* ["<" FormalTypeParameter {FormalTypeParameter} ">"].
*
* FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
* ClassBound ::= ":" [FieldTypeSignature].
* InterfaceBound ::= ":" FieldTypeSignature.
*
* FieldTypeSignature ::=
* ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
* ArrayTypeSignature ::= "[" TypSignature.
*
* ClassTypeSignature ::=
* "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments} ";".
*
* OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">".
*
* TypeArgument ::= ([WildcardIndicator] FieldTypeSignature) | "*".
* WildcardIndicator ::= "+" | "-".
*
* TypeVariableSignature ::= "T" Ident ";".
*
* TypSignature ::= FieldTypeSignature | BaseType.
* BaseType ::= "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z".
*
* MethodTypeSignature ::=
* OptFormalTypeParams "(" {TypeSignature} ")" ReturnType {ThrowsSignature}.
* ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
*
* ReturnType ::= TypSignature | VoidDescriptor.
* VoidDescriptor ::= "V".
* </pre>
*/
public class GenericSignatureParser<T> {
private GenericSignatureAction<T> actions;
/*
* Parser:
*/
private char symbol; // 0: eof; else valid term symbol or first char of identifier.
private String identifier;
/*
* Scanner:
* eof is private to the scan methods
* and it's set only when a scan is issued at the end of the buffer.
*/
private boolean eof;
private char[] buffer;
private int pos;
public GenericSignatureParser(GenericSignatureAction<T> actions) {
this.actions = actions;
}
public void parseClassSignature(String signature) {
try {
actions.start();
setInput(signature);
parseClassSignature();
actions.stop();
} catch (GenericSignatureFormatError e) {
throw e;
} catch (Throwable t) {
Error e = new GenericSignatureFormatError(
"Unknown error parsing class signature: " + t.getMessage());
e.addSuppressed(t);
throw e;
}
}
public void parseMethodSignature(String signature) {
try {
actions.start();
setInput(signature);
parseMethodTypeSignature();
actions.stop();
} catch (GenericSignatureFormatError e) {
throw e;
} catch (Throwable t) {
Error e = new GenericSignatureFormatError(
"Unknown error parsing method signature: " + t.getMessage());
e.addSuppressed(t);
throw e;
}
}
public void parseFieldSignature(String signature) {
try {
actions.start();
setInput(signature);
parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
actions.stop();
} catch (GenericSignatureFormatError e) {
throw e;
} catch (Throwable t) {
Error e = new GenericSignatureFormatError(
"Unknown error parsing field signature: " + t.getMessage());
e.addSuppressed(t);
throw e;
}
}
private void setInput(String input) {
this.buffer = input.toCharArray();
this.eof = false;
pos = 0;
symbol = 0;
identifier = null;
scanSymbol();
}
//
// Parser:
//
void parseClassSignature() {
// ClassSignature ::= OptFormalTypeParameters SuperclassSignature {SuperinterfaceSignature}.
parseOptFormalTypeParameters();
// SuperclassSignature ::= ClassTypeSignature.
parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION);
while (symbol > 0) {
// SuperinterfaceSignature ::= ClassTypeSignature.
parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION);
}
}
void parseOptFormalTypeParameters() {
// OptFormalTypeParameters ::= ["<" FormalTypeParameter {FormalTypeParameter} ">"].
if (symbol == '<') {
actions.parsedSymbol(symbol);
scanSymbol();
updateFormalTypeParameter();
while ((symbol != '>') && (symbol > 0)) {
updateFormalTypeParameter();
}
actions.parsedSymbol(symbol);
expect('>');
}
}
void updateFormalTypeParameter() {
// FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
scanIdentifier();
assert identifier != null;
actions.parsedIdentifier(identifier);
// ClassBound ::= ":" [FieldTypeSignature].
actions.parsedSymbol(symbol);
expect(':');
if (symbol == 'L' || symbol == '[' || symbol == 'T') {
parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
}
while (symbol == ':') {
// InterfaceBound ::= ":" FieldTypeSignature.
actions.parsedSymbol(symbol);
scanSymbol();
parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
}
}
private void parseFieldTypeSignature(ParserPosition parserPosition) {
// FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
switch (symbol) {
case 'L':
parseClassTypeSignature(parserPosition);
break;
case '[':
// ArrayTypeSignature ::= "[" TypeSignature.
actions.parsedSymbol(symbol);
scanSymbol();
updateTypeSignature(parserPosition);
break;
case 'T':
updateTypeVariableSignature();
break;
default:
parseError("Expected L, [ or T", pos);
}
}
private void parseClassTypeSignature(ParserPosition parserPosition) {
// ClassTypeSignature ::= "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments}
// ";".
actions.parsedSymbol(symbol);
expect('L');
StringBuilder qualIdent = new StringBuilder();
scanIdentifier();
assert identifier != null;
while (symbol == '/') {
qualIdent.append(identifier).append(symbol);
scanSymbol();
scanIdentifier();
assert identifier != null;
}
qualIdent.append(this.identifier);
T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString(), parserPosition);
updateOptTypeArguments();
while (symbol == '.') {
// Deal with Member Classes.
actions.parsedSymbol(symbol);
scanSymbol();
scanIdentifier();
assert identifier != null;
parsedEnclosingType = actions.parsedInnerTypeName(parsedEnclosingType, identifier);
updateOptTypeArguments();
}
actions.parsedSymbol(symbol);
expect(';');
}
private void updateOptTypeArguments() {
// OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">".
if (symbol == '<') {
actions.parsedSymbol(symbol);
scanSymbol();
updateTypeArgument();
while ((symbol != '>') && (symbol > 0)) {
updateTypeArgument();
}
actions.parsedSymbol(symbol);
expect('>');
}
}
private void updateTypeArgument() {
// TypeArgument ::= (["+" | "-"] FieldTypeSignature) | "*".
if (symbol == '*') {
actions.parsedSymbol(symbol);
scanSymbol();
} else if (symbol == '+') {
actions.parsedSymbol(symbol);
scanSymbol();
parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
} else if (symbol == '-') {
actions.parsedSymbol(symbol);
scanSymbol();
parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
} else {
parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
}
}
private void updateTypeVariableSignature() {
// TypeVariableSignature ::= "T" Ident ";".
actions.parsedSymbol(symbol);
expect('T');
scanIdentifier();
assert identifier != null;
actions.parsedIdentifier(identifier);
actions.parsedSymbol(symbol);
expect(';');
}
private void updateTypeSignature(ParserPosition parserPosition) {
switch (symbol) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
actions.parsedSymbol(symbol);
scanSymbol();
break;
default:
// Not an elementary type, but a FieldTypeSignature.
parseFieldTypeSignature(parserPosition);
}
}
private void parseMethodTypeSignature() {
// MethodTypeSignature ::= [FormalTypeParameters] "(" {TypeSignature} ")" ReturnType
// {ThrowsSignature}.
parseOptFormalTypeParameters();
actions.parsedSymbol(symbol);
expect('(');
while (symbol != ')' && (symbol > 0)) {
updateTypeSignature(ParserPosition.MEMBER_ANNOTATION);
}
actions.parsedSymbol(symbol);
expect(')');
updateReturnType();
if (symbol == '^') {
do {
actions.parsedSymbol(symbol);
scanSymbol();
// ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
if (symbol == 'T') {
updateTypeVariableSignature();
} else {
parseClassTypeSignature(ParserPosition.MEMBER_ANNOTATION);
}
} while (symbol == '^');
}
}
private void updateReturnType() {
// ReturnType ::= TypeSignature | "V".
if (symbol != 'V') {
updateTypeSignature(ParserPosition.MEMBER_ANNOTATION);
} else {
actions.parsedSymbol(symbol);
scanSymbol();
}
}
//
// Scanner:
//
private void scanSymbol() {
if (!eof) {
assert buffer != null;
if (pos < buffer.length) {
symbol = buffer[pos];
pos++;
} else {
symbol = 0;
eof = true;
}
} else {
parseError("Unexpected end of signature", pos);
}
}
private void expect(char c) {
if (eof) {
parseError("Unexpected end of signature", pos);
}
if (symbol == c) {
scanSymbol();
} else {
parseError("Expected " + c, pos - 1);
}
}
private boolean isStopSymbol(char ch) {
switch (ch) {
case ':':
case '/':
case ';':
case '<':
case '.':
return true;
default:
return false;
}
}
// PRE: symbol is the first char of the identifier.
// POST: symbol = the next symbol AFTER the identifier.
private void scanIdentifier() {
if (!eof && pos < buffer.length) {
StringBuilder identBuf = new StringBuilder(32);
if (!isStopSymbol(symbol)) {
identBuf.append(symbol);
// FINDBUGS
char[] bufferLocal = buffer;
assert bufferLocal != null;
do {
char ch = bufferLocal[pos];
if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))
|| !isStopSymbol(ch)) {
identBuf.append(bufferLocal[pos]);
pos++;
} else {
identifier = identBuf.toString();
scanSymbol();
return;
}
} while (pos != bufferLocal.length);
identifier = identBuf.toString();
symbol = 0;
eof = true;
} else {
// Ident starts with incorrect char.
symbol = 0;
eof = true;
parseError();
}
} else {
parseError("Unexpected end of signature", pos);
}
}
private void parseError() {
parseError("Unexpected", pos);
}
private void parseError(String message, int pos) {
String arrow = CharBuffer.allocate(pos).toString().replace('\0', ' ') + '^';
throw new GenericSignatureFormatError(
message + " at position " + (pos + 1) + "\n" + String.valueOf(buffer) + "\n" + arrow);
}
}