--- /dev/null
+/*
+ * 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();
+ }
+ }
+}