| // 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 ::= "[" TypeSignature. |
| * |
| * 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> |
| */ |
| // TODO(b/129925954): Deprecate this once ...graph.GenericSignature is ready and rewriter is |
| // reimplemented based on the internal encoding and transformation logic. |
| 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: |
| // |
| |
| private 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); |
| } |
| } |
| |
| private void parseOptFormalTypeParameters() { |
| // OptFormalTypeParameters ::= ["<" FormalTypeParameter {FormalTypeParameter} ">"]. |
| |
| if (symbol == '<') { |
| actions.parsedSymbol(symbol); |
| scanSymbol(); |
| |
| updateFormalTypeParameter(); |
| |
| while ((symbol != '>') && (symbol > 0)) { |
| updateFormalTypeParameter(); |
| } |
| |
| actions.parsedSymbol(symbol); |
| expect('>'); |
| } |
| } |
| |
| private 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); |
| |
| 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); |
| } |
| } |