/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package signature.converter.dex; import static signature.converter.dex.DexUtil.getClassName; import static signature.converter.dex.DexUtil.getPackageName; import signature.model.IClassDefinition; import signature.model.IClassReference; import signature.model.IConstructor; import signature.model.IGenericDeclaration; import signature.model.IMethod; import signature.model.ITypeReference; import signature.model.ITypeVariableDefinition; import signature.model.ITypeVariableReference; import signature.model.impl.SigArrayType; import signature.model.impl.SigParameterizedType; import signature.model.impl.SigPrimitiveType; import signature.model.impl.SigTypeVariableDefinition; import signature.model.impl.SigWildcardType; import signature.model.impl.Uninitialized; import signature.model.util.ITypeFactory; import java.lang.reflect.GenericSignatureFormatError; import java.util.ArrayList; import java.util.List; /** * Implements a parser for the generics signature attribute. 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 { public List<ITypeReference> exceptionTypes; public List<ITypeReference> parameterTypes; public List<ITypeVariableDefinition> formalTypeParameters; public ITypeReference returnType; public ITypeReference fieldType; public List<ITypeReference> interfaceTypes; public ITypeReference superclassType; private IGenericDeclaration genericDecl; /* * 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; private final ITypeFactory factory; private final IClassInitializer classFinder; private boolean parseForField; public GenericSignatureParser(ITypeFactory factory, IClassInitializer classFinder) { this.factory = factory; this.classFinder = classFinder; } private void setInput(IGenericDeclaration genericDecl, String input) { if (input != null) { this.genericDecl = genericDecl; this.buffer = input.toCharArray(); this.eof = false; scanSymbol(); } else { this.eof = true; } } public ITypeReference parseNonGenericType(String typeSignature) { setInput(null, typeSignature); ITypeReference type = parsePrimitiveType(); if (type == null) { type = parseFieldTypeSignature(); } return type; } public ITypeReference parseNonGenericReturnType(String typeSignature) { setInput(null, typeSignature); ITypeReference returnType = parsePrimitiveType(); if (returnType == null) { returnType = parseReturnType(); } return returnType; } private ITypeReference parsePrimitiveType() { switch (symbol) { case 'B': scanSymbol(); return SigPrimitiveType.BYTE_TYPE; case 'C': scanSymbol(); return SigPrimitiveType.CHAR_TYPE; case 'D': scanSymbol(); return SigPrimitiveType.DOUBLE_TYPE; case 'F': scanSymbol(); return SigPrimitiveType.FLOAT_TYPE; case 'I': scanSymbol(); return SigPrimitiveType.INT_TYPE; case 'J': scanSymbol(); return SigPrimitiveType.LONG_TYPE; case 'S': scanSymbol(); return SigPrimitiveType.SHORT_TYPE; case 'Z': scanSymbol(); return SigPrimitiveType.BOOLEAN_TYPE; default: return null; } } /** * Parses the generic signature of a class and creates the data structure * representing the signature. * * @param classToProcess * the GenericDeclaration calling this method * @param signature * the generic signature of the class */ public void parseForClass(IClassDefinition classToProcess, String signature) { setInput(classToProcess, signature); if (!eof) { parseClassSignature(); } else { throw new IllegalStateException("Generic signature is invalid!"); } } /** * Parses the generic signature of a method and creates the data structure * representing the signature. * * @param genericDecl * the GenericDeclaration calling this method * @param signature * the generic signature of the class */ public void parseForMethod(IMethod genericDecl, String signature) { setInput(genericDecl, signature); if (!eof) { parseMethodTypeSignature(); } else { throw new IllegalStateException("Generic signature is invalid!"); } } /** * Parses the generic signature of a constructor and creates the data * structure representing the signature. * * @param genericDecl * the GenericDeclaration calling this method * @param signature * the generic signature of the class */ public void parseForConstructor(IConstructor genericDecl, String signature) { setInput(genericDecl, signature); if (!eof) { parseMethodTypeSignature(); } else { throw new IllegalStateException("Generic signature is invalid!"); } } /** * Parses the generic signature of a field and creates the data structure * representing the signature. * * @param genericDecl * the GenericDeclaration calling this method * @param signature * the generic signature of the class */ public void parseForField(IClassDefinition genericDecl, String signature) { parseForField = true; setInput(genericDecl, signature); try { if (!eof) { this.fieldType = parseFieldTypeSignature(); } else { throw new IllegalStateException( "Generic signature is invalid!"); } } finally { parseForField = false; } } private void parseClassSignature() { // ClassSignature ::= // OptFormalTypeParameters SuperclassSignature // {SuperinterfaceSignature}. parseOptFormalTypeParameters(); // SuperclassSignature ::= ClassTypeSignature. this.superclassType = parseClassTypeSignature(); interfaceTypes = new ArrayList<ITypeReference>(16); while (symbol > 0) { // SuperinterfaceSignature ::= ClassTypeSignature. interfaceTypes.add(parseClassTypeSignature()); } } private void parseOptFormalTypeParameters() { // OptFormalTypeParameters ::= // ["<" FormalTypeParameter {FormalTypeParameter} ">"]. List<ITypeVariableDefinition> typeParameters = new ArrayList<ITypeVariableDefinition>(); if (symbol == '<') { scanSymbol(); typeParameters.add(parseFormalTypeParameter()); while ((symbol != '>') && (symbol > 0)) { typeParameters.add(parseFormalTypeParameter()); } expect('>'); } formalTypeParameters = typeParameters; } private SigTypeVariableDefinition parseFormalTypeParameter() { // FormalTypeParameter ::= Ident ClassBound {InterfaceBound}. scanIdentifier(); String name = identifier.intern(); SigTypeVariableDefinition typeVariable = factory.getTypeVariable(name, genericDecl); List<ITypeReference> bounds = new ArrayList<ITypeReference>(); // ClassBound ::= ":" [FieldTypeSignature]. expect(':'); if (symbol == 'L' || symbol == '[' || symbol == 'T') { bounds.add(parseFieldTypeSignature()); } while (symbol == ':') { // InterfaceBound ::= ":" FieldTypeSignature. scanSymbol(); bounds.add(parseFieldTypeSignature()); } typeVariable.setUpperBounds(bounds); return typeVariable; } /** * Returns the generic declaration for the type variable with the specified * name. * * @param variableName * the name of the type variable * @param declaration * the declaration to start searching * @return the declaration which defines the specified type variable */ private IGenericDeclaration getDeclarationOfTypeVariable( String variableName, IClassDefinition declaration) { assert variableName != null; assert declaration != null; if (!Uninitialized.isInitialized(declaration.getTypeParameters())) { declaration = classFinder.initializeClass(declaration .getPackageName(), declaration.getName()); } for (ITypeVariableDefinition typeVariable : declaration .getTypeParameters()) { if (variableName.equals(typeVariable.getName())) { return declaration; } } return getDeclarationOfTypeVariable(variableName, declaration .getDeclaringClass()); } private ITypeReference parseFieldTypeSignature() { // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature // | TypeVariableSignature. switch (symbol) { case 'L': return parseClassTypeSignature(); case '[': // ArrayTypeSignature ::= "[" TypSignature. scanSymbol(); SigArrayType arrayType = factory.getArrayType(parseTypeSignature()); return arrayType; case 'T': return parseTypeVariableSignature(); default: throw new GenericSignatureFormatError(); } } private ITypeReference parseClassTypeSignature() { // ClassTypeSignature ::= "L" {Ident "/"} Ident // OptTypeArguments {"." Ident OptTypeArguments} ";". expect('L'); StringBuilder qualIdent = new StringBuilder("L"); scanIdentifier(); while (symbol == '/') { scanSymbol(); qualIdent.append(identifier).append("/"); scanIdentifier(); } qualIdent.append(this.identifier); List<ITypeReference> typeArgs = parseOptTypeArguments(); ITypeReference parentType = null; String packageName = getPackageName(qualIdent.toString() + ";"); String className = getClassName(qualIdent.toString() + ";"); if (typeArgs.isEmpty()) { parentType = factory.getClassReference(packageName, className); } else { IClassReference rawType = factory.getClassReference(packageName, className); SigParameterizedType parameterizedType = factory .getParameterizedType(null, rawType, typeArgs); parentType = parameterizedType; } ITypeReference typeToReturn = parentType; // if owner type is a parameterized type, the types are separated by '.' while (symbol == '.') { // Deal with Member Classes: scanSymbol(); scanIdentifier(); qualIdent.append("$").append(identifier); typeArgs = parseOptTypeArguments(); ITypeReference memberType = null; packageName = getPackageName(qualIdent.toString() + ";"); className = getClassName(qualIdent.toString() + ";"); if (typeArgs.isEmpty()) { memberType = factory.getClassReference(packageName, className); } else { IClassReference rawType = factory.getClassReference( packageName, className); SigParameterizedType parameterizedType = factory .getParameterizedType(parentType, rawType, typeArgs); memberType = parameterizedType; } typeToReturn = memberType; } expect(';'); return typeToReturn; } private List<ITypeReference> parseOptTypeArguments() { // OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">". List<ITypeReference> typeArgs = new ArrayList<ITypeReference>(8); if (symbol == '<') { scanSymbol(); typeArgs.add(parseTypeArgument()); while ((symbol != '>') && (symbol > 0)) { typeArgs.add(parseTypeArgument()); } expect('>'); } return typeArgs; } private ITypeReference parseTypeArgument() { // TypeArgument ::= (["+" | "-"] FieldTypeSignature) | "*". List<ITypeReference> extendsBound = new ArrayList<ITypeReference>(1); ITypeReference superBound = null; if (symbol == '*') { scanSymbol(); extendsBound.add(factory.getClassReference("java.lang", "Object")); SigWildcardType wildcardType = factory.getWildcardType(superBound, extendsBound); return wildcardType; } else if (symbol == '+') { scanSymbol(); extendsBound.add(parseFieldTypeSignature()); SigWildcardType wildcardType = factory.getWildcardType(superBound, extendsBound); return wildcardType; } else if (symbol == '-') { scanSymbol(); superBound = parseFieldTypeSignature(); extendsBound.add(factory.getClassReference("java.lang", "Object")); SigWildcardType wildcardType = factory.getWildcardType(superBound, extendsBound); return wildcardType; } else { return parseFieldTypeSignature(); } } private ITypeVariableReference parseTypeVariableSignature() { // TypeVariableSignature ::= "T" Ident ";". expect('T'); scanIdentifier(); expect(';'); IGenericDeclaration declaration = genericDecl; if (!factory.containsTypeVariableDefinition(identifier, declaration)) { // since a field is not a generic declaration, i need to treat it // differently. // the generic declaration if (parseForField) { declaration = getDeclarationOfTypeVariable(identifier, (IClassDefinition) genericDecl); } else { declaration = getDeclarationOfTypeVariable(identifier, genericDecl.getDeclaringClass()); } // just create type variable factory.getTypeVariable(identifier, declaration); } return factory.getTypeVariableReference(identifier, declaration); } private ITypeReference parseTypeSignature() { switch (symbol) { case 'B': scanSymbol(); return SigPrimitiveType.BYTE_TYPE; case 'C': scanSymbol(); return SigPrimitiveType.CHAR_TYPE; case 'D': scanSymbol(); return SigPrimitiveType.DOUBLE_TYPE; case 'F': scanSymbol(); return SigPrimitiveType.FLOAT_TYPE; case 'I': scanSymbol(); return SigPrimitiveType.INT_TYPE; case 'J': scanSymbol(); return SigPrimitiveType.LONG_TYPE; case 'S': scanSymbol(); return SigPrimitiveType.SHORT_TYPE; case 'Z': scanSymbol(); return SigPrimitiveType.BOOLEAN_TYPE; default: // Not an elementary type, but a FieldTypeSignature. return parseFieldTypeSignature(); } } private void parseMethodTypeSignature() { // MethodTypeSignature ::= [FormalTypeParameters] // "(" {TypeSignature} ")" ReturnType {ThrowsSignature}. parseOptFormalTypeParameters(); parameterTypes = new ArrayList<ITypeReference>(16); expect('('); while (symbol != ')' && (symbol > 0)) { parameterTypes.add(parseTypeSignature()); } expect(')'); returnType = parseReturnType(); exceptionTypes = new ArrayList<ITypeReference>(8); while (symbol == '^') { scanSymbol(); // ThrowsSignature ::= ("^" ClassTypeSignature) | // ("^" TypeVariableSignature). if (symbol == 'T') { exceptionTypes.add(parseTypeVariableSignature()); } else { exceptionTypes.add(parseClassTypeSignature()); } } } private ITypeReference parseReturnType() { // ReturnType ::= TypeSignature | "V". if (symbol != 'V') { return parseTypeSignature(); } else { scanSymbol(); return SigPrimitiveType.VOID_TYPE; } } // // Scanner: // private void scanSymbol() { if (!eof) { if (pos < buffer.length) { symbol = buffer[pos]; pos++; } else { symbol = 0; eof = true; } } else { throw new GenericSignatureFormatError(); } } private void expect(char c) { if (symbol == c) { scanSymbol(); } else { throw new GenericSignatureFormatError(); } } private boolean isStopSymbol(char ch) { switch (ch) { case ':': case '/': case ';': case '<': case '.': return true; } return false; } // PRE: symbol is the first char of the identifier. // POST: symbol = the next symbol AFTER the identifier. private void scanIdentifier() { if (!eof) { StringBuilder identBuf = new StringBuilder(32); if (!isStopSymbol(symbol)) { identBuf.append(symbol); do { char ch = buffer[pos]; if ((ch >= 'a') && (ch <= 'z') || (ch >= 'A') && (ch <= 'Z') || !isStopSymbol(ch)) { identBuf.append(buffer[pos]); pos++; } else { identifier = identBuf.toString(); scanSymbol(); return; } } while (pos != buffer.length); identifier = identBuf.toString(); symbol = 0; eof = true; } else { // Ident starts with incorrect char. symbol = 0; eof = true; throw new GenericSignatureFormatError(); } } else { throw new GenericSignatureFormatError(); } } }