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

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