/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