You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GenericSignatureParser.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /* *******************************************************************
  2. * Copyright (c) 2005-2008 Contributors.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v 2.0
  6. * which accompanies this distribution and is available at
  7. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  8. *
  9. * ******************************************************************/
  10. package org.aspectj.util;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. import org.aspectj.util.GenericSignature.ArrayTypeSignature;
  14. import org.aspectj.util.GenericSignature.BaseTypeSignature;
  15. import org.aspectj.util.GenericSignature.ClassTypeSignature;
  16. import org.aspectj.util.GenericSignature.FieldTypeSignature;
  17. import org.aspectj.util.GenericSignature.FormalTypeParameter;
  18. import org.aspectj.util.GenericSignature.MethodTypeSignature;
  19. import org.aspectj.util.GenericSignature.SimpleClassTypeSignature;
  20. import org.aspectj.util.GenericSignature.TypeArgument;
  21. import org.aspectj.util.GenericSignature.TypeSignature;
  22. import org.aspectj.util.GenericSignature.TypeVariableSignature;
  23. /**
  24. * Parses the generic signature attribute as defined in the JVM spec.
  25. *
  26. * @author Adrian Colyer
  27. * @author Andy Clement
  28. */
  29. public class GenericSignatureParser {
  30. private String inputString;
  31. private String[] tokenStream; // for parse in flight
  32. private int tokenIndex = 0;
  33. /**
  34. * AMC. Parse the signature string interpreting it as a ClassSignature according to the grammar defined in Section 4.4.4 of the
  35. * JVM specification.
  36. */
  37. public GenericSignature.ClassSignature parseAsClassSignature(String sig) {
  38. this.inputString = sig;
  39. tokenStream = tokenize(sig);
  40. tokenIndex = 0;
  41. GenericSignature.ClassSignature classSig = new GenericSignature.ClassSignature();
  42. // FormalTypeParameters-opt
  43. if (maybeEat("<")) {
  44. List<FormalTypeParameter> formalTypeParametersList = new ArrayList<>();
  45. do {
  46. formalTypeParametersList.add(parseFormalTypeParameter());
  47. } while (!maybeEat(">"));
  48. classSig.formalTypeParameters = new FormalTypeParameter[formalTypeParametersList.size()];
  49. formalTypeParametersList.toArray(classSig.formalTypeParameters);
  50. }
  51. classSig.superclassSignature = parseClassTypeSignature();
  52. List<ClassTypeSignature> superIntSigs = new ArrayList<>();
  53. while (tokenIndex < tokenStream.length) {
  54. superIntSigs.add(parseClassTypeSignature());
  55. }
  56. classSig.superInterfaceSignatures = new ClassTypeSignature[superIntSigs.size()];
  57. superIntSigs.toArray(classSig.superInterfaceSignatures);
  58. return classSig;
  59. }
  60. /**
  61. * AMC. Parse the signature string interpreting it as a MethodTypeSignature according to the grammar defined in Section 4.4.4 of
  62. * the JVM specification.
  63. */
  64. public MethodTypeSignature parseAsMethodSignature(String sig) {
  65. this.inputString = sig;
  66. tokenStream = tokenize(sig);
  67. tokenIndex = 0;
  68. FormalTypeParameter[] formals = FormalTypeParameter.NONE;
  69. TypeSignature returnType = null;
  70. // FormalTypeParameters-opt
  71. if (maybeEat("<")) {
  72. List<FormalTypeParameter> formalTypeParametersList = new ArrayList<>();
  73. do {
  74. formalTypeParametersList.add(parseFormalTypeParameter());
  75. } while (!maybeEat(">"));
  76. formals = new FormalTypeParameter[formalTypeParametersList.size()];
  77. formalTypeParametersList.toArray(formals);
  78. }
  79. // Parameters
  80. eat("(");
  81. List<TypeSignature> paramList = new ArrayList<>();
  82. while (!maybeEat(")")) {
  83. FieldTypeSignature fsig = parseFieldTypeSignature(true);
  84. if (fsig != null) {
  85. paramList.add(fsig);
  86. } else {
  87. paramList.add(new GenericSignature.BaseTypeSignature(eatIdentifier()));
  88. }
  89. }
  90. TypeSignature[] params = new TypeSignature[paramList.size()];
  91. paramList.toArray(params);
  92. // return type
  93. returnType = parseFieldTypeSignature(true);
  94. if (returnType == null)
  95. returnType = new GenericSignature.BaseTypeSignature(eatIdentifier());
  96. // throws
  97. List<FieldTypeSignature> throwsList = new ArrayList<>();
  98. while (maybeEat("^")) {
  99. FieldTypeSignature fsig = parseFieldTypeSignature(false);
  100. throwsList.add(fsig);
  101. }
  102. FieldTypeSignature[] throwsSigs = new FieldTypeSignature[throwsList.size()];
  103. throwsList.toArray(throwsSigs);
  104. return new GenericSignature.MethodTypeSignature(formals, params, returnType, throwsSigs);
  105. }
  106. /**
  107. * AMC. Parse the signature string interpreting it as a FieldTypeSignature according to the grammar defined in Section 4.4.4 of
  108. * the JVM specification.
  109. */
  110. public FieldTypeSignature parseAsFieldSignature(String sig) {
  111. this.inputString = sig;
  112. tokenStream = tokenize(sig);
  113. tokenIndex = 0;
  114. return parseFieldTypeSignature(false);
  115. }
  116. private FormalTypeParameter parseFormalTypeParameter() {
  117. FormalTypeParameter ftp = new FormalTypeParameter();
  118. // Identifier
  119. ftp.identifier = eatIdentifier();
  120. // ClassBound
  121. eat(":");
  122. ftp.classBound = parseFieldTypeSignature(true);
  123. if (ftp.classBound == null) {
  124. ftp.classBound = new ClassTypeSignature("Ljava/lang/Object;", "Ljava/lang/Object");
  125. }
  126. // Optional InterfaceBounds
  127. List<FieldTypeSignature> optionalBounds = new ArrayList<>();
  128. while (maybeEat(":")) {
  129. optionalBounds.add(parseFieldTypeSignature(false));
  130. }
  131. ftp.interfaceBounds = new FieldTypeSignature[optionalBounds.size()];
  132. optionalBounds.toArray(ftp.interfaceBounds);
  133. return ftp;
  134. }
  135. private FieldTypeSignature parseFieldTypeSignature(boolean isOptional) {
  136. if (isOptional) {
  137. // anything other than 'L', 'T' or '[' and we're out of here
  138. if (!tokenStream[tokenIndex].startsWith("L") && !tokenStream[tokenIndex].startsWith("T")
  139. && !tokenStream[tokenIndex].startsWith("[")) {
  140. return null;
  141. }
  142. }
  143. if (maybeEat("[")) {
  144. return parseArrayTypeSignature();
  145. } else if (tokenStream[tokenIndex].startsWith("L")) {
  146. return parseClassTypeSignature();
  147. } else if (tokenStream[tokenIndex].startsWith("T")) {
  148. return parseTypeVariableSignature();
  149. } else {
  150. throw new IllegalStateException("Expecting [,L, or T, but found " + tokenStream[tokenIndex] + " while unpacking "
  151. + inputString);
  152. }
  153. }
  154. private ArrayTypeSignature parseArrayTypeSignature() {
  155. // opening [ already eaten
  156. FieldTypeSignature fieldType = parseFieldTypeSignature(true);
  157. if (fieldType != null) {
  158. return new ArrayTypeSignature(fieldType);
  159. } else {
  160. // must be BaseType array
  161. return new ArrayTypeSignature(new BaseTypeSignature(eatIdentifier()));
  162. }
  163. }
  164. // L PackageSpecifier* SimpleClassTypeSignature ClassTypeSignature* ;
  165. private ClassTypeSignature parseClassTypeSignature() {
  166. SimpleClassTypeSignature outerType = null;
  167. SimpleClassTypeSignature[] nestedTypes = new SimpleClassTypeSignature[0];
  168. StringBuilder ret = new StringBuilder();
  169. String identifier = eatIdentifier();
  170. ret.append(identifier);
  171. while (maybeEat("/")) {
  172. ret.append("/"); // dont forget this...
  173. ret.append(eatIdentifier());
  174. }
  175. identifier = ret.toString();
  176. // now we have either a "." indicating the start of a nested type,
  177. // or a "<" indication type arguments, or ";" and we are done.
  178. while (!maybeEat(";")) {
  179. if (tokenStream[tokenIndex].equals(".")) {
  180. // outer type completed
  181. outerType = new SimpleClassTypeSignature(identifier);
  182. nestedTypes = parseNestedTypesHelper(ret);
  183. } else if (tokenStream[tokenIndex].equals("<")) {
  184. ret.append("<");
  185. TypeArgument[] tArgs = maybeParseTypeArguments();
  186. for (TypeArgument tArg : tArgs) {
  187. ret.append(tArg.toString());
  188. }
  189. ret.append(">");
  190. outerType = new SimpleClassTypeSignature(identifier, tArgs);
  191. nestedTypes = parseNestedTypesHelper(ret);
  192. } else {
  193. throw new IllegalStateException("Expecting .,<, or ;, but found " + tokenStream[tokenIndex] + " while unpacking "
  194. + inputString);
  195. }
  196. }
  197. ret.append(";");
  198. if (outerType == null)
  199. outerType = new SimpleClassTypeSignature(ret.toString());
  200. return new ClassTypeSignature(ret.toString(), outerType, nestedTypes);
  201. }
  202. /**
  203. * Helper method to digest nested types, slightly more complex than necessary to cope with some android related
  204. * incorrect classes (see bug 406167)
  205. */
  206. private SimpleClassTypeSignature[] parseNestedTypesHelper(StringBuilder ret) {
  207. boolean brokenSignature = false;
  208. SimpleClassTypeSignature[] nestedTypes;
  209. List<SimpleClassTypeSignature> nestedTypeList = new ArrayList<>();
  210. while (maybeEat(".")) {
  211. ret.append(".");
  212. SimpleClassTypeSignature sig = parseSimpleClassTypeSignature();
  213. if (tokenStream[tokenIndex].equals("/")) {
  214. if (!brokenSignature) {
  215. System.err.println("[See bug 406167] Bad class file signature encountered, nested types appear package qualified, ignoring those incorrect pieces. Signature: "+inputString);
  216. }
  217. brokenSignature = true;
  218. // hit something like: Lcom/a/a/b/t<TK;TV;>.com/a/a/b/af.com/a/a/b/ag;
  219. // and we are looking at the '/' after the com
  220. tokenIndex++; // pointing at the next identifier
  221. while (tokenStream[tokenIndex+1].equals("/")) {
  222. tokenIndex+=2; // jump over an 'identifier' '/' pair
  223. }
  224. // now tokenIndex is the final bit of the name (which we'll treat as the inner type name)
  225. sig = parseSimpleClassTypeSignature();
  226. }
  227. ret.append(sig.toString());
  228. nestedTypeList.add(sig);
  229. };
  230. nestedTypes = new SimpleClassTypeSignature[nestedTypeList.size()];
  231. nestedTypeList.toArray(nestedTypes);
  232. return nestedTypes;
  233. }
  234. private SimpleClassTypeSignature parseSimpleClassTypeSignature() {
  235. String identifier = eatIdentifier();
  236. TypeArgument[] tArgs = maybeParseTypeArguments();
  237. if (tArgs != null) {
  238. return new SimpleClassTypeSignature(identifier, tArgs);
  239. } else {
  240. return new SimpleClassTypeSignature(identifier);
  241. }
  242. }
  243. private TypeArgument parseTypeArgument() {
  244. boolean isPlus = false;
  245. boolean isMinus = false;
  246. if (maybeEat("*")) {
  247. return new TypeArgument();
  248. } else if (maybeEat("+")) {
  249. isPlus = true;
  250. } else if (maybeEat("-")) {
  251. isMinus = true;
  252. }
  253. FieldTypeSignature sig = parseFieldTypeSignature(false);
  254. return new TypeArgument(isPlus, isMinus, sig);
  255. }
  256. private TypeArgument[] maybeParseTypeArguments() {
  257. if (maybeEat("<")) {
  258. List<TypeArgument> typeArgs = new ArrayList<>();
  259. do {
  260. TypeArgument arg = parseTypeArgument();
  261. typeArgs.add(arg);
  262. } while (!maybeEat(">"));
  263. TypeArgument[] tArgs = new TypeArgument[typeArgs.size()];
  264. typeArgs.toArray(tArgs);
  265. return tArgs;
  266. } else {
  267. return null;
  268. }
  269. }
  270. private TypeVariableSignature parseTypeVariableSignature() {
  271. TypeVariableSignature tv = new TypeVariableSignature(eatIdentifier());
  272. eat(";");
  273. return tv;
  274. }
  275. private boolean maybeEat(String token) {
  276. if (tokenStream.length <= tokenIndex)
  277. return false;
  278. if (tokenStream[tokenIndex].equals(token)) {
  279. tokenIndex++;
  280. return true;
  281. }
  282. return false;
  283. }
  284. private void eat(String token) {
  285. if (!tokenStream[tokenIndex].equals(token)) {
  286. throw new IllegalStateException("Expecting " + token + " but found " + tokenStream[tokenIndex] + " while unpacking "
  287. + inputString);
  288. }
  289. tokenIndex++;
  290. }
  291. private String eatIdentifier() {
  292. return tokenStream[tokenIndex++];
  293. }
  294. /**
  295. * non-private for test visibility Splits a string containing a generic signature into tokens for consumption by the parser.
  296. */
  297. public String[] tokenize(String signatureString) {
  298. char[] chars = signatureString.toCharArray();
  299. int index = 0;
  300. List<String> tokens = new ArrayList<>();
  301. StringBuilder identifier = new StringBuilder();
  302. boolean inParens = false;
  303. boolean inArray = false;
  304. boolean couldSeePrimitive = false;
  305. do {
  306. switch (chars[index]) {
  307. case '<':
  308. if (identifier.length() > 0)
  309. tokens.add(identifier.toString());
  310. identifier = new StringBuilder();
  311. tokens.add("<");
  312. break;
  313. case '>':
  314. if (identifier.length() > 0)
  315. tokens.add(identifier.toString());
  316. identifier = new StringBuilder();
  317. tokens.add(">");
  318. break;
  319. case ':':
  320. if (identifier.length() > 0)
  321. tokens.add(identifier.toString());
  322. identifier = new StringBuilder();
  323. tokens.add(":");
  324. break;
  325. case '/':
  326. if (identifier.length() > 0)
  327. tokens.add(identifier.toString());
  328. identifier = new StringBuilder();
  329. tokens.add("/");
  330. couldSeePrimitive = false;
  331. break;
  332. case ';':
  333. if (identifier.length() > 0)
  334. tokens.add(identifier.toString());
  335. identifier = new StringBuilder();
  336. tokens.add(";");
  337. couldSeePrimitive = true;
  338. inArray = false;
  339. break;
  340. case '^':
  341. if (identifier.length() > 0)
  342. tokens.add(identifier.toString());
  343. identifier = new StringBuilder();
  344. tokens.add("^");
  345. break;
  346. case '+':
  347. tokens.add("+");
  348. break;
  349. case '-':
  350. tokens.add("-");
  351. break;
  352. case '*':
  353. tokens.add("*");
  354. break;
  355. case '.':
  356. if (identifier.length() > 0)
  357. tokens.add(identifier.toString());
  358. identifier = new StringBuilder();
  359. couldSeePrimitive = false;
  360. tokens.add(".");
  361. break;
  362. case '(':
  363. tokens.add("(");
  364. inParens = true;
  365. couldSeePrimitive = true;
  366. break;
  367. case ')':
  368. tokens.add(")");
  369. inParens = false;
  370. break;
  371. case '[':
  372. tokens.add("[");
  373. couldSeePrimitive = true;
  374. inArray = true;
  375. break;
  376. case 'B':
  377. case 'C':
  378. case 'D':
  379. case 'F':
  380. case 'I':
  381. case 'J':
  382. case 'S':
  383. case 'V':
  384. case 'Z':
  385. if ((inParens || inArray) && couldSeePrimitive && identifier.length() == 0) {
  386. tokens.add(new String("" + chars[index]));
  387. } else {
  388. identifier.append(chars[index]);
  389. }
  390. inArray = false;
  391. break;
  392. case 'L':
  393. couldSeePrimitive = false;
  394. // deliberate fall-through
  395. default:
  396. identifier.append(chars[index]);
  397. }
  398. } while ((++index) < chars.length);
  399. if (identifier.length() > 0)
  400. tokens.add(identifier.toString());
  401. String[] tokenArray = new String[tokens.size()];
  402. tokens.toArray(tokenArray);
  403. return tokenArray;
  404. }
  405. }