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

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