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

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