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.

Descriptor.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. Alternatively, the contents of this file may be used under
  8. * the terms of the GNU Lesser General Public License Version 2.1 or later,
  9. * or the Apache License Version 2.0.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. */
  16. package javassist.bytecode;
  17. import java.util.Map;
  18. import javassist.ClassPool;
  19. import javassist.CtClass;
  20. import javassist.CtPrimitiveType;
  21. import javassist.NotFoundException;
  22. /**
  23. * A support class for dealing with descriptors.
  24. *
  25. * <p>See chapter 4.3 in "The Java Virtual Machine Specification (2nd ed.)"
  26. */
  27. public class Descriptor {
  28. /**
  29. * Converts a class name into the internal representation used in
  30. * the JVM.
  31. *
  32. * <p>Note that <code>toJvmName(toJvmName(s))</code> is equivalent
  33. * to <code>toJvmName(s)</code>.
  34. */
  35. public static String toJvmName(String classname) {
  36. return classname.replace('.', '/');
  37. }
  38. /**
  39. * Converts a class name from the internal representation used in
  40. * the JVM to the normal one used in Java.
  41. * This method does not deal with an array type name such as
  42. * "[Ljava/lang/Object;" and "[I;". For such names, use
  43. * <code>toClassName()</code>.
  44. *
  45. * @see #toClassName(String)
  46. */
  47. public static String toJavaName(String classname) {
  48. return classname.replace('/', '.');
  49. }
  50. /**
  51. * Returns the internal representation of the class name in the
  52. * JVM.
  53. */
  54. public static String toJvmName(CtClass clazz) {
  55. if (clazz.isArray())
  56. return of(clazz);
  57. return toJvmName(clazz.getName());
  58. }
  59. /**
  60. * Converts to a Java class name from a descriptor.
  61. *
  62. * @param descriptor type descriptor.
  63. */
  64. public static String toClassName(String descriptor) {
  65. int arrayDim = 0;
  66. int i = 0;
  67. char c = descriptor.charAt(0);
  68. while (c == '[') {
  69. ++arrayDim;
  70. c = descriptor.charAt(++i);
  71. }
  72. String name;
  73. if (c == 'L') {
  74. int i2 = descriptor.indexOf(';', i++);
  75. name = descriptor.substring(i, i2).replace('/', '.');
  76. i = i2;
  77. }
  78. else if (c == 'V')
  79. name = "void";
  80. else if (c == 'I')
  81. name = "int";
  82. else if (c == 'B')
  83. name = "byte";
  84. else if (c == 'J')
  85. name = "long";
  86. else if (c == 'D')
  87. name = "double";
  88. else if (c == 'F')
  89. name = "float";
  90. else if (c == 'C')
  91. name = "char";
  92. else if (c == 'S')
  93. name = "short";
  94. else if (c == 'Z')
  95. name = "boolean";
  96. else
  97. throw new RuntimeException("bad descriptor: " + descriptor);
  98. if (i + 1 != descriptor.length())
  99. throw new RuntimeException("multiple descriptors?: " + descriptor);
  100. if (arrayDim == 0)
  101. return name;
  102. StringBuilder sbuf = new StringBuilder(name);
  103. do {
  104. sbuf.append("[]");
  105. } while (--arrayDim > 0);
  106. return sbuf.toString();
  107. }
  108. /**
  109. * Converts to a descriptor from a Java class name
  110. */
  111. public static String of(String classname) {
  112. if (classname.equals("void"))
  113. return "V";
  114. else if (classname.equals("int"))
  115. return "I";
  116. else if (classname.equals("byte"))
  117. return "B";
  118. else if (classname.equals("long"))
  119. return "J";
  120. else if (classname.equals("double"))
  121. return "D";
  122. else if (classname.equals("float"))
  123. return "F";
  124. else if (classname.equals("char"))
  125. return "C";
  126. else if (classname.equals("short"))
  127. return "S";
  128. else if (classname.equals("boolean"))
  129. return "Z";
  130. else
  131. return "L" + toJvmName(classname) + ";";
  132. }
  133. /**
  134. * Substitutes a class name
  135. * in the given descriptor string.
  136. *
  137. * @param desc descriptor string
  138. * @param oldname replaced JVM class name
  139. * @param newname substituted JVM class name
  140. *
  141. * @see Descriptor#toJvmName(String)
  142. */
  143. public static String rename(String desc, String oldname, String newname) {
  144. if (desc.indexOf(oldname) < 0)
  145. return desc;
  146. StringBuilder newdesc = new StringBuilder();
  147. int head = 0;
  148. int i = 0;
  149. for (;;) {
  150. int j = desc.indexOf('L', i);
  151. if (j < 0)
  152. break;
  153. else if (desc.startsWith(oldname, j + 1)
  154. && desc.charAt(j + oldname.length() + 1) == ';') {
  155. newdesc.append(desc.substring(head, j));
  156. newdesc.append('L');
  157. newdesc.append(newname);
  158. newdesc.append(';');
  159. head = i = j + oldname.length() + 2;
  160. }
  161. else {
  162. i = desc.indexOf(';', j) + 1;
  163. if (i < 1)
  164. break; // ';' was not found.
  165. }
  166. }
  167. if (head == 0)
  168. return desc;
  169. int len = desc.length();
  170. if (head < len)
  171. newdesc.append(desc.substring(head, len));
  172. return newdesc.toString();
  173. }
  174. /**
  175. * Substitutes class names and generics in the given descriptor string
  176. * according to the given <code>map</code>.
  177. *
  178. * @param map a map between replaced and substituted
  179. * JVM class names.
  180. * @see Descriptor#toJvmName(String)
  181. */
  182. public static String renameIncludeGenerics(String desc, Map<String, String> map) {
  183. if (map == null)
  184. return desc;
  185. StringBuilder newdesc = new StringBuilder();
  186. int head = 0;
  187. int i = 0;
  188. for (; ; ) {
  189. int j = desc.indexOf('L', i);
  190. if (j < 0)
  191. break;
  192. int x = desc.indexOf('<', j);
  193. int y = desc.indexOf(';', j);
  194. if (x == y)
  195. break;
  196. int k = x == -1 ? y : 0;
  197. if (k == 0)
  198. k = Math.min(x,y);
  199. i = k + 1;
  200. String name = desc.substring(j + 1, k);
  201. String name2 = map.get(name);
  202. if (name2 != null) {
  203. newdesc.append(desc.substring(head, j));
  204. newdesc.append('L');
  205. newdesc.append(name2);
  206. newdesc.append(';');
  207. head = i;
  208. }
  209. }
  210. if (head == 0)
  211. return desc;
  212. int len = desc.length();
  213. if (head < len)
  214. newdesc.append(desc.substring(head, len));
  215. return newdesc.toString();
  216. }
  217. /**
  218. * Substitutes class names in the given descriptor string
  219. * according to the given <code>map</code>.
  220. *
  221. * @param map a map between replaced and substituted
  222. * JVM class names.
  223. * @see Descriptor#toJvmName(String)
  224. */
  225. public static String rename(String desc, Map<String,String> map) {
  226. if (map == null)
  227. return desc;
  228. StringBuilder newdesc = new StringBuilder();
  229. int head = 0;
  230. int i = 0;
  231. for (;;) {
  232. int j = desc.indexOf('L', i);
  233. if (j < 0)
  234. break;
  235. int k = desc.indexOf(';', j);
  236. if (k < 0)
  237. break;
  238. i = k + 1;
  239. String name = desc.substring(j + 1, k);
  240. String name2 = map.get(name);
  241. if (name2 != null) {
  242. newdesc.append(desc.substring(head, j));
  243. newdesc.append('L');
  244. newdesc.append(name2);
  245. newdesc.append(';');
  246. head = i;
  247. }
  248. }
  249. if (head == 0)
  250. return desc;
  251. int len = desc.length();
  252. if (head < len)
  253. newdesc.append(desc.substring(head, len));
  254. return newdesc.toString();
  255. }
  256. /**
  257. * Returns the descriptor representing the given type.
  258. */
  259. public static String of(CtClass type) {
  260. StringBuilder sbuf = new StringBuilder();
  261. toDescriptor(sbuf, type);
  262. return sbuf.toString();
  263. }
  264. private static void toDescriptor(StringBuilder desc, CtClass type) {
  265. if (type.isArray()) {
  266. desc.append('[');
  267. try {
  268. toDescriptor(desc, type.getComponentType());
  269. }
  270. catch (NotFoundException e) {
  271. desc.append('L');
  272. String name = type.getName();
  273. desc.append(toJvmName(name.substring(0, name.length() - 2)));
  274. desc.append(';');
  275. }
  276. }
  277. else if (type.isPrimitive()) {
  278. CtPrimitiveType pt = (CtPrimitiveType)type;
  279. desc.append(pt.getDescriptor());
  280. }
  281. else { // class type
  282. desc.append('L');
  283. desc.append(type.getName().replace('.', '/'));
  284. desc.append(';');
  285. }
  286. }
  287. /**
  288. * Returns the descriptor representing a constructor receiving
  289. * the given parameter types.
  290. *
  291. * @param paramTypes parameter types
  292. */
  293. public static String ofConstructor(CtClass[] paramTypes) {
  294. return ofMethod(CtClass.voidType, paramTypes);
  295. }
  296. /**
  297. * Returns the descriptor representing a method that receives
  298. * the given parameter types and returns the given type.
  299. *
  300. * @param returnType return type
  301. * @param paramTypes parameter types
  302. */
  303. public static String ofMethod(CtClass returnType, CtClass[] paramTypes) {
  304. StringBuilder desc = new StringBuilder();
  305. desc.append('(');
  306. if (paramTypes != null) {
  307. int n = paramTypes.length;
  308. for (int i = 0; i < n; ++i)
  309. toDescriptor(desc, paramTypes[i]);
  310. }
  311. desc.append(')');
  312. if (returnType != null)
  313. toDescriptor(desc, returnType);
  314. return desc.toString();
  315. }
  316. /**
  317. * Returns the descriptor representing a list of parameter types.
  318. * For example, if the given parameter types are two <code>int</code>,
  319. * then this method returns <code>"(II)"</code>.
  320. *
  321. * @param paramTypes parameter types
  322. */
  323. public static String ofParameters(CtClass[] paramTypes) {
  324. return ofMethod(null, paramTypes);
  325. }
  326. /**
  327. * Appends a parameter type to the parameter list represented
  328. * by the given descriptor.
  329. *
  330. * <p><code>classname</code> must not be an array type.
  331. *
  332. * @param classname parameter type (not primitive type)
  333. * @param desc descriptor
  334. */
  335. public static String appendParameter(String classname, String desc) {
  336. int i = desc.indexOf(')');
  337. if (i < 0)
  338. return desc;
  339. StringBuilder newdesc = new StringBuilder();
  340. newdesc.append(desc.substring(0, i));
  341. newdesc.append('L');
  342. newdesc.append(classname.replace('.', '/'));
  343. newdesc.append(';');
  344. newdesc.append(desc.substring(i));
  345. return newdesc.toString();
  346. }
  347. /**
  348. * Inserts a parameter type at the beginning of the parameter
  349. * list represented
  350. * by the given descriptor.
  351. *
  352. * <p><code>classname</code> must not be an array type.
  353. *
  354. * @param classname parameter type (not primitive type)
  355. * @param desc descriptor
  356. */
  357. public static String insertParameter(String classname, String desc) {
  358. if (desc.charAt(0) != '(')
  359. return desc;
  360. return "(L" + classname.replace('.', '/') + ';'
  361. + desc.substring(1);
  362. }
  363. /**
  364. * Appends a parameter type to the parameter list represented
  365. * by the given descriptor. The appended parameter becomes
  366. * the last parameter.
  367. *
  368. * @param type the type of the appended parameter.
  369. * @param descriptor the original descriptor.
  370. */
  371. public static String appendParameter(CtClass type, String descriptor) {
  372. int i = descriptor.indexOf(')');
  373. if (i < 0)
  374. return descriptor;
  375. StringBuilder newdesc = new StringBuilder();
  376. newdesc.append(descriptor.substring(0, i));
  377. toDescriptor(newdesc, type);
  378. newdesc.append(descriptor.substring(i));
  379. return newdesc.toString();
  380. }
  381. /**
  382. * Inserts a parameter type at the beginning of the parameter
  383. * list represented
  384. * by the given descriptor.
  385. *
  386. * @param type the type of the inserted parameter.
  387. * @param descriptor the descriptor of the method.
  388. */
  389. public static String insertParameter(CtClass type,
  390. String descriptor) {
  391. if (descriptor.charAt(0) != '(')
  392. return descriptor;
  393. return "(" + of(type) + descriptor.substring(1);
  394. }
  395. /**
  396. * Changes the return type included in the given descriptor.
  397. *
  398. * <p><code>classname</code> must not be an array type.
  399. *
  400. * @param classname return type
  401. * @param desc descriptor
  402. */
  403. public static String changeReturnType(String classname, String desc) {
  404. int i = desc.indexOf(')');
  405. if (i < 0)
  406. return desc;
  407. StringBuilder newdesc = new StringBuilder();
  408. newdesc.append(desc.substring(0, i + 1));
  409. newdesc.append('L');
  410. newdesc.append(classname.replace('.', '/'));
  411. newdesc.append(';');
  412. return newdesc.toString();
  413. }
  414. /**
  415. * Returns the <code>CtClass</code> objects representing the parameter
  416. * types specified by the given descriptor.
  417. *
  418. * @param desc descriptor
  419. * @param cp the class pool used for obtaining
  420. * a <code>CtClass</code> object.
  421. */
  422. public static CtClass[] getParameterTypes(String desc, ClassPool cp)
  423. throws NotFoundException
  424. {
  425. if (desc.charAt(0) != '(')
  426. return null;
  427. int num = numOfParameters(desc);
  428. CtClass[] args = new CtClass[num];
  429. int n = 0;
  430. int i = 1;
  431. do {
  432. i = toCtClass(cp, desc, i, args, n++);
  433. } while (i > 0);
  434. return args;
  435. }
  436. /**
  437. * Returns true if the list of the parameter types of desc1 is equal to
  438. * that of desc2.
  439. * For example, "(II)V" and "(II)I" are equal.
  440. */
  441. public static boolean eqParamTypes(String desc1, String desc2) {
  442. if (desc1.charAt(0) != '(')
  443. return false;
  444. for (int i = 0; true; ++i) {
  445. char c = desc1.charAt(i);
  446. if (c != desc2.charAt(i))
  447. return false;
  448. if (c == ')')
  449. return true;
  450. }
  451. }
  452. /**
  453. * Returns the signature of the given descriptor. The signature does
  454. * not include the return type. For example, the signature of "(I)V"
  455. * is "(I)".
  456. */
  457. public static String getParamDescriptor(String decl) {
  458. return decl.substring(0, decl.indexOf(')') + 1);
  459. }
  460. /**
  461. * Returns the <code>CtClass</code> object representing the return
  462. * type specified by the given descriptor.
  463. *
  464. * @param desc descriptor
  465. * @param cp the class pool used for obtaining
  466. * a <code>CtClass</code> object.
  467. */
  468. public static CtClass getReturnType(String desc, ClassPool cp)
  469. throws NotFoundException
  470. {
  471. int i = desc.indexOf(')');
  472. if (i < 0)
  473. return null;
  474. CtClass[] type = new CtClass[1];
  475. toCtClass(cp, desc, i + 1, type, 0);
  476. return type[0];
  477. }
  478. /**
  479. * Returns the number of the prameters included in the given
  480. * descriptor.
  481. *
  482. * @param desc descriptor
  483. */
  484. public static int numOfParameters(String desc) {
  485. int n = 0;
  486. int i = 1;
  487. for (;;) {
  488. char c = desc.charAt(i);
  489. if (c == ')')
  490. break;
  491. while (c == '[')
  492. c = desc.charAt(++i);
  493. if (c == 'L') {
  494. i = desc.indexOf(';', i) + 1;
  495. if (i <= 0)
  496. throw new IndexOutOfBoundsException("bad descriptor");
  497. }
  498. else
  499. ++i;
  500. ++n;
  501. }
  502. return n;
  503. }
  504. /**
  505. * Returns a <code>CtClass</code> object representing the type
  506. * specified by the given descriptor.
  507. *
  508. * <p>This method works even if the package-class separator is
  509. * not <code>/</code> but <code>.</code> (period). For example,
  510. * it accepts <code>Ljava.lang.Object;</code>
  511. * as well as <code>Ljava/lang/Object;</code>.
  512. *
  513. * @param desc descriptor.
  514. * @param cp the class pool used for obtaining
  515. * a <code>CtClass</code> object.
  516. */
  517. public static CtClass toCtClass(String desc, ClassPool cp)
  518. throws NotFoundException
  519. {
  520. CtClass[] clazz = new CtClass[1];
  521. int res = toCtClass(cp, desc, 0, clazz, 0);
  522. if (res >= 0)
  523. return clazz[0];
  524. // maybe, you forgot to surround the class name with
  525. // L and ;. It violates the protocol, but I'm tolerant...
  526. return cp.get(desc.replace('/', '.'));
  527. }
  528. private static int toCtClass(ClassPool cp, String desc, int i,
  529. CtClass[] args, int n)
  530. throws NotFoundException
  531. {
  532. int i2;
  533. String name;
  534. int arrayDim = 0;
  535. char c = desc.charAt(i);
  536. while (c == '[') {
  537. ++arrayDim;
  538. c = desc.charAt(++i);
  539. }
  540. if (c == 'L') {
  541. i2 = desc.indexOf(';', ++i);
  542. name = desc.substring(i, i2++).replace('/', '.');
  543. }
  544. else {
  545. CtClass type = toPrimitiveClass(c);
  546. if (type == null)
  547. return -1; // error
  548. i2 = i + 1;
  549. if (arrayDim == 0) {
  550. args[n] = type;
  551. return i2; // neither an array type or a class type
  552. }
  553. name = type.getName();
  554. }
  555. if (arrayDim > 0) {
  556. StringBuilder sbuf = new StringBuilder(name);
  557. while (arrayDim-- > 0)
  558. sbuf.append("[]");
  559. name = sbuf.toString();
  560. }
  561. args[n] = cp.get(name);
  562. return i2;
  563. }
  564. static CtClass toPrimitiveClass(char c) {
  565. CtClass type = null;
  566. switch (c) {
  567. case 'Z' :
  568. type = CtClass.booleanType;
  569. break;
  570. case 'C' :
  571. type = CtClass.charType;
  572. break;
  573. case 'B' :
  574. type = CtClass.byteType;
  575. break;
  576. case 'S' :
  577. type = CtClass.shortType;
  578. break;
  579. case 'I' :
  580. type = CtClass.intType;
  581. break;
  582. case 'J' :
  583. type = CtClass.longType;
  584. break;
  585. case 'F' :
  586. type = CtClass.floatType;
  587. break;
  588. case 'D' :
  589. type = CtClass.doubleType;
  590. break;
  591. case 'V' :
  592. type = CtClass.voidType;
  593. break;
  594. }
  595. return type;
  596. }
  597. /**
  598. * Computes the dimension of the array represented by the given
  599. * descriptor. For example, if the descriptor is <code>"[[I"</code>,
  600. * then this method returns 2.
  601. *
  602. * @param desc the descriptor.
  603. * @return 0 if the descriptor does not represent an array type.
  604. */
  605. public static int arrayDimension(String desc) {
  606. int dim = 0;
  607. while (desc.charAt(dim) == '[')
  608. ++dim;
  609. return dim;
  610. }
  611. /**
  612. * Returns the descriptor of the type of the array component.
  613. * For example, if the given descriptor is
  614. * <code>"[[Ljava/lang/String;"</code> and the given dimension is 2,
  615. * then this method returns <code>"Ljava/lang/String;"</code>.
  616. *
  617. * @param desc the descriptor.
  618. * @param dim the array dimension.
  619. */
  620. public static String toArrayComponent(String desc, int dim) {
  621. return desc.substring(dim);
  622. }
  623. /**
  624. * Computes the data size specified by the given descriptor.
  625. * For example, if the descriptor is "D", this method returns 2.
  626. *
  627. * <p>If the descriptor represents a method type, this method returns
  628. * (the size of the returned value) - (the sum of the data sizes
  629. * of all the parameters). For example, if the descriptor is
  630. * <code>"(I)D"</code>, then this method returns 1 (= 2 - 1).
  631. *
  632. * @param desc descriptor
  633. */
  634. public static int dataSize(String desc) {
  635. return dataSize(desc, true);
  636. }
  637. /**
  638. * Computes the data size of parameters.
  639. * If one of the parameters is double type, the size of that parameter
  640. * is 2 words. For example, if the given descriptor is
  641. * <code>"(IJ)D"</code>, then this method returns 3. The size of the
  642. * return type is not computed.
  643. *
  644. * @param desc a method descriptor.
  645. */
  646. public static int paramSize(String desc) {
  647. return -dataSize(desc, false);
  648. }
  649. private static int dataSize(String desc, boolean withRet) {
  650. int n = 0;
  651. char c = desc.charAt(0);
  652. if (c == '(') {
  653. int i = 1;
  654. for (;;) {
  655. c = desc.charAt(i);
  656. if (c == ')') {
  657. c = desc.charAt(i + 1);
  658. break;
  659. }
  660. boolean array = false;
  661. while (c == '[') {
  662. array = true;
  663. c = desc.charAt(++i);
  664. }
  665. if (c == 'L') {
  666. i = desc.indexOf(';', i) + 1;
  667. if (i <= 0)
  668. throw new IndexOutOfBoundsException("bad descriptor");
  669. }
  670. else
  671. ++i;
  672. if (!array && (c == 'J' || c == 'D'))
  673. n -= 2;
  674. else
  675. --n;
  676. }
  677. }
  678. if (withRet)
  679. if (c == 'J' || c == 'D')
  680. n += 2;
  681. else if (c != 'V')
  682. ++n;
  683. return n;
  684. }
  685. /**
  686. * Returns a human-readable representation of the
  687. * given descriptor. For example, <code>Ljava/lang/Object;</code>
  688. * is converted into <code>java.lang.Object</code>.
  689. * <code>(I[I)V</code> is converted into <code>(int, int[])</code>
  690. * (the return type is ignored).
  691. */
  692. public static String toString(String desc) {
  693. return PrettyPrinter.toString(desc);
  694. }
  695. static class PrettyPrinter {
  696. static String toString(String desc) {
  697. StringBuilder sbuf = new StringBuilder();
  698. if (desc.charAt(0) == '(') {
  699. int pos = 1;
  700. sbuf.append('(');
  701. while (desc.charAt(pos) != ')') {
  702. if (pos > 1)
  703. sbuf.append(',');
  704. pos = readType(sbuf, pos, desc);
  705. }
  706. sbuf.append(')');
  707. }
  708. else
  709. readType(sbuf, 0, desc);
  710. return sbuf.toString();
  711. }
  712. static int readType(StringBuilder sbuf, int pos, String desc) {
  713. char c = desc.charAt(pos);
  714. int arrayDim = 0;
  715. while (c == '[') {
  716. arrayDim++;
  717. c = desc.charAt(++pos);
  718. }
  719. if (c == 'L')
  720. while (true) {
  721. c = desc.charAt(++pos);
  722. if (c == ';')
  723. break;
  724. if (c == '/')
  725. c = '.';
  726. sbuf.append(c);
  727. }
  728. else {
  729. CtClass t = toPrimitiveClass(c);
  730. sbuf.append(t.getName());
  731. }
  732. while (arrayDim-- > 0)
  733. sbuf.append("[]");
  734. return pos + 1;
  735. }
  736. }
  737. /**
  738. * An Iterator over a descriptor.
  739. */
  740. public static class Iterator {
  741. private String desc;
  742. private int index, curPos;
  743. private boolean param;
  744. /**
  745. * Constructs an iterator.
  746. *
  747. * @param s descriptor.
  748. */
  749. public Iterator(String s) {
  750. desc = s;
  751. index = curPos = 0;
  752. param = false;
  753. }
  754. /**
  755. * Returns true if the iteration has more elements.
  756. */
  757. public boolean hasNext() {
  758. return index < desc.length();
  759. }
  760. /**
  761. * Returns true if the current element is a parameter type.
  762. */
  763. public boolean isParameter() { return param; }
  764. /**
  765. * Returns the first character of the current element.
  766. */
  767. public char currentChar() { return desc.charAt(curPos); }
  768. /**
  769. * Returns true if the current element is double or long type.
  770. */
  771. public boolean is2byte() {
  772. char c = currentChar();
  773. return c == 'D' || c == 'J';
  774. }
  775. /**
  776. * Returns the position of the next type character.
  777. * That type character becomes a new current element.
  778. */
  779. public int next() {
  780. int nextPos = index;
  781. char c = desc.charAt(nextPos);
  782. if (c == '(') {
  783. ++index;
  784. c = desc.charAt(++nextPos);
  785. param = true;
  786. }
  787. if (c == ')') {
  788. ++index;
  789. c = desc.charAt(++nextPos);
  790. param = false;
  791. }
  792. while (c == '[')
  793. c = desc.charAt(++nextPos);
  794. if (c == 'L') {
  795. nextPos = desc.indexOf(';', nextPos) + 1;
  796. if (nextPos <= 0)
  797. throw new IndexOutOfBoundsException("bad descriptor");
  798. }
  799. else
  800. ++nextPos;
  801. curPos = index;
  802. index = nextPos;
  803. return curPos;
  804. }
  805. }
  806. }