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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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 v1.0
  6. * which accompanies this distribution and is available at
  7. * http://eclipse.org/legal/epl-v10.html
  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<FormalTypeParameter>();
  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<ClassTypeSignature>();
  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 = new FormalTypeParameter[0];
  69. TypeSignature returnType = null;
  70. // FormalTypeParameters-opt
  71. if (maybeEat("<")) {
  72. List<FormalTypeParameter> formalTypeParametersList = new ArrayList<FormalTypeParameter>();
  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<TypeSignature>();
  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<FieldTypeSignature>();
  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<FieldTypeSignature>();
  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. StringBuffer ret = new StringBuffer();
  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 (maybeEat(".")) {
  180. // outer type completed
  181. outerType = new SimpleClassTypeSignature(identifier);
  182. List<SimpleClassTypeSignature> nestedTypeList = new ArrayList<SimpleClassTypeSignature>();
  183. do {
  184. ret.append(".");
  185. SimpleClassTypeSignature sig = parseSimpleClassTypeSignature();
  186. ret.append(sig.toString());
  187. nestedTypeList.add(sig);
  188. } while (maybeEat("."));
  189. nestedTypes = new SimpleClassTypeSignature[nestedTypeList.size()];
  190. nestedTypeList.toArray(nestedTypes);
  191. } else if (tokenStream[tokenIndex].equals("<")) {
  192. ret.append("<");
  193. TypeArgument[] tArgs = maybeParseTypeArguments();
  194. for (int i = 0; i < tArgs.length; i++) {
  195. ret.append(tArgs[i].toString());
  196. }
  197. ret.append(">");
  198. outerType = new SimpleClassTypeSignature(identifier, tArgs);
  199. // now parse possible nesteds...
  200. List<SimpleClassTypeSignature> nestedTypeList = new ArrayList<SimpleClassTypeSignature>();
  201. while (maybeEat(".")) {
  202. ret.append(".");
  203. SimpleClassTypeSignature sig = parseSimpleClassTypeSignature();
  204. ret.append(sig.toString());
  205. nestedTypeList.add(sig);
  206. }
  207. nestedTypes = new SimpleClassTypeSignature[nestedTypeList.size()];
  208. nestedTypeList.toArray(nestedTypes);
  209. } else {
  210. throw new IllegalStateException("Expecting .,<, or ;, but found " + tokenStream[tokenIndex] + " while unpacking "
  211. + inputString);
  212. }
  213. }
  214. ret.append(";");
  215. if (outerType == null)
  216. outerType = new SimpleClassTypeSignature(ret.toString());
  217. return new ClassTypeSignature(ret.toString(), outerType, nestedTypes);
  218. }
  219. private SimpleClassTypeSignature parseSimpleClassTypeSignature() {
  220. String identifier = eatIdentifier();
  221. TypeArgument[] tArgs = maybeParseTypeArguments();
  222. if (tArgs != null) {
  223. return new SimpleClassTypeSignature(identifier, tArgs);
  224. } else {
  225. return new SimpleClassTypeSignature(identifier);
  226. }
  227. }
  228. private TypeArgument parseTypeArgument() {
  229. boolean isPlus = false;
  230. boolean isMinus = false;
  231. if (maybeEat("*")) {
  232. return new TypeArgument();
  233. } else if (maybeEat("+")) {
  234. isPlus = true;
  235. } else if (maybeEat("-")) {
  236. isMinus = true;
  237. }
  238. FieldTypeSignature sig = parseFieldTypeSignature(false);
  239. return new TypeArgument(isPlus, isMinus, sig);
  240. }
  241. private TypeArgument[] maybeParseTypeArguments() {
  242. if (maybeEat("<")) {
  243. List<TypeArgument> typeArgs = new ArrayList<TypeArgument>();
  244. do {
  245. TypeArgument arg = parseTypeArgument();
  246. typeArgs.add(arg);
  247. } while (!maybeEat(">"));
  248. TypeArgument[] tArgs = new TypeArgument[typeArgs.size()];
  249. typeArgs.toArray(tArgs);
  250. return tArgs;
  251. } else {
  252. return null;
  253. }
  254. }
  255. private TypeVariableSignature parseTypeVariableSignature() {
  256. TypeVariableSignature tv = new TypeVariableSignature(eatIdentifier());
  257. eat(";");
  258. return tv;
  259. }
  260. private boolean maybeEat(String token) {
  261. if (tokenStream.length <= tokenIndex)
  262. return false;
  263. if (tokenStream[tokenIndex].equals(token)) {
  264. tokenIndex++;
  265. return true;
  266. }
  267. return false;
  268. }
  269. private void eat(String token) {
  270. if (!tokenStream[tokenIndex].equals(token)) {
  271. throw new IllegalStateException("Expecting " + token + " but found " + tokenStream[tokenIndex] + " while unpacking "
  272. + inputString);
  273. }
  274. tokenIndex++;
  275. }
  276. private String eatIdentifier() {
  277. return tokenStream[tokenIndex++];
  278. }
  279. /**
  280. * non-private for test visibility Splits a string containing a generic signature into tokens for consumption by the parser.
  281. */
  282. public String[] tokenize(String signatureString) {
  283. char[] chars = signatureString.toCharArray();
  284. int index = 0;
  285. List<String> tokens = new ArrayList<String>();
  286. StringBuffer identifier = new StringBuffer();
  287. boolean inParens = false;
  288. boolean inArray = false;
  289. boolean couldSeePrimitive = false;
  290. do {
  291. switch (chars[index]) {
  292. case '<':
  293. if (identifier.length() > 0)
  294. tokens.add(identifier.toString());
  295. identifier = new StringBuffer();
  296. tokens.add("<");
  297. break;
  298. case '>':
  299. if (identifier.length() > 0)
  300. tokens.add(identifier.toString());
  301. identifier = new StringBuffer();
  302. tokens.add(">");
  303. break;
  304. case ':':
  305. if (identifier.length() > 0)
  306. tokens.add(identifier.toString());
  307. identifier = new StringBuffer();
  308. tokens.add(":");
  309. break;
  310. case '/':
  311. if (identifier.length() > 0)
  312. tokens.add(identifier.toString());
  313. identifier = new StringBuffer();
  314. tokens.add("/");
  315. couldSeePrimitive = false;
  316. break;
  317. case ';':
  318. if (identifier.length() > 0)
  319. tokens.add(identifier.toString());
  320. identifier = new StringBuffer();
  321. tokens.add(";");
  322. couldSeePrimitive = true;
  323. inArray = false;
  324. break;
  325. case '^':
  326. if (identifier.length() > 0)
  327. tokens.add(identifier.toString());
  328. identifier = new StringBuffer();
  329. tokens.add("^");
  330. break;
  331. case '+':
  332. tokens.add("+");
  333. break;
  334. case '-':
  335. tokens.add("-");
  336. break;
  337. case '*':
  338. tokens.add("*");
  339. break;
  340. case '.':
  341. if (identifier.length() > 0)
  342. tokens.add(identifier.toString());
  343. identifier = new StringBuffer();
  344. couldSeePrimitive = false;
  345. tokens.add(".");
  346. break;
  347. case '(':
  348. tokens.add("(");
  349. inParens = true;
  350. couldSeePrimitive = true;
  351. break;
  352. case ')':
  353. tokens.add(")");
  354. inParens = false;
  355. break;
  356. case '[':
  357. tokens.add("[");
  358. couldSeePrimitive = true;
  359. inArray = true;
  360. break;
  361. case 'B':
  362. case 'C':
  363. case 'D':
  364. case 'F':
  365. case 'I':
  366. case 'J':
  367. case 'S':
  368. case 'V':
  369. case 'Z':
  370. if ((inParens || inArray) && couldSeePrimitive && identifier.length() == 0) {
  371. tokens.add(new String("" + chars[index]));
  372. } else {
  373. identifier.append(chars[index]);
  374. }
  375. inArray = false;
  376. break;
  377. case 'L':
  378. couldSeePrimitive = false;
  379. // deliberate fall-through
  380. default:
  381. identifier.append(chars[index]);
  382. }
  383. } while ((++index) < chars.length);
  384. if (identifier.length() > 0)
  385. tokens.add(identifier.toString());
  386. String[] tokenArray = new String[tokens.size()];
  387. tokens.toArray(tokenArray);
  388. return tokenArray;
  389. }
  390. }