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