View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  
18  package org.apache.jdo.impl.enhancer.classfile;
19  
20  import java.util.Stack;
21  import java.util.Map;
22  
23  //@olsen: subst: Hashtable -> Map, HashMap
24  
25  
26  /***
27   * A collection of static methods which manipulate type descriptors
28   */
29  public class Descriptor implements VMConstants {
30      /*** 
31       * Return the number of words of arguments to the method 
32       * based on the method signature
33       */
34      public static int countMethodArgWords(String sig) {
35          if (sig.charAt(0) != '(')
36              throw new InsnError ("not a method signature");
37          int count = 0;
38          for (int idx = 1; sig.charAt(idx) != ')'; idx++) {
39              switch (sig.charAt(idx)) {
40              case 'B': /* byte */
41              case 'C': /* char */
42              case 'S': /* short */
43              case 'I': /* int */
44              case 'F': /* float */
45              case 'Z': /* boolean */
46                  count++;
47                  break;
48              case 'J': /* long */
49              case 'D': /* double */
50                  count += 2;
51                  break;
52              case 'L':
53                  count++;
54                  idx = sig.indexOf(';', idx);
55                  break;
56              case '[':
57                  count++;
58                  while (sig.charAt(idx) == '[' || sig.charAt(idx) == ']')
59                      idx++;
60                  if (sig.charAt(idx) == 'L')
61                      idx = sig.indexOf(';', idx);
62                  /* else, let idx++ at loop iteration skip primitive descriptor */
63                  break;
64              default:
65                  throw new InsnError("missing case");
66              }
67          }
68          return count;
69      }
70  
71      /*** 
72       * Return the number of words of return value for the method
73       * based on the method signature
74       */
75      public static int countMethodReturnWords(String sig) {
76          int idx = sig.lastIndexOf(')') + 1;
77          if (idx == 0)
78              throw new InsnError ("not a method signature");
79          switch (sig.charAt(idx)) {
80          case 'J': /* long */
81          case 'D': /* double */
82              return 2;
83          case 'B': /* byte */
84          case 'C': /* char */
85          case 'S': /* short */
86          case 'I': /* int */
87          case 'F': /* float */
88          case 'Z': /* boolean */
89          case 'L': /* object */
90          case '[': /* array */
91              return 1;
92          case 'V': /* void */
93              return 0;
94          default:
95              throw new InsnError("missing case");
96          }
97      }
98  
99      /***
100      * Return the stack descriptor for the result of a method
101      * invocation.  Void return values yield "V".
102      */
103     public static String extractResultSig(String methodSig) {
104         return methodSig.substring(methodSig.indexOf(')')+1);
105     }
106 
107     /***
108      * Return the stack descriptor for the arguments to a method
109      * invocation (not including any "this" argument)
110      */
111     public static String extractArgSig(String methodSig) {
112         return methodSig.substring(1, methodSig.indexOf(')'));
113     }
114 
115     /***
116      * Return the reversed stack descriptor for the arguments to a method
117      * invocation (not including any "this" argument).  The top of stack
118      * element will be first.
119      */
120     public static String extractReversedArgSig(String methodSig) {
121         StringBuffer buf = new StringBuffer();;
122         reverseArgSig(buf, methodSig, 1);
123         return buf.toString();
124     }
125 
126     /***
127      * Given a StringBuffer, a method descriptor, and a index to the 
128      * start of an argument descriptor, append the arguments to the
129      * string buffer in reverse order.
130      */
131     private static void reverseArgSig(StringBuffer buf, String methodSig, 
132                                       int idx) {
133         char c = methodSig.charAt(idx);
134         if (c == ')')
135             return;
136         int startIdx = idx;
137 
138         switch(c) {
139         case 'B':
140         case 'C':
141         case 'S':
142         case 'I':
143         case 'F':
144         case 'J':
145         case 'D':
146         case 'Z':
147             idx = idx+1;
148             break;
149         case '[':
150             while (methodSig.charAt(idx) == '[' || methodSig.charAt(idx) == ']')
151                 idx++;
152             if (methodSig.charAt(idx) != 'L') {
153                 idx++;
154                 break;
155             }
156             /* fall through */
157         case 'L':
158             idx = methodSig.indexOf(';', idx) + 1;
159             break;
160         default:
161             throw new InsnError("bad signature char");
162         }
163 
164         reverseArgSig(buf, methodSig, idx);
165         while (startIdx < idx)
166             buf.append(methodSig.charAt(startIdx++));
167     }
168 
169     /*** 
170      * Return the number of words of a field based on its signature.
171      */
172     //@olsen: added method
173     public static int countFieldWords(String sig) {
174         if (sig == null || sig.length() < 1)
175             throw new InsnError ("not a field signature");
176         switch (sig.charAt(0)) {
177         case 'J': /* long */
178         case 'D': /* double */
179             return 2;
180         case 'B': /* byte */
181         case 'C': /* char */
182         case 'S': /* short */
183         case 'I': /* int */
184         case 'F': /* float */
185         case 'Z': /* boolean */
186         case 'L': /* object */
187         case '[': /* array */
188             return 1;
189         default:
190             throw new InsnError("missing case");
191         }
192     }
193 
194     /***
195      * Return the element type for the first char in the type descriptor string.
196      */
197     //@olsen: added method
198     public static int elementType(String sig) {
199         if (sig == null || sig.length() < 1)
200             throw new InsnError ("not a value signature");
201         switch(sig.charAt(0)) {
202         case 'B':
203             return T_BOOLEAN;
204         case 'C':
205             return T_CHAR;
206         case 'Z':
207             return T_BYTE;
208         case 'S':
209             return T_SHORT;
210         case 'I':
211             return T_INT;
212         case 'J':
213             return T_LONG;
214         case 'F':
215             return T_FLOAT;
216         case 'D':
217             return T_DOUBLE;
218         case '[':
219             return TC_OBJECT;
220         case 'L':
221             return TC_OBJECT;
222         default:
223             throw new InsnError("bad signature char");
224         }
225     }
226 
227     /***
228      * Return the element type descriptor char for the element type.
229      * The element type must be one of the T_ or TC_OBJECT.
230      */
231     public static String elementSig(int valueType) {
232         switch(valueType) {
233         case T_BYTE:
234             return "B";
235         case T_CHAR:
236             return "C";
237         case T_BOOLEAN:
238             return "Z";
239         case T_SHORT:
240             return "S";
241         case T_INT:
242             return "I";
243         case T_LONG:
244             return "J";
245         case T_FLOAT:
246             return "F";
247         case T_DOUBLE:
248             return "D";
249         case TC_OBJECT:
250             return "Ljava/lang/Object;";
251         default:
252             throw new InsnError("bad element type");
253         }
254     }
255 
256     /***
257      * Return the number of stack words required for a value of the specified
258      * type on the operand stack.
259      */
260     public static int elementSize(int elementType) {
261         switch(elementType) {
262         case T_LONG:
263         case T_DOUBLE:
264         case T_TWOWORD:
265             return 2;
266         default:
267             return 1;
268         }
269     }
270 
271     /***
272      * stackSig is a signature for a list of types on the JVM stack with the
273      * last type in the signature intended to be on the top of JVM stack.
274      * For each type in the signature, pushes an Integer objects identifying
275      * the types on top of the input Stack object.
276      */
277     public static void computeStackTypes(String stackSig, Stack stack) {
278         for (int idx = 0; idx < stackSig.length(); idx++) {
279             int tp = 0;
280             switch(stackSig.charAt(idx)) {
281             case 'B':
282             case 'C':
283             case 'Z':
284             case 'S':
285             case 'I':
286                 tp = T_INT;
287                 break;
288             case 'F':
289                 tp = T_FLOAT;
290                 break;
291             case 'J':
292                 tp = T_LONG;
293                 break;
294             case 'D':
295                 tp = T_DOUBLE;
296                 break;
297             case '?':
298                 tp = T_UNKNOWN;
299                 break;
300             case 'W':
301                 tp = T_WORD;
302                 break;
303             case 'X':
304                 tp = T_TWOWORD;
305                 break;
306             case 'A':
307                 /* This isn't a real type, but any object refrence */
308                 tp = TC_OBJECT;
309                 break;
310             case '[':
311                 tp = TC_OBJECT;
312                 while (stackSig.charAt(idx) == '[' || stackSig.charAt(idx) == ']')
313                     idx++;
314                 if (stackSig.charAt(idx) != 'L')
315                     break;
316                 /* fall through */
317             case 'L':
318                 tp = TC_OBJECT;
319                 idx = stackSig.indexOf(';', idx);
320                 break;
321             default:
322                 throw new InsnError("bad signature char");
323             }
324             stack.push(new Integer(tp));
325         }
326     }
327 
328     /***
329      * stackSig is a signature for the types on the stack with the last
330      * type in the signature on the top of stack.  idx is the index of
331      * the start of a valid signature type element.  Return the index of
332      * the next element (which may be past the end of the string).
333      */
334     public static int nextSigElement(String stackSig, int idx) {
335         switch(stackSig.charAt(idx)) {
336         case 'B':
337         case 'C':
338         case 'Z':
339         case 'S':
340         case 'I':
341         case 'F':
342         case 'J':
343         case 'D':
344             break;
345         case '[':
346             while (stackSig.charAt(idx) == '[' || stackSig.charAt(idx) == ']')
347                 idx++;
348             if (stackSig.charAt(idx) != 'L')
349                 break;
350             /* fall through */
351         case 'L':
352             idx = stackSig.indexOf(';', idx);
353             break;
354         default:
355             throw new InsnError("bad signature char");
356         }
357 
358         idx++;
359         return idx;
360     }
361 
362     /***
363      * classTranslations contains a set of mappings of class names.
364      * For any types within the input signature which appear as keys
365      * in the translation table, change the signature to replace the
366      * original type with the translation.  Return a string containing
367      * the original signature with any translations applied.
368      */
369     public static String remapTypes(String sig, Map classTranslations) {
370         /* Defer allocation of the string buffer until it's needed */
371         StringBuffer buf = null;
372 
373         for (int idx = 0; idx < sig.length(); idx++) {
374             char c;
375             switch(c = sig.charAt(idx)) {
376             case '[':
377                 /* An array - skip through the [] pairs, copying to buf if not null */
378                 while ((c = sig.charAt(idx)) == '[' || c == ']') {
379                     idx++;
380                     if (buf != null)
381                         buf.append(c);
382                 }
383 
384                 /* If the next char isnt 'L', the next char is a simple type and
385                    will be handled by the default 1 char translation */
386                 if (sig.charAt(idx) != 'L')
387                     break;
388                 /* fall through to type name translation */
389             case 'L':
390                 /* This is a type name */
391                 idx++;
392                 int endIdx = sig.indexOf(';', idx);
393                 String typeName = sig.substring(idx, endIdx);
394                 String mapTo = (String) classTranslations.get(typeName);
395                 if (mapTo != null) {
396                     /* This type needs translation - allocate the string buffer
397                        now if needed and copy in all up to this type name. */
398                     if (buf == null) {
399                         buf = new StringBuffer(sig.length() + 20);
400                         buf.append(sig.substring(0,idx-1));
401                     }
402                     typeName = mapTo;
403                 }
404 
405                 if (buf != null) {
406                     buf.append('L');
407                     buf.append(typeName);
408                 }
409                 idx = endIdx;
410                 c = ';';
411                 break;
412             }
413 
414             if (buf != null)
415                 buf.append(c);
416         }
417         return (buf == null) ? sig : (buf.toString());
418     }
419 
420     /***
421      * classTranslations contains a set of mappings of class names.
422      * Translate the class name (which may be an array class) according
423      * to the entries in the translation table.
424      * Return either the original string if no translation applies or
425      * else the translated string.
426      */
427     public static String translateClass(
428 	String cls, Map classTranslations) {
429         if (cls.charAt(0) == '[')
430             return remapTypes(cls, classTranslations);
431         else {
432             String mapTo = (String) classTranslations.get(cls);
433             if (mapTo != null)
434                 return mapTo;
435             return cls;
436         }
437     }
438 
439     /***
440      * Translates a VM type field signature into a  user-format signature.
441      * Just a front for the two argument overload of this method.
442      */
443     public static String userFieldSig(String vmSig) {
444         return userFieldSig(vmSig, 0);
445     }
446 
447     /***
448      * Translates a VM type field signature into a  user-format signature.
449      */
450     public static String userFieldSig(String vmSig, int idx) {
451         String sigElement = "";
452         int arrayDims = 0;
453         boolean moreSig = true;
454         while (moreSig) {
455             moreSig = false;
456             char c = vmSig.charAt(idx);
457             switch (c) {
458             case 'B':
459                 sigElement = "byte";
460                 break;
461             case 'C':
462                 sigElement = "char";
463                 break;
464             case 'Z':
465                 sigElement = "boolean";
466                 break;
467             case 'S':
468                 sigElement = "short";
469                 break;
470             case 'I':
471                 sigElement = "int";
472                 break;
473             case 'F':
474                 sigElement = "float";
475                 break;
476             case 'J':
477                 sigElement = "long";
478                 break;
479             case 'D':
480                 sigElement = "double";
481                 break;
482             case 'V':
483                 /* void isn't really valid as a field signature but this method
484                    might be useful in implementing method signature conversion and
485                    void is a valid return type. */
486                 sigElement = "void";
487                 break;
488             case '[':
489                 idx++;
490                 arrayDims++;
491                 moreSig = true;
492                 break;
493             case 'L':
494                 int nextIdx = vmSig.indexOf(';', idx);
495                 sigElement = vmSig.substring(idx+1,nextIdx).replace('/','.');
496                 break;
497             default:
498                 throw new InsnError("bad signature char");
499             }
500         }
501 
502         /* If a non-array type, we already have the answer */
503         if (arrayDims == 0)
504             return sigElement;
505 
506         /* array types need a little more work */
507         StringBuffer buf = new StringBuffer(sigElement.length()
508                                             + 2 * arrayDims);
509         buf.append(sigElement);
510         while (arrayDims-- > 0) 
511             buf.append("[]");
512 
513         return buf.toString();
514     }
515 
516     /***
517      * Produce a user consumable representation of a method argument list
518      * from the method signature.  The return value is ignored.
519      */
520     public static String userMethodArgs(String methodSig) {
521         /* This better be a method signature */
522         if (methodSig.charAt(0) != '(')
523             throw new InsnError("Invalid method signature");
524 
525         StringBuffer buf = new StringBuffer();
526 
527         buf.append('(');
528 
529         int idx = 1;
530         boolean firstArg = true;
531         while (methodSig.charAt(idx) != ')') {
532             if (firstArg)
533                 firstArg = false;
534             else
535                 buf.append(", ");
536 	
537             buf.append(userFieldSig(methodSig, idx));
538             idx = nextSigElement(methodSig, idx);
539         }
540 
541         buf.append(')');
542         return buf.toString();
543     }
544 
545     /***
546      * Produce a user consumable representation of a method result type
547      * from the method signature.  The argument list is ignored.
548      */
549     //@olsen: added method
550     public static String userMethodResult(String methodSig) {
551         /* This better be a method signature */
552         if (methodSig.charAt(0) != '(')
553             throw new InsnError("Invalid method signature");
554         return userFieldSig(extractResultSig(methodSig));    
555     }
556 }