View Javadoc

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