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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999-2004 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 java.util.Map;
  17. import javassist.CtClass;
  18. import javassist.CtPrimitiveType;
  19. import javassist.ClassPool;
  20. import javassist.NotFoundException;
  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. public static String toClassName(String descriptor) {
  58. String newname = toJavaName(descriptor);
  59. return newname.substring(1, newname.length() - 1);
  60. }
  61. /**
  62. * Converts to a descriptor from a Java class name
  63. */
  64. public static String of(String classname) {
  65. return "L" + toJvmName(classname) + ";";
  66. }
  67. /**
  68. * Substitutes a class name
  69. * in the given descriptor string.
  70. *
  71. * @param desc descriptor string
  72. * @param oldname replaced JVM class name
  73. * @param newname substituted JVM class name
  74. *
  75. * @see Descriptor#toJvmName(String)
  76. */
  77. public static String rename(String desc,
  78. String oldname, String newname) {
  79. if (desc.indexOf(oldname) < 0)
  80. return desc;
  81. StringBuffer newdesc = new StringBuffer();
  82. int head = 0;
  83. int i = 0;
  84. for (;;) {
  85. int j = desc.indexOf('L', i);
  86. if (j < 0)
  87. break;
  88. else if (desc.startsWith(oldname, j + 1)
  89. && desc.charAt(j + oldname.length() + 1) == ';') {
  90. newdesc.append(desc.substring(head, j));
  91. newdesc.append('L');
  92. newdesc.append(newname);
  93. newdesc.append(';');
  94. head = i = j + oldname.length() + 2;
  95. }
  96. else {
  97. i = desc.indexOf(';', j) + 1;
  98. if (i < 1)
  99. break; // ';' was not found.
  100. }
  101. }
  102. if (head == 0)
  103. return desc;
  104. else {
  105. int len = desc.length();
  106. if (head < len)
  107. newdesc.append(desc.substring(head, len));
  108. return newdesc.toString();
  109. }
  110. }
  111. /**
  112. * Substitutes class names in the given descriptor string
  113. * according to the given <code>map</code>.
  114. *
  115. * @param map a map between replaced and substituted
  116. * JVM class names.
  117. *
  118. * @see Descriptor#toJvmName(String)
  119. */
  120. public static String rename(String desc, Map map) {
  121. if (map == null)
  122. return desc;
  123. StringBuffer newdesc = new StringBuffer();
  124. int head = 0;
  125. int i = 0;
  126. for (;;) {
  127. int j = desc.indexOf('L', i);
  128. if (j < 0)
  129. break;
  130. int k = desc.indexOf(';', j);
  131. if (k < 0)
  132. break;
  133. i = k + 1;
  134. String name = desc.substring(j + 1, k);
  135. String name2 = (String)map.get(name);
  136. if (name2 != null) {
  137. newdesc.append(desc.substring(head, j));
  138. newdesc.append('L');
  139. newdesc.append(name2);
  140. newdesc.append(';');
  141. head = i;
  142. }
  143. }
  144. if (head == 0)
  145. return desc;
  146. else {
  147. int len = desc.length();
  148. if (head < len)
  149. newdesc.append(desc.substring(head, len));
  150. return newdesc.toString();
  151. }
  152. }
  153. /**
  154. * Returns the descriptor representing the given type.
  155. */
  156. public static String of(CtClass type) {
  157. StringBuffer sbuf = new StringBuffer();
  158. toDescriptor(sbuf, type);
  159. return sbuf.toString();
  160. }
  161. private static void toDescriptor(StringBuffer desc, CtClass type) {
  162. if (type.isArray()) {
  163. desc.append('[');
  164. try {
  165. toDescriptor(desc, type.getComponentType());
  166. }
  167. catch (NotFoundException e) {
  168. desc.append('L');
  169. String name = type.getName();
  170. desc.append(toJvmName(name.substring(0, name.length() - 2)));
  171. desc.append(';');
  172. }
  173. }
  174. else if (type.isPrimitive()) {
  175. CtPrimitiveType pt = (CtPrimitiveType)type;
  176. desc.append(pt.getDescriptor());
  177. }
  178. else { // class type
  179. desc.append('L');
  180. desc.append(type.getName().replace('.', '/'));
  181. desc.append(';');
  182. }
  183. }
  184. /**
  185. * Returns the descriptor representing a constructor receiving
  186. * the given parameter types.
  187. *
  188. * @param paramTypes parameter types
  189. */
  190. public static String ofConstructor(CtClass[] paramTypes) {
  191. return ofMethod(CtClass.voidType, paramTypes);
  192. }
  193. /**
  194. * Returns the descriptor representing a method that receives
  195. * the given parameter types and returns the given type.
  196. *
  197. * @param returnType return type
  198. * @param paramTypes parameter types
  199. */
  200. public static String ofMethod(CtClass returnType, CtClass[] paramTypes) {
  201. StringBuffer desc = new StringBuffer();
  202. desc.append('(');
  203. if (paramTypes != null) {
  204. int n = paramTypes.length;
  205. for (int i = 0; i < n; ++i)
  206. toDescriptor(desc, paramTypes[i]);
  207. }
  208. desc.append(')');
  209. if (returnType != null)
  210. toDescriptor(desc, returnType);
  211. return desc.toString();
  212. }
  213. /**
  214. * Returns the descriptor representing a list of parameter types.
  215. * For example, if the given parameter types are two <code>int</code>,
  216. * then this method returns <code>"(II)"</code>.
  217. *
  218. * @param paramTypes parameter types
  219. */
  220. public static String ofParameters(CtClass[] paramTypes) {
  221. return ofMethod(null, paramTypes);
  222. }
  223. /**
  224. * Appends a parameter type to the parameter list represented
  225. * by the given descriptor.
  226. *
  227. * <p><code>classname</code> must not be an array type.
  228. *
  229. * @param classname parameter type (not primitive type)
  230. * @param desc descriptor
  231. */
  232. public static String appendParameter(String classname,
  233. String desc) {
  234. int i = desc.indexOf(')');
  235. if (i < 0)
  236. return desc;
  237. else {
  238. StringBuffer newdesc = new StringBuffer();
  239. newdesc.append(desc.substring(0, i));
  240. newdesc.append('L');
  241. newdesc.append(classname.replace('.', '/'));
  242. newdesc.append(';');
  243. newdesc.append(desc.substring(i));
  244. return newdesc.toString();
  245. }
  246. }
  247. /**
  248. * Inserts a parameter type at the beginning of the parameter
  249. * list represented
  250. * by the given descriptor.
  251. *
  252. * <p><code>classname</code> must not be an array type.
  253. *
  254. * @param classname parameter type (not primitive type)
  255. * @param desc descriptor
  256. */
  257. public static String insertParameter(String classname,
  258. String desc) {
  259. if (desc.charAt(0) != '(')
  260. return desc;
  261. else
  262. return "(L" + classname.replace('.', '/') + ';'
  263. + desc.substring(1);
  264. }
  265. /**
  266. * Changes the return type included in the given descriptor.
  267. *
  268. * <p><code>classname</code> must not be an array type.
  269. *
  270. * @param classname return type
  271. * @param desc descriptor
  272. */
  273. public static String changeReturnType(String classname, String desc) {
  274. int i = desc.indexOf(')');
  275. if (i < 0)
  276. return desc;
  277. else {
  278. StringBuffer newdesc = new StringBuffer();
  279. newdesc.append(desc.substring(0, i + 1));
  280. newdesc.append('L');
  281. newdesc.append(classname.replace('.', '/'));
  282. newdesc.append(';');
  283. return newdesc.toString();
  284. }
  285. }
  286. /**
  287. * Returns the <code>CtClass</code> objects representing the parameter
  288. * types specified by the given descriptor.
  289. *
  290. * @param desc descriptor
  291. * @param cp the class pool used for obtaining
  292. * a <code>CtClass</code> object.
  293. */
  294. public static CtClass[] getParameterTypes(String desc, ClassPool cp)
  295. throws NotFoundException
  296. {
  297. if (desc.charAt(0) != '(')
  298. return null;
  299. else {
  300. int num = numOfParameters(desc);
  301. CtClass[] args = new CtClass[num];
  302. int n = 0;
  303. int i = 1;
  304. do {
  305. i = toCtClass(cp, desc, i, args, n++);
  306. } while(i > 0);
  307. return args;
  308. }
  309. }
  310. /**
  311. * Returns true if the list of the parameter types of desc1 is equal to
  312. * that of desc2.
  313. * For example, "(II)V" and "(II)I" are equal.
  314. */
  315. public static boolean eqParamTypes(String desc1, String desc2) {
  316. if (desc1.charAt(0) != '(')
  317. return false;
  318. for (int i = 0; true; ++i) {
  319. char c = desc1.charAt(i);
  320. if (c != desc2.charAt(i))
  321. return false;
  322. if (c == ')')
  323. return true;
  324. }
  325. }
  326. /**
  327. * Returns the signature of the given descriptor. The signature does
  328. * not include the return type. For example, the signature of "(I)V"
  329. * is "(I)".
  330. */
  331. public static String getParamDescriptor(String decl) {
  332. return decl.substring(0, decl.indexOf(')') + 1);
  333. }
  334. /**
  335. * Returns the <code>CtClass</code> object representing the return
  336. * type specified by the given descriptor.
  337. *
  338. * @param desc descriptor
  339. * @param cp the class pool used for obtaining
  340. * a <code>CtClass</code> object.
  341. */
  342. public static CtClass getReturnType(String desc, ClassPool cp)
  343. throws NotFoundException
  344. {
  345. int i = desc.indexOf(')');
  346. if (i < 0)
  347. return null;
  348. else {
  349. CtClass[] type = new CtClass[1];
  350. toCtClass(cp, desc, i + 1, type, 0);
  351. return type[0];
  352. }
  353. }
  354. /**
  355. * Returns the number of the prameters included in the given
  356. * descriptor.
  357. *
  358. * @param desc descriptor
  359. */
  360. public static int numOfParameters(String desc) {
  361. int n = 0;
  362. int i = 1;
  363. for (;;) {
  364. char c = desc.charAt(i);
  365. if (c == ')')
  366. break;
  367. while (c == '[')
  368. c = desc.charAt(++i);
  369. if (c == 'L') {
  370. i = desc.indexOf(';', i) + 1;
  371. if (i <= 0)
  372. throw new IndexOutOfBoundsException("bad descriptor");
  373. }
  374. else
  375. ++i;
  376. ++n;
  377. }
  378. return n;
  379. }
  380. /**
  381. * Returns a <code>CtClass</code> object representing the type
  382. * specified by the given descriptor.
  383. *
  384. * <p>This method works even if the package-class separator is
  385. * not <code>/</code> but <code>.</code> (period). For example,
  386. * it accepts <code>Ljava.lang.Object;</code>
  387. * as well as <code>Ljava/lang/Object;</code>.
  388. *
  389. * @param desc descriptor
  390. * @param cp the class pool used for obtaining
  391. * a <code>CtClass</code> object.
  392. */
  393. public static CtClass toCtClass(String desc, ClassPool cp)
  394. throws NotFoundException
  395. {
  396. CtClass[] clazz = new CtClass[1];
  397. int res = toCtClass(cp, desc, 0, clazz, 0);
  398. if (res >= 0)
  399. return clazz[0];
  400. else {
  401. // maybe, you forgot to surround the class name with
  402. // L and ;. It violates the protocol, but I'm tolerant...
  403. return cp.get(desc.replace('/', '.'));
  404. }
  405. }
  406. private static int toCtClass(ClassPool cp, String desc, int i,
  407. CtClass[] args, int n)
  408. throws NotFoundException
  409. {
  410. int i2;
  411. String name;
  412. int arrayDim = 0;
  413. char c = desc.charAt(i);
  414. while (c == '[') {
  415. ++arrayDim;
  416. c = desc.charAt(++i);
  417. }
  418. if (c == 'L') {
  419. i2 = desc.indexOf(';', ++i);
  420. name = desc.substring(i, i2++).replace('/', '.');
  421. }
  422. else {
  423. CtClass type = toPrimitiveClass(c);
  424. if (type == null)
  425. return -1; // error
  426. i2 = i + 1;
  427. if (arrayDim == 0) {
  428. args[n] = type;
  429. return i2; // neither an array type or a class type
  430. }
  431. else
  432. name = type.getName();
  433. }
  434. if (arrayDim > 0) {
  435. StringBuffer sbuf = new StringBuffer(name);
  436. while (arrayDim-- > 0)
  437. sbuf.append("[]");
  438. name = sbuf.toString();
  439. }
  440. args[n] = cp.get(name);
  441. return i2;
  442. }
  443. private static CtClass toPrimitiveClass(char c) {
  444. CtClass type = null;
  445. switch (c) {
  446. case 'Z' :
  447. type = CtClass.booleanType;
  448. break;
  449. case 'C' :
  450. type = CtClass.charType;
  451. break;
  452. case 'B' :
  453. type = CtClass.byteType;
  454. break;
  455. case 'S' :
  456. type = CtClass.shortType;
  457. break;
  458. case 'I' :
  459. type = CtClass.intType;
  460. break;
  461. case 'J' :
  462. type = CtClass.longType;
  463. break;
  464. case 'F' :
  465. type = CtClass.floatType;
  466. break;
  467. case 'D' :
  468. type = CtClass.doubleType;
  469. break;
  470. case 'V' :
  471. type = CtClass.voidType;
  472. break;
  473. }
  474. return type;
  475. }
  476. /**
  477. * Computes the dimension of the array represented by the given
  478. * descriptor. For example, if the descriptor is <code>"[[I"</code>,
  479. * then this method returns 2.
  480. *
  481. * @param desc the descriptor.
  482. * @return 0 if the descriptor does not represent an array type.
  483. */
  484. public static int arrayDimension(String desc) {
  485. int dim = 0;
  486. while (desc.charAt(dim) == '[')
  487. ++dim;
  488. return dim;
  489. }
  490. /**
  491. * Returns the descriptor of the type of the array component.
  492. * For example, if the given descriptor is
  493. * <code>"[[Ljava/lang/String;"</code> and the given dimension is 2,
  494. * then this method returns <code>"Ljava/lang/String;"</code>.
  495. *
  496. * @param desc the descriptor.
  497. * @param dim the array dimension.
  498. */
  499. public static String toArrayComponent(String desc, int dim) {
  500. return desc.substring(dim);
  501. }
  502. /**
  503. * Computes the data size specified by the given descriptor.
  504. * For example, if the descriptor is "D", this method returns 2.
  505. *
  506. * <p>If the descriptor represents a method type, this method returns
  507. * (the size of the returned value) - (the sum of the data sizes
  508. * of all the parameters). For example, if the descriptor is
  509. * "(I)D", then this method returns 1 (= 2 - 1).
  510. *
  511. * @param desc descriptor
  512. */
  513. public static int dataSize(String desc) {
  514. int n = 0;
  515. char c = desc.charAt(0);
  516. if (c == '(') {
  517. int i = 1;
  518. for (;;) {
  519. c = desc.charAt(i);
  520. if (c == ')') {
  521. c = desc.charAt(i + 1);
  522. break;
  523. }
  524. boolean array = false;
  525. while (c == '[') {
  526. array = true;
  527. c = desc.charAt(++i);
  528. }
  529. if (c == 'L') {
  530. i = desc.indexOf(';', i) + 1;
  531. if (i <= 0)
  532. throw new IndexOutOfBoundsException("bad descriptor");
  533. }
  534. else
  535. ++i;
  536. if (!array && (c == 'J' || c == 'D'))
  537. n -= 2;
  538. else
  539. --n;
  540. }
  541. }
  542. if (c == 'J' || c == 'D')
  543. n += 2;
  544. else if (c != 'V')
  545. ++n;
  546. return n;
  547. }
  548. }