123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- /* *******************************************************************
- * Copyright (c) 2005-2008 Contributors.
- * All rights reserved.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License v 2.0
- * which accompanies this distribution and is available at
- * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
- *
- * ******************************************************************/
- package org.aspectj.util;
-
- import java.util.ArrayList;
- import java.util.List;
-
- import org.aspectj.util.GenericSignature.ArrayTypeSignature;
- import org.aspectj.util.GenericSignature.BaseTypeSignature;
- import org.aspectj.util.GenericSignature.ClassTypeSignature;
- import org.aspectj.util.GenericSignature.FieldTypeSignature;
- import org.aspectj.util.GenericSignature.FormalTypeParameter;
- import org.aspectj.util.GenericSignature.MethodTypeSignature;
- import org.aspectj.util.GenericSignature.SimpleClassTypeSignature;
- import org.aspectj.util.GenericSignature.TypeArgument;
- import org.aspectj.util.GenericSignature.TypeSignature;
- import org.aspectj.util.GenericSignature.TypeVariableSignature;
-
- /**
- * Parses the generic signature attribute as defined in the JVM spec.
- *
- * @author Adrian Colyer
- * @author Andy Clement
- */
- public class GenericSignatureParser {
-
- private String inputString;
- private String[] tokenStream; // for parse in flight
- private int tokenIndex = 0;
-
- /**
- * AMC. Parse the signature string interpreting it as a ClassSignature according to the grammar defined in Section 4.4.4 of the
- * JVM specification.
- */
- public GenericSignature.ClassSignature parseAsClassSignature(String sig) {
- this.inputString = sig;
- tokenStream = tokenize(sig);
- tokenIndex = 0;
- GenericSignature.ClassSignature classSig = new GenericSignature.ClassSignature();
- // FormalTypeParameters-opt
- if (maybeEat("<")) {
- List<FormalTypeParameter> formalTypeParametersList = new ArrayList<>();
- do {
- formalTypeParametersList.add(parseFormalTypeParameter());
- } while (!maybeEat(">"));
- classSig.formalTypeParameters = new FormalTypeParameter[formalTypeParametersList.size()];
- formalTypeParametersList.toArray(classSig.formalTypeParameters);
- }
- classSig.superclassSignature = parseClassTypeSignature();
- List<ClassTypeSignature> superIntSigs = new ArrayList<>();
- while (tokenIndex < tokenStream.length) {
- superIntSigs.add(parseClassTypeSignature());
- }
- classSig.superInterfaceSignatures = new ClassTypeSignature[superIntSigs.size()];
- superIntSigs.toArray(classSig.superInterfaceSignatures);
- return classSig;
- }
-
- /**
- * AMC. Parse the signature string interpreting it as a MethodTypeSignature according to the grammar defined in Section 4.4.4 of
- * the JVM specification.
- */
- public MethodTypeSignature parseAsMethodSignature(String sig) {
- this.inputString = sig;
- tokenStream = tokenize(sig);
- tokenIndex = 0;
- FormalTypeParameter[] formals = new FormalTypeParameter[0];
- TypeSignature returnType = null;
- // FormalTypeParameters-opt
- if (maybeEat("<")) {
- List<FormalTypeParameter> formalTypeParametersList = new ArrayList<>();
- do {
- formalTypeParametersList.add(parseFormalTypeParameter());
- } while (!maybeEat(">"));
- formals = new FormalTypeParameter[formalTypeParametersList.size()];
- formalTypeParametersList.toArray(formals);
- }
- // Parameters
- eat("(");
- List<TypeSignature> paramList = new ArrayList<>();
- while (!maybeEat(")")) {
- FieldTypeSignature fsig = parseFieldTypeSignature(true);
- if (fsig != null) {
- paramList.add(fsig);
- } else {
- paramList.add(new GenericSignature.BaseTypeSignature(eatIdentifier()));
- }
- }
- TypeSignature[] params = new TypeSignature[paramList.size()];
- paramList.toArray(params);
- // return type
- returnType = parseFieldTypeSignature(true);
- if (returnType == null)
- returnType = new GenericSignature.BaseTypeSignature(eatIdentifier());
- // throws
- List<FieldTypeSignature> throwsList = new ArrayList<>();
- while (maybeEat("^")) {
- FieldTypeSignature fsig = parseFieldTypeSignature(false);
- throwsList.add(fsig);
- }
- FieldTypeSignature[] throwsSigs = new FieldTypeSignature[throwsList.size()];
- throwsList.toArray(throwsSigs);
- return new GenericSignature.MethodTypeSignature(formals, params, returnType, throwsSigs);
- }
-
- /**
- * AMC. Parse the signature string interpreting it as a FieldTypeSignature according to the grammar defined in Section 4.4.4 of
- * the JVM specification.
- */
- public FieldTypeSignature parseAsFieldSignature(String sig) {
- this.inputString = sig;
- tokenStream = tokenize(sig);
- tokenIndex = 0;
- return parseFieldTypeSignature(false);
- }
-
- private FormalTypeParameter parseFormalTypeParameter() {
- FormalTypeParameter ftp = new FormalTypeParameter();
- // Identifier
- ftp.identifier = eatIdentifier();
- // ClassBound
- eat(":");
- ftp.classBound = parseFieldTypeSignature(true);
- if (ftp.classBound == null) {
- ftp.classBound = new ClassTypeSignature("Ljava/lang/Object;", "Ljava/lang/Object");
- }
- // Optional InterfaceBounds
- List<FieldTypeSignature> optionalBounds = new ArrayList<>();
- while (maybeEat(":")) {
- optionalBounds.add(parseFieldTypeSignature(false));
- }
- ftp.interfaceBounds = new FieldTypeSignature[optionalBounds.size()];
- optionalBounds.toArray(ftp.interfaceBounds);
- return ftp;
- }
-
- private FieldTypeSignature parseFieldTypeSignature(boolean isOptional) {
- if (isOptional) {
- // anything other than 'L', 'T' or '[' and we're out of here
- if (!tokenStream[tokenIndex].startsWith("L") && !tokenStream[tokenIndex].startsWith("T")
- && !tokenStream[tokenIndex].startsWith("[")) {
- return null;
- }
- }
- if (maybeEat("[")) {
- return parseArrayTypeSignature();
- } else if (tokenStream[tokenIndex].startsWith("L")) {
- return parseClassTypeSignature();
- } else if (tokenStream[tokenIndex].startsWith("T")) {
- return parseTypeVariableSignature();
- } else {
- throw new IllegalStateException("Expecting [,L, or T, but found " + tokenStream[tokenIndex] + " while unpacking "
- + inputString);
- }
- }
-
- private ArrayTypeSignature parseArrayTypeSignature() {
- // opening [ already eaten
- FieldTypeSignature fieldType = parseFieldTypeSignature(true);
- if (fieldType != null) {
- return new ArrayTypeSignature(fieldType);
- } else {
- // must be BaseType array
- return new ArrayTypeSignature(new BaseTypeSignature(eatIdentifier()));
- }
- }
-
- // L PackageSpecifier* SimpleClassTypeSignature ClassTypeSignature* ;
- private ClassTypeSignature parseClassTypeSignature() {
- SimpleClassTypeSignature outerType = null;
- SimpleClassTypeSignature[] nestedTypes = new SimpleClassTypeSignature[0];
- StringBuilder ret = new StringBuilder();
- String identifier = eatIdentifier();
- ret.append(identifier);
- while (maybeEat("/")) {
- ret.append("/"); // dont forget this...
- ret.append(eatIdentifier());
- }
- identifier = ret.toString();
- // now we have either a "." indicating the start of a nested type,
- // or a "<" indication type arguments, or ";" and we are done.
- while (!maybeEat(";")) {
- if (tokenStream[tokenIndex].equals(".")) {
- // outer type completed
- outerType = new SimpleClassTypeSignature(identifier);
- nestedTypes = parseNestedTypesHelper(ret);
- } else if (tokenStream[tokenIndex].equals("<")) {
- ret.append("<");
- TypeArgument[] tArgs = maybeParseTypeArguments();
- for (TypeArgument tArg : tArgs) {
- ret.append(tArg.toString());
- }
- ret.append(">");
- outerType = new SimpleClassTypeSignature(identifier, tArgs);
- nestedTypes = parseNestedTypesHelper(ret);
- } else {
- throw new IllegalStateException("Expecting .,<, or ;, but found " + tokenStream[tokenIndex] + " while unpacking "
- + inputString);
- }
- }
- ret.append(";");
- if (outerType == null)
- outerType = new SimpleClassTypeSignature(ret.toString());
- return new ClassTypeSignature(ret.toString(), outerType, nestedTypes);
- }
-
- /**
- * Helper method to digest nested types, slightly more complex than necessary to cope with some android related
- * incorrect classes (see bug 406167)
- */
- private SimpleClassTypeSignature[] parseNestedTypesHelper(StringBuilder ret) {
- boolean brokenSignature = false;
- SimpleClassTypeSignature[] nestedTypes;
- List<SimpleClassTypeSignature> nestedTypeList = new ArrayList<>();
- while (maybeEat(".")) {
- ret.append(".");
- SimpleClassTypeSignature sig = parseSimpleClassTypeSignature();
- if (tokenStream[tokenIndex].equals("/")) {
- if (!brokenSignature) {
- System.err.println("[See bug 406167] Bad class file signature encountered, nested types appear package qualified, ignoring those incorrect pieces. Signature: "+inputString);
- }
- brokenSignature = true;
- // hit something like: Lcom/a/a/b/t<TK;TV;>.com/a/a/b/af.com/a/a/b/ag;
- // and we are looking at the '/' after the com
- tokenIndex++; // pointing at the next identifier
- while (tokenStream[tokenIndex+1].equals("/")) {
- tokenIndex+=2; // jump over an 'identifier' '/' pair
- }
- // now tokenIndex is the final bit of the name (which we'll treat as the inner type name)
- sig = parseSimpleClassTypeSignature();
- }
- ret.append(sig.toString());
- nestedTypeList.add(sig);
- };
- nestedTypes = new SimpleClassTypeSignature[nestedTypeList.size()];
- nestedTypeList.toArray(nestedTypes);
- return nestedTypes;
- }
-
- private SimpleClassTypeSignature parseSimpleClassTypeSignature() {
- String identifier = eatIdentifier();
- TypeArgument[] tArgs = maybeParseTypeArguments();
- if (tArgs != null) {
- return new SimpleClassTypeSignature(identifier, tArgs);
- } else {
- return new SimpleClassTypeSignature(identifier);
- }
- }
-
- private TypeArgument parseTypeArgument() {
- boolean isPlus = false;
- boolean isMinus = false;
- if (maybeEat("*")) {
- return new TypeArgument();
- } else if (maybeEat("+")) {
- isPlus = true;
- } else if (maybeEat("-")) {
- isMinus = true;
- }
- FieldTypeSignature sig = parseFieldTypeSignature(false);
- return new TypeArgument(isPlus, isMinus, sig);
- }
-
- private TypeArgument[] maybeParseTypeArguments() {
- if (maybeEat("<")) {
- List<TypeArgument> typeArgs = new ArrayList<>();
- do {
- TypeArgument arg = parseTypeArgument();
- typeArgs.add(arg);
- } while (!maybeEat(">"));
- TypeArgument[] tArgs = new TypeArgument[typeArgs.size()];
- typeArgs.toArray(tArgs);
- return tArgs;
- } else {
- return null;
- }
- }
-
- private TypeVariableSignature parseTypeVariableSignature() {
- TypeVariableSignature tv = new TypeVariableSignature(eatIdentifier());
- eat(";");
- return tv;
- }
-
- private boolean maybeEat(String token) {
- if (tokenStream.length <= tokenIndex)
- return false;
- if (tokenStream[tokenIndex].equals(token)) {
- tokenIndex++;
- return true;
- }
- return false;
- }
-
- private void eat(String token) {
- if (!tokenStream[tokenIndex].equals(token)) {
- throw new IllegalStateException("Expecting " + token + " but found " + tokenStream[tokenIndex] + " while unpacking "
- + inputString);
- }
- tokenIndex++;
- }
-
- private String eatIdentifier() {
- return tokenStream[tokenIndex++];
- }
-
- /**
- * non-private for test visibility Splits a string containing a generic signature into tokens for consumption by the parser.
- */
- public String[] tokenize(String signatureString) {
- char[] chars = signatureString.toCharArray();
- int index = 0;
- List<String> tokens = new ArrayList<>();
- StringBuilder identifier = new StringBuilder();
- boolean inParens = false;
- boolean inArray = false;
- boolean couldSeePrimitive = false;
- do {
- switch (chars[index]) {
- case '<':
- if (identifier.length() > 0)
- tokens.add(identifier.toString());
- identifier = new StringBuilder();
- tokens.add("<");
- break;
- case '>':
- if (identifier.length() > 0)
- tokens.add(identifier.toString());
- identifier = new StringBuilder();
- tokens.add(">");
- break;
- case ':':
- if (identifier.length() > 0)
- tokens.add(identifier.toString());
- identifier = new StringBuilder();
- tokens.add(":");
- break;
- case '/':
- if (identifier.length() > 0)
- tokens.add(identifier.toString());
- identifier = new StringBuilder();
- tokens.add("/");
- couldSeePrimitive = false;
- break;
- case ';':
- if (identifier.length() > 0)
- tokens.add(identifier.toString());
- identifier = new StringBuilder();
- tokens.add(";");
- couldSeePrimitive = true;
- inArray = false;
- break;
- case '^':
- if (identifier.length() > 0)
- tokens.add(identifier.toString());
- identifier = new StringBuilder();
- tokens.add("^");
- break;
- case '+':
- tokens.add("+");
- break;
- case '-':
- tokens.add("-");
- break;
- case '*':
- tokens.add("*");
- break;
- case '.':
- if (identifier.length() > 0)
- tokens.add(identifier.toString());
- identifier = new StringBuilder();
- couldSeePrimitive = false;
- tokens.add(".");
- break;
- case '(':
- tokens.add("(");
- inParens = true;
- couldSeePrimitive = true;
- break;
- case ')':
- tokens.add(")");
- inParens = false;
- break;
- case '[':
- tokens.add("[");
- couldSeePrimitive = true;
- inArray = true;
- break;
- case 'B':
- case 'C':
- case 'D':
- case 'F':
- case 'I':
- case 'J':
- case 'S':
- case 'V':
- case 'Z':
- if ((inParens || inArray) && couldSeePrimitive && identifier.length() == 0) {
- tokens.add(new String("" + chars[index]));
- } else {
- identifier.append(chars[index]);
- }
- inArray = false;
- break;
- case 'L':
- couldSeePrimitive = false;
- // deliberate fall-through
- default:
- identifier.append(chars[index]);
- }
- } while ((++index) < chars.length);
- if (identifier.length() > 0)
- tokens.add(identifier.toString());
- String[] tokenArray = new String[tokens.size()];
- tokens.toArray(tokenArray);
- return tokenArray;
- }
-
- }
|