KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > aspectwerkz > transform > inlining > AsmHelper


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.aspectwerkz.transform.inlining;
5
6 import com.tc.asm.ClassWriter;
7 import com.tc.asm.MethodVisitor;
8 import com.tc.asm.Type;
9 import com.tc.asm.Label;
10 import com.tc.asm.ClassReader;
11
12 import com.tc.aspectwerkz.exception.WrappedRuntimeException;
13 import com.tc.aspectwerkz.reflect.impl.java.JavaClassInfo;
14 import com.tc.aspectwerkz.reflect.ClassInfo;
15 import com.tc.aspectwerkz.reflect.FieldInfo;
16 import com.tc.aspectwerkz.reflect.ConstructorInfo;
17 import com.tc.aspectwerkz.reflect.MethodInfo;
18 import com.tc.aspectwerkz.transform.Properties;
19 import com.tc.aspectwerkz.transform.TransformationConstants;
20 import com.tc.aspectwerkz.util.ContextClassLoader;
21
22
23 import java.io.File JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.lang.reflect.Constructor JavaDoc;
27 import java.lang.reflect.Method JavaDoc;
28 import java.lang.reflect.InvocationTargetException JavaDoc;
29 import java.security.ProtectionDomain JavaDoc;
30 import java.security.AccessController JavaDoc;
31 import java.security.PrivilegedAction JavaDoc;
32
33 /**
34  * Helper class with utility methods for the ASM library.
35  *
36  * @author <a HREF="mailto:jboner@codehaus.org">Jonas BonŽr </a>
37  * @author <a HREF="mailto:alex@gnilux.com">Alexandre Vasseur </a>
38  */

39 public class AsmHelper implements TransformationConstants {
40
41   public final static ClassInfo INTEGER = JavaClassInfo.getClassInfo(Integer.TYPE);
42   public final static ClassInfo VOID = JavaClassInfo.getClassInfo(Void.TYPE);
43   public final static ClassInfo BOOLEAN = JavaClassInfo.getClassInfo(Boolean.TYPE);
44   public final static ClassInfo BYTE = JavaClassInfo.getClassInfo(Byte.TYPE);
45   public final static ClassInfo CHARACTER = JavaClassInfo.getClassInfo(Character.TYPE);
46   public final static ClassInfo SHORT = JavaClassInfo.getClassInfo(Short.TYPE);
47   public final static ClassInfo DOUBLE = JavaClassInfo.getClassInfo(Double.TYPE);
48   public final static ClassInfo FLOAT = JavaClassInfo.getClassInfo(Float.TYPE);
49   public final static ClassInfo LONG = JavaClassInfo.getClassInfo(Long.TYPE);
50
51   private static Class JavaDoc CLASS_LOADER;
52   private static Method JavaDoc CLASS_LOADER_DEFINE;
53   private static final ProtectionDomain JavaDoc PROTECTION_DOMAIN;
54
55   static {
56     PROTECTION_DOMAIN = (ProtectionDomain JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
57       public Object JavaDoc run() {
58         return AsmHelper.class.getProtectionDomain();
59       }
60     });
61
62     AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
63       public Object JavaDoc run() {
64         try {
65           CLASS_LOADER = Class.forName(CLASS_LOADER_REFLECT_CLASS_NAME);
66           CLASS_LOADER_DEFINE = CLASS_LOADER.getDeclaredMethod(
67                   DEFINE_CLASS_METHOD_NAME, new Class JavaDoc[]{
68                   String JavaDoc.class, byte[].class, int.class, int.class, ProtectionDomain JavaDoc.class
69           }
70           );
71           CLASS_LOADER_DEFINE.setAccessible(true);
72         } catch (Throwable JavaDoc t) {
73           throw new Error JavaDoc(t.toString());
74         }
75         return null;
76       }
77     });
78   }
79
80
81   /**
82    * A boolean to check if we have a J2SE 5 support
83    */

84   public final static boolean IS_JAVA_5;
85   public static int JAVA_VERSION = V1_3;
86
87   static {
88     Class JavaDoc annotation = null;
89     try {
90       annotation = Class.forName("java.lang.annotation.Annotation");
91       ClassReader cr = new ClassReader("java.lang.annotation.Annotation");
92       JAVA_VERSION = V1_5;
93     } catch (Throwable JavaDoc e) {
94       annotation = null;
95     }
96     if (annotation == null) {
97       IS_JAVA_5 = false;
98     } else {
99       IS_JAVA_5 = true;
100     }
101   }
102
103   /**
104    * Factory method for ASM ClassWriter and J2SE 5 support
105    * See http://www.objectweb.org/wws/arc/asm/2004-08/msg00005.html
106    *
107    * @param computeMax
108    * @return
109    */

110   public static ClassWriter newClassWriter(boolean computeMax) {
111     return new ClassWriter(computeMax, true);
112   }
113
114   /**
115    * Gets the argument types for a constructor. <p/>Parts of code in this method is taken from the ASM codebase.
116    *
117    * @param constructor
118    * @return the ASM argument types for the constructor
119    */

120   public static Type[] getArgumentTypes(final Constructor JavaDoc constructor) {
121     Class JavaDoc[] classes = constructor.getParameterTypes();
122     Type[] types = new Type[classes.length];
123     for (int i = classes.length - 1; i >= 0; --i) {
124       types[i] = Type.getType(classes[i]);
125     }
126     return types;
127   }
128
129   /**
130    * Dumps an ASM class to disk.
131    *
132    * @param dumpDir
133    * @param className
134    * @param bytes
135    * @throws java.io.IOException
136    */

137   public static void dumpClass(final String JavaDoc dumpDir, final String JavaDoc className, final byte[] bytes)
138           throws IOException JavaDoc {
139     final File JavaDoc dir;
140     if (className.lastIndexOf('/') > 0) {
141       dir = new File JavaDoc(dumpDir + File.separator + className.substring(0, className.lastIndexOf('/')));
142     } else {
143       dir = new File JavaDoc(dumpDir);
144     }
145     dir.mkdirs();
146     String JavaDoc fileName = dumpDir + File.separator + className + ".class";
147     if (Properties.PRINT_DEPLOYMENT_INFO) {
148       System.out.println("AW INFO: dumping class " + className + " to " + dumpDir);
149     }
150     FileOutputStream JavaDoc os = new FileOutputStream JavaDoc(fileName);
151     os.write(bytes);
152     os.close();
153   }
154
155   /**
156    * Dumps an ASM class to disk.
157    *
158    * @param dumpDir
159    * @param className
160    * @param cw
161    * @throws java.io.IOException
162    */

163   public static void dumpClass(final String JavaDoc dumpDir, final String JavaDoc className, final ClassWriter cw)
164           throws IOException JavaDoc {
165     String JavaDoc base = "";
166     if (className.lastIndexOf('/') > 0) {
167       base = className.substring(0, className.lastIndexOf('/'));
168     }
169     File JavaDoc dir = new File JavaDoc(dumpDir + File.separator + base);
170     dir.mkdirs();
171     String JavaDoc fileName = dumpDir + File.separator + className + ".class";
172     if (Properties.PRINT_DEPLOYMENT_INFO) {
173       System.out.println("AW INFO: dumping class " + className + " to " + dumpDir);
174     }
175     FileOutputStream JavaDoc os = new FileOutputStream JavaDoc(fileName);
176     os.write(cw.toByteArray());
177     os.close();
178   }
179
180   /**
181    * Adds a class to a class loader and loads it.
182    *
183    * @param loader the class loader (if null the context class loader will be used)
184    * @param bytes the bytes for the class
185    * @param name the name of the class
186    * @return the class
187    */

188   public static Class JavaDoc defineClass(ClassLoader JavaDoc loader, final byte[] bytes, final String JavaDoc name) {
189     String JavaDoc className = name.replace('/', '.');
190     try {
191       if (loader == null) {
192         loader = ContextClassLoader.getLoader();
193       }
194
195       Object JavaDoc[] args = new Object JavaDoc[]{
196               className, bytes, new Integer JavaDoc(0), new Integer JavaDoc(bytes.length), PROTECTION_DOMAIN
197       };
198       Class JavaDoc klass = (Class JavaDoc) CLASS_LOADER_DEFINE.invoke(loader, args);
199       return klass;
200
201     } catch (InvocationTargetException JavaDoc e) {
202       // JIT failovering for Thread concurrency
203
// AW-222 (Tomcat and WLS were reported for AW-222)
204
if (e.getTargetException() instanceof LinkageError JavaDoc) {
205         Class JavaDoc failoverJoinpointClass = forName(loader, className);
206         if (failoverJoinpointClass != null) {
207           return failoverJoinpointClass;
208         }
209       }
210       throw new WrappedRuntimeException(e);
211     } catch (Exception JavaDoc e) {
212       throw new WrappedRuntimeException(e);
213     }
214   }
215
216   /**
217    * Tries to load a class if unsuccessful returns null.
218    *
219    * @param loader the class loader
220    * @param name the name of the class
221    * @return the class
222    */

223   public static Class JavaDoc forName(ClassLoader JavaDoc loader, final String JavaDoc name) {
224     String JavaDoc className = name.replace('/', '.');
225     try {
226       if (loader == null) {
227         loader = ContextClassLoader.getLoader();
228       }
229       // Use Class.forName since loader.loadClass fails on JBoss UCL
230
return Class.forName(className, false, loader);
231     } catch (Exception JavaDoc e) {
232       return null;
233     }
234   }
235
236   /**
237    * Calculates the method hash. The computation MUST be the same as in ReflectHelper, thus we switch back the names
238    * to Java style. Note that for array type, Java.reflect is using "[Lpack.foo;" style unless primitive.
239    *
240    * @param name
241    * @param desc
242    * @return
243    */

244   public static int calculateMethodHash(final String JavaDoc name, final String JavaDoc desc) {
245     int hash = 17;
246     hash = (37 * hash) + name.replace('/', '.').hashCode();
247     Type[] argumentTypes = Type.getArgumentTypes(desc);
248     for (int i = 0; i < argumentTypes.length; i++) {
249       hash = (37 * hash)
250               + AsmHelper.convertTypeDescToReflectDesc(argumentTypes[i].getDescriptor()).hashCode();
251     }
252     return hash;
253   }
254
255   /**
256    * Calculates the constructor hash.
257    *
258    * @param desc
259    * @return
260    */

261   public static int calculateConstructorHash(final String JavaDoc desc) {
262     return AsmHelper.calculateMethodHash(INIT_METHOD_NAME, desc);
263   }
264
265   /**
266    * Calculates the field hash.
267    *
268    * @param name
269    * @param desc
270    * @return
271    */

272   public static int calculateFieldHash(final String JavaDoc name, final String JavaDoc desc) {
273     int hash = 17;
274     hash = (37 * hash) + name.hashCode();
275     Type type = Type.getType(desc);
276     hash = (37 * hash) + AsmHelper.convertTypeDescToReflectDesc(type.getDescriptor()).hashCode();
277     return hash;
278   }
279
280   /**
281    * Calculates the class hash.
282    *
283    * @param declaringType
284    * @return
285    */

286   public static int calculateClassHash(final String JavaDoc declaringType) {
287     return AsmHelper.convertTypeDescToReflectDesc(declaringType).hashCode();
288   }
289
290   /**
291    * Converts an internal Java array type name ([Lblabla) to the a the format used by the expression matcher
292    * (blabla[])
293    *
294    * @param typeName is type name
295    * @return
296    */

297   public static String JavaDoc convertArrayTypeName(final String JavaDoc typeName) {
298     int index = typeName.lastIndexOf('[');
299     if (index != -1) {
300       StringBuffer JavaDoc arrayType = new StringBuffer JavaDoc();
301       if (typeName.endsWith("I")) {
302         arrayType.append("int");
303       } else if (typeName.endsWith("J")) {
304         arrayType.append("long");
305       } else if (typeName.endsWith("S")) {
306         arrayType.append("short");
307       } else if (typeName.endsWith("F")) {
308         arrayType.append("float");
309       } else if (typeName.endsWith("D")) {
310         arrayType.append("double");
311       } else if (typeName.endsWith("Z")) {
312         arrayType.append("boolean");
313       } else if (typeName.endsWith("C")) {
314         arrayType.append("char");
315       } else if (typeName.endsWith("B")) {
316         arrayType.append("byte");
317       } else {
318         arrayType.append(typeName.substring(index + 2, typeName.length() - 1));
319       }
320       for (int i = 0; i < (index + 1); i++) {
321         arrayType.append("[]");
322       }
323       return arrayType.toString();
324     } else {
325       return typeName;
326     }
327   }
328
329   /**
330    * Converts an ASM type descriptor" (I, [I, [Ljava/lang/String;, Ljava/lang/String;) to a Java.reflect one (int, [I,
331    * [Ljava.lang.String;, java.lang.String)
332    *
333    * @param typeDesc
334    * @return the Java.reflect string representation
335    */

336   public static String JavaDoc convertTypeDescToReflectDesc(final String JavaDoc typeDesc) {
337     if (typeDesc == null) {
338       return null;
339     }
340     String JavaDoc result = null;
341     // change needed for array types only
342
if (typeDesc.startsWith("[")) {
343       result = typeDesc;
344     } else {
345       // support for single dimension type
346
if (typeDesc.startsWith("L") && typeDesc.endsWith(";")) {
347         result = typeDesc.substring(1, typeDesc.length() - 1);
348       } else {
349         // primitive type, single dimension
350
if (typeDesc.equals("I")) {
351           result = "int";
352         } else if (typeDesc.equals("J")) {
353           result = "long";
354         } else if (typeDesc.equals("S")) {
355           result = "short";
356         } else if (typeDesc.equals("F")) {
357           result = "float";
358         } else if (typeDesc.equals("D")) {
359           result = "double";
360         } else if (typeDesc.equals("Z")) {
361           result = "boolean";
362         } else if (typeDesc.equals("C")) {
363           result = "char";
364         } else if (typeDesc.equals("B")) {
365           result = "byte";
366         } else {
367           throw new RuntimeException JavaDoc("unknown primitive type " + typeDesc);
368         }
369       }
370     }
371     return result.replace('/', '.');
372   }
373
374   /**
375    * Converts a java reflect type desc to ASM type desc.
376    *
377    * @param desc
378    * @return
379    */

380   public static String JavaDoc convertReflectDescToTypeDesc(final String JavaDoc desc) {
381     if (desc == null) {
382       return null;
383     }
384     String JavaDoc typeDesc = desc;
385     int dimension = 0;
386     char[] arr = desc.toCharArray();
387     for (int i = 0; i < arr.length; i++) {
388       if (arr[i] == ']') {
389         dimension++;
390       }
391     }
392     typeDesc = desc.substring(0, desc.length() - dimension * 2);
393     if (typeDesc.equals("int")) {
394       typeDesc = "I";
395     } else if (typeDesc.equals("short")) {
396       typeDesc = "S";
397     } else if (typeDesc.equals("long")) {
398       typeDesc = "J";
399     } else if (typeDesc.equals("float")) {
400       typeDesc = "F";
401     } else if (typeDesc.equals("double")) {
402       typeDesc = "D";
403     } else if (typeDesc.equals("byte")) {
404       typeDesc = "B";
405     } else if (typeDesc.equals("char")) {
406       typeDesc = "C";
407     } else if (typeDesc.equals("boolean")) {
408       typeDesc = "Z";
409     } else {
410       typeDesc = 'L' + typeDesc + ';';
411     }
412     for (int i = 0; i < dimension; i++) {
413       typeDesc = '[' + typeDesc;
414     }
415     return typeDesc.replace('.', '/');
416   }
417
418   /**
419    * Adds the correct return statement.
420    *
421    * @param mv
422    * @param type
423    */

424   public static void addReturnStatement(final MethodVisitor mv, final Type type) {
425     switch (type.getSort()) {
426       case Type.VOID:
427         mv.visitInsn(RETURN);
428         break;
429       case Type.LONG:
430         mv.visitInsn(LRETURN);
431         break;
432       case Type.INT:
433         mv.visitInsn(IRETURN);
434         break;
435       case Type.SHORT:
436         mv.visitInsn(IRETURN);
437         break;
438       case Type.DOUBLE:
439         mv.visitInsn(DRETURN);
440         break;
441       case Type.FLOAT:
442         mv.visitInsn(FRETURN);
443         break;
444       case Type.BYTE:
445         mv.visitInsn(IRETURN);
446         break;
447       case Type.BOOLEAN:
448         mv.visitInsn(IRETURN);
449         break;
450       case Type.CHAR:
451         mv.visitInsn(IRETURN);
452         break;
453       case Type.ARRAY:
454         mv.visitInsn(ARETURN);
455         break;
456       case Type.OBJECT:
457         mv.visitInsn(ARETURN);
458         break;
459     }
460   }
461
462   /**
463    * Loads argument types.
464    *
465    * @param mv
466    * @param argumentTypes
467    */

468   public static void loadArgumentTypes(final MethodVisitor mv, final Type[] argumentTypes, final boolean staticMethod) {
469     int index;
470     if (staticMethod) {
471       index = 0;
472     } else {
473       index = 1;
474     }
475     for (int i = 0; i < argumentTypes.length; i++) {
476       index = loadType(mv, index, argumentTypes[i]);
477     }
478   }
479
480   /**
481    * Loads a type.
482    *
483    * @param cv
484    * @param index
485    * @param type
486    * @return the incremented index
487    */

488   public static int loadType(final MethodVisitor cv, int index, final Type type) {
489     switch (type.getSort()) {
490       case Type.LONG:
491         cv.visitVarInsn(LLOAD, index++);
492         index++;
493         break;
494       case Type.INT:
495         cv.visitVarInsn(ILOAD, index++);
496         break;
497       case Type.SHORT:
498         cv.visitVarInsn(ILOAD, index++);
499         break;
500       case Type.DOUBLE:
501         cv.visitVarInsn(DLOAD, index++);
502         index++;
503         break;
504       case Type.FLOAT:
505         cv.visitVarInsn(FLOAD, index++);
506         break;
507       case Type.BYTE:
508         cv.visitVarInsn(ILOAD, index++);
509         break;
510       case Type.BOOLEAN:
511         cv.visitVarInsn(ILOAD, index++);
512         break;
513       case Type.CHAR:
514         cv.visitVarInsn(ILOAD, index++);
515         break;
516       case Type.ARRAY:
517         cv.visitVarInsn(ALOAD, index++);
518         break;
519       case Type.OBJECT:
520         cv.visitVarInsn(ALOAD, index++);
521         break;
522     }
523     return index;
524   }
525
526   /**
527    * Stores a type.
528    *
529    * @param cv
530    * @param index
531    * @param type
532    * @return the incremented index
533    */

534   public static int storeType(final MethodVisitor cv, int index, final Type type) {
535     switch (type.getSort()) {
536       case Type.VOID:
537         break;
538       case Type.LONG:
539         cv.visitVarInsn(LSTORE, index++);
540         index++;
541         break;
542       case Type.INT:
543         cv.visitVarInsn(ISTORE, index++);
544         break;
545       case Type.SHORT:
546         cv.visitVarInsn(ISTORE, index++);
547         break;
548       case Type.DOUBLE:
549         cv.visitVarInsn(DSTORE, index++);
550         index++;
551         break;
552       case Type.FLOAT:
553         cv.visitVarInsn(FSTORE, index++);
554         break;
555       case Type.BYTE:
556         cv.visitVarInsn(ISTORE, index++);
557         break;
558       case Type.BOOLEAN:
559         cv.visitVarInsn(ISTORE, index++);
560         break;
561       case Type.CHAR:
562         cv.visitVarInsn(ISTORE, index++);
563         break;
564       case Type.ARRAY:
565         cv.visitVarInsn(ASTORE, index++);
566         break;
567       case Type.OBJECT:
568         cv.visitVarInsn(ASTORE, index++);
569         break;
570     }
571     return index;
572   }
573
574   /**
575    * Push the string on the stack. Deal when case where string is null.
576    *
577    * @param cv
578    * @param s
579    */

580   public static void loadStringConstant(final MethodVisitor cv, final String JavaDoc s) {
581     if (s != null) {
582       cv.visitLdcInsn(s);
583     } else {
584       cv.visitInsn(ACONST_NULL);
585     }
586   }
587
588   /**
589    * Creates and adds the correct parameter index for integer types.
590    *
591    * @param cv
592    * @param index
593    */

594   public static void loadIntegerConstant(final MethodVisitor cv, final int index) {
595     switch (index) {
596       case 0:
597         cv.visitInsn(ICONST_0);
598         break;
599       case 1:
600         cv.visitInsn(ICONST_1);
601         break;
602       case 2:
603         cv.visitInsn(ICONST_2);
604         break;
605       case 3:
606         cv.visitInsn(ICONST_3);
607         break;
608       case 4:
609         cv.visitInsn(ICONST_4);
610         break;
611       case 5:
612         cv.visitInsn(ICONST_5);
613         break;
614       default:
615         cv.visitIntInsn(BIPUSH, index);
616         break;
617     }
618   }
619
620   /**
621    * Prepares the wrapping or a primitive type.
622    *
623    * @param cv
624    * @param type
625    */

626   public static void prepareWrappingOfPrimitiveType(final MethodVisitor cv, final Type type) {
627     switch (type.getSort()) {
628       case Type.SHORT:
629         cv.visitTypeInsn(NEW, SHORT_CLASS_NAME);
630         cv.visitInsn(DUP);
631         break;
632       case Type.INT:
633         cv.visitTypeInsn(NEW, INTEGER_CLASS_NAME);
634         cv.visitInsn(DUP);
635         break;
636       case Type.LONG:
637         cv.visitTypeInsn(NEW, LONG_CLASS_NAME);
638         cv.visitInsn(DUP);
639         break;
640       case Type.FLOAT:
641         cv.visitTypeInsn(NEW, FLOAT_CLASS_NAME);
642         cv.visitInsn(DUP);
643         break;
644       case Type.DOUBLE:
645         cv.visitTypeInsn(NEW, DOUBLE_CLASS_NAME);
646         cv.visitInsn(DUP);
647         break;
648       case Type.BYTE:
649         cv.visitTypeInsn(NEW, BYTE_CLASS_NAME);
650         cv.visitInsn(DUP);
651         break;
652       case Type.BOOLEAN:
653         cv.visitTypeInsn(NEW, BOOLEAN_CLASS_NAME);
654         cv.visitInsn(DUP);
655         break;
656       case Type.CHAR:
657         cv.visitTypeInsn(NEW, CHARACTER_CLASS_NAME);
658         cv.visitInsn(DUP);
659         break;
660     }
661   }
662
663   /**
664    * Handles the wrapping of a primitive type.
665    *
666    * @param cv
667    * @param type
668    */

669   public static void wrapPrimitiveType(final MethodVisitor cv, final Type type) {
670     switch (type.getSort()) {
671       case Type.VOID:
672         cv.visitInsn(ACONST_NULL);
673         break;
674       case Type.SHORT:
675         cv.visitMethodInsn(
676                 INVOKESPECIAL,
677                 SHORT_CLASS_NAME,
678                 INIT_METHOD_NAME,
679                 SHORT_CLASS_INIT_METHOD_SIGNATURE
680         );
681         break;
682       case Type.INT:
683         cv.visitMethodInsn(
684                 INVOKESPECIAL,
685                 INTEGER_CLASS_NAME,
686                 INIT_METHOD_NAME,
687                 INTEGER_CLASS_INIT_METHOD_SIGNATURE
688         );
689         break;
690       case Type.LONG:
691         cv.visitMethodInsn(INVOKESPECIAL, LONG_CLASS_NAME, INIT_METHOD_NAME, LONG_CLASS_INIT_METHOD_SIGNATURE);
692         break;
693       case Type.FLOAT:
694         cv.visitMethodInsn(
695                 INVOKESPECIAL,
696                 FLOAT_CLASS_NAME,
697                 INIT_METHOD_NAME,
698                 FLOAT_CLASS_INIT_METHOD_SIGNATURE
699         );
700         break;
701       case Type.DOUBLE:
702         cv.visitMethodInsn(
703                 INVOKESPECIAL,
704                 DOUBLE_CLASS_NAME,
705                 INIT_METHOD_NAME,
706                 DOUBLE_CLASS_INIT_METHOD_SIGNATURE
707         );
708         break;
709       case Type.BYTE:
710         cv.visitMethodInsn(INVOKESPECIAL, BYTE_CLASS_NAME, INIT_METHOD_NAME, BYTE_CLASS_INIT_METHOD_SIGNATURE);
711         break;
712       case Type.BOOLEAN:
713         cv.visitMethodInsn(
714                 INVOKESPECIAL,
715                 BOOLEAN_CLASS_NAME,
716                 INIT_METHOD_NAME,
717                 BOOLEAN_CLASS_INIT_METHOD_SIGNATURE
718         );
719         break;
720       case Type.CHAR:
721         cv.visitMethodInsn(
722                 INVOKESPECIAL,
723                 CHARACTER_CLASS_NAME,
724                 INIT_METHOD_NAME,
725                 CHARACTER_CLASS_INIT_METHOD_SIGNATURE
726         );
727         break;
728     }
729   }
730
731   /**
732    * Handles the unwrapping of a type, unboxing of primitives and casting to the correct object type.
733    * Takes care of null value replaced by default primitive value.
734    * <pre>(obj==null)?0L:((Long)obj).longValue();</pre>
735    *
736    * @param cv
737    * @param type
738    */

739   public static void unwrapType(final MethodVisitor cv, final Type type) {
740     // void, object and array type handling
741
switch (type.getSort()) {
742       case Type.OBJECT:
743         String JavaDoc objectTypeName = type.getClassName().replace('.', '/');
744         cv.visitTypeInsn(CHECKCAST, objectTypeName);
745         return;
746       case Type.ARRAY:
747         cv.visitTypeInsn(CHECKCAST, type.getDescriptor());
748         return;
749       case Type.VOID:
750         return;
751     }
752     // primitive type handling
753
Label l0If = new Label();
754     Label l1End = new Label();
755     // if != null
756
cv.visitInsn(DUP);
757     cv.visitJumpInsn(IFNONNULL, l0If);
758     // else, default value
759
cv.visitInsn(POP);
760     addDefaultValue(cv, type);
761     // end
762
cv.visitJumpInsn(GOTO, l1End);
763     // if body
764
cv.visitLabel(l0If);
765     switch (type.getSort()) {
766       case Type.SHORT:
767         cv.visitTypeInsn(CHECKCAST, SHORT_CLASS_NAME);
768         cv.visitMethodInsn(
769                 INVOKEVIRTUAL,
770                 SHORT_CLASS_NAME,
771                 SHORT_VALUE_METHOD_NAME,
772                 SHORT_VALUE_METHOD_SIGNATURE
773         );
774         break;
775       case Type.INT:
776         cv.visitTypeInsn(CHECKCAST, INTEGER_CLASS_NAME);
777         cv.visitMethodInsn(
778                 INVOKEVIRTUAL,
779                 INTEGER_CLASS_NAME,
780                 INT_VALUE_METHOD_NAME,
781                 INT_VALUE_METHOD_SIGNATURE
782         );
783         break;
784       case Type.LONG:
785         cv.visitTypeInsn(CHECKCAST, LONG_CLASS_NAME);
786         cv.visitMethodInsn(
787                 INVOKEVIRTUAL, LONG_CLASS_NAME, LONG_VALUE_METHOD_NAME, LONG_VALUE_METHOD_SIGNATURE
788         );
789         break;
790       case Type.FLOAT:
791         cv.visitTypeInsn(CHECKCAST, FLOAT_CLASS_NAME);
792         cv.visitMethodInsn(
793                 INVOKEVIRTUAL,
794                 FLOAT_CLASS_NAME,
795                 FLOAT_VALUE_METHOD_NAME,
796                 FLOAT_VALUE_METHOD_SIGNATURE
797         );
798         break;
799       case Type.DOUBLE:
800         cv.visitTypeInsn(CHECKCAST, DOUBLE_CLASS_NAME);
801         cv.visitMethodInsn(
802                 INVOKEVIRTUAL,
803                 DOUBLE_CLASS_NAME,
804                 DOUBLE_VALUE_METHOD_NAME,
805                 DOUBLE_VALUE_METHOD_SIGNATURE
806         );
807         break;
808       case Type.BYTE:
809         cv.visitTypeInsn(CHECKCAST, BYTE_CLASS_NAME);
810         cv.visitMethodInsn(
811                 INVOKEVIRTUAL, BYTE_CLASS_NAME, BYTE_VALUE_METHOD_NAME, BYTE_VALUE_METHOD_SIGNATURE
812         );
813         break;
814       case Type.BOOLEAN:
815         cv.visitTypeInsn(CHECKCAST, BOOLEAN_CLASS_NAME);
816         cv.visitMethodInsn(
817                 INVOKEVIRTUAL,
818                 BOOLEAN_CLASS_NAME,
819                 BOOLEAN_VALUE_METHOD_NAME,
820                 BOOLEAN_VALUE_METHOD_SIGNATURE
821         );
822         break;
823       case Type.CHAR:
824         cv.visitTypeInsn(CHECKCAST, CHARACTER_CLASS_NAME);
825         cv.visitMethodInsn(
826                 INVOKEVIRTUAL,
827                 CHARACTER_CLASS_NAME,
828                 CHAR_VALUE_METHOD_NAME,
829                 CHAR_VALUE_METHOD_SIGNATURE
830         );
831         break;
832     }
833     cv.visitLabel(l1End);
834   }
835
836   /**
837    * Adds the default value for a type.
838    *
839    * @param cv
840    * @param type
841    */

842   public static void addDefaultValue(final MethodVisitor cv, final Type type) {
843     switch (type.getSort()) {
844       case Type.OBJECT:
845         cv.visitInsn(ACONST_NULL);
846         break;
847       case Type.ARRAY:
848         cv.visitInsn(ACONST_NULL);
849         break;
850       case Type.INT:
851         cv.visitInsn(ICONST_0);
852         break;
853       case Type.LONG:
854         cv.visitInsn(LCONST_0);
855         break;
856       case Type.SHORT:
857         cv.visitInsn(ICONST_0);
858         break;
859       case Type.FLOAT:
860         cv.visitInsn(FCONST_0);
861         break;
862       case Type.DOUBLE:
863         cv.visitInsn(DCONST_0);
864         break;
865       case Type.BYTE:
866         cv.visitInsn(ICONST_0);
867         break;
868       case Type.BOOLEAN:
869         cv.visitInsn(ICONST_0);
870         break;
871       case Type.CHAR:
872         cv.visitInsn(ICONST_0);
873         break;
874     }
875   }
876
877   /**
878    * Adds a string and inserts null if the string is null.
879    *
880    * @param cv
881    * @param value
882    */

883   public static void addNullableString(final MethodVisitor cv, final String JavaDoc value) {
884     if (value == null) {
885       cv.visitInsn(ACONST_NULL);
886     } else {
887       cv.visitLdcInsn(value);
888     }
889   }
890
891   /**
892    * Compute the register depth, based on an array of types (long, double = 2 bytes address)
893    *
894    * @param typesOnStack
895    * @return depth of the stack
896    */

897   public static int getRegisterDepth(final Type[] typesOnStack) {
898     int depth = 0;
899     for (int i = 0; i < typesOnStack.length; i++) {
900       Type type = typesOnStack[i];
901       depth++;
902       switch (type.getSort()) {
903         case Type.LONG:
904           depth++;
905           break;
906         case Type.DOUBLE:
907           depth++;
908           break;
909       }
910     }
911     return depth;
912   }
913
914   /**
915    * Compute the index on the stack of a given argument based on its index in the signature
916    *
917    * @param typesOnStack
918    * @param typeIndex
919    * @return
920    */

921   public static int getRegisterIndexOf(final Type[] typesOnStack, final int typeIndex) {
922     int depth = 0;
923     for (int i = 0; i < typeIndex; i++) {
924       Type type = typesOnStack[i];
925       depth++;
926       switch (type.getSort()) {
927         case Type.LONG:
928           depth++;
929           break;
930         case Type.DOUBLE:
931           depth++;
932           break;
933       }
934     }
935     return depth;
936   }
937
938   /**
939    * Compute the index on the signature of a given argument based on its index as if it was on the stack
940    * where the stack would start at the first argument
941    *
942    * @param typesOnStack
943    * @param registerIndex
944    * @return
945    */

946   public static int getTypeIndexOf(final Type[] typesOnStack, final int registerIndex) {
947     for (int i = 0; i < typesOnStack.length; i++) {
948       int presumedRegisterIndex = getRegisterIndexOf(typesOnStack, i);
949       if (registerIndex == presumedRegisterIndex) {
950         return i;
951       }
952     }
953     return -1;
954   }
955
956   /**
957    * Checks if the Type is a primitive.
958    *
959    * @param returnType
960    * @return TRUE/FALSE
961    */

962   public static boolean isPrimitive(final Type returnType) {
963     if (returnType.getSort() == Type.INT ||
964             returnType.getSort() == Type.SHORT ||
965             returnType.getSort() == Type.LONG ||
966             returnType.getSort() == Type.FLOAT ||
967             returnType.getSort() == Type.DOUBLE ||
968             returnType.getSort() == Type.BYTE ||
969             returnType.getSort() == Type.CHAR ||
970             returnType.getSort() == Type.BOOLEAN ||
971             returnType.getSort() == Type.VOID) {
972       return true;
973     } else {
974       return false;
975     }
976   }
977
978   /**
979    * Increments the index (takes doubles and longs in to account).
980    *
981    * @param index
982    * @param type
983    * @return the incremented index
984    */

985   public static int incrementIndex(int index, final Type type) {
986     if (type.getSort() == Type.LONG || type.getSort() == Type.DOUBLE) {
987       index += 2;
988     } else {
989       index++;
990     }
991     return index;
992   }
993
994   /**
995    * Returns the descriptor corresponding to the given method info.
996    * Adapted from ASM Type.getMethodDescriptor(<Method>)
997    *
998    * @param method
999    * @return the descriptor of the given method.
1000   */

1001  public static String JavaDoc getMethodDescriptor(final MethodInfo method) {
1002    ClassInfo[] parameters = method.getParameterTypes();
1003    StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1004    buf.append('(');
1005    for (int i = 0; i < parameters.length; ++i) {
1006      getClassDescriptor(buf, parameters[i]);
1007    }
1008    buf.append(')');
1009    getClassDescriptor(buf, method.getReturnType());
1010    return buf.toString();
1011  }
1012
1013  /**
1014   * Returns the descriptor corresponding to the given constructor info.
1015   *
1016   * @param constructor
1017   * @return the descriptor of the given constructor.
1018   */

1019  public static String JavaDoc getConstructorDescriptor(final ConstructorInfo constructor) {
1020    ClassInfo[] parameters = constructor.getParameterTypes();
1021    StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1022    buf.append('(');
1023    for (int i = 0; i < parameters.length; ++i) {
1024      getClassDescriptor(buf, parameters[i]);
1025    }
1026    buf.append(')');
1027    getClassDescriptor(buf, VOID);
1028    return buf.toString();
1029  }
1030
1031  /**
1032   * Returns the descriptor corresponding to the given field info.
1033   *
1034   * @param field
1035   * @return the descriptor of the given field.
1036   */

1037  public static String JavaDoc getFieldDescriptor(final FieldInfo field) {
1038    return getClassDescriptor(field.getType());
1039  }
1040
1041  /**
1042   * Returns the descriptor corresponding to the given Java type.
1043   * Adapted from ASM Type.getClassDescriptor(Class)
1044   * <p/>
1045   * TODO remove the delegation to private method
1046   *
1047   * @param c an object class, a primitive class or an array class.
1048   * @return the descriptor corresponding to the given class.
1049   */

1050  public static String JavaDoc getClassDescriptor(final ClassInfo c) {
1051    StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1052    getClassDescriptor(buf, c);
1053    return buf.toString();
1054  }
1055
1056  /**
1057   * Appends the descriptor of the given class to the given string buffer.
1058   * Adapted from ASM Type.getClassDescriptor(StringBuffer, Class)
1059   *
1060   * @param buf the string buffer to which the descriptor must be appended.
1061   * @param klass the class whose descriptor must be computed.
1062   */

1063  private static void getClassDescriptor(final StringBuffer JavaDoc buf, final ClassInfo klass) {
1064    ClassInfo d = klass;
1065    while (true) {
1066      if (d.isPrimitive()) {
1067        char car;
1068        if (d.equals(INTEGER)) {
1069          car = 'I';
1070        } else if (d.equals(VOID)) {
1071          car = 'V';
1072        } else if (d.equals(BOOLEAN)) {
1073          car = 'Z';
1074        } else if (d.equals(BYTE)) {
1075          car = 'B';
1076        } else if (d.equals(CHARACTER)) {
1077          car = 'C';
1078        } else if (d.equals(SHORT)) {
1079          car = 'S';
1080        } else if (d.equals(DOUBLE)) {
1081          car = 'D';
1082        } else if (d.equals(FLOAT)) {
1083          car = 'F';
1084        } else if (d.equals(LONG)) {
1085          car = 'J';
1086        } else {
1087          throw new Error JavaDoc("should not happen");
1088        }
1089        buf.append(car);
1090        return;
1091      } else if (d.isArray()) {
1092        buf.append('[');
1093        d = d.getComponentType();
1094      } else {
1095        buf.append('L');
1096        String JavaDoc name = d.getName();
1097        int len = name.length();
1098        for (int i = 0; i < len; ++i) {
1099          char car = name.charAt(i);
1100          buf.append(car == '.' ? '/' : car);
1101        }
1102        buf.append(';');
1103        return;
1104      }
1105    }
1106  }
1107
1108  /**
1109   * Returns the Java types corresponding to the argument types of the given
1110   * method.
1111   * Adapted from ASM getArgumentTypes(Method)
1112   *
1113   * @param method a method.
1114   * @return the Java types corresponding to the argument types of the given
1115   * method.
1116   */

1117  public static Type[] getArgumentTypes(final MethodInfo method) {
1118    ClassInfo[] classes = method.getParameterTypes();
1119    Type[] types = new Type[classes.length];
1120    for (int i = classes.length - 1; i >= 0; --i) {
1121      types[i] = getType(classes[i]);
1122    }
1123    return types;
1124  }
1125
1126  /**
1127   * Returns the Java type corresponding to the given class.
1128   * Adapted from ASM getType(Class)
1129   *
1130   * @param c a class.
1131   * @return the Java type corresponding to the given class.
1132   */

1133  public static Type getType(final ClassInfo c) {
1134    if (c.isPrimitive()) {
1135      if (c.equals(INTEGER)) {
1136        return Type.INT_TYPE;
1137      } else if (c.equals(VOID)) {
1138        return Type.VOID_TYPE;
1139      } else if (c.equals(BOOLEAN)) {
1140        return Type.BOOLEAN_TYPE;
1141      } else if (c.equals(BYTE)) {
1142        return Type.BYTE_TYPE;
1143      } else if (c.equals(CHARACTER)) {
1144        return Type.CHAR_TYPE;
1145      } else if (c.equals(SHORT)) {
1146        return Type.SHORT_TYPE;
1147      } else if (c.equals(DOUBLE)) {
1148        return Type.DOUBLE_TYPE;
1149      } else if (c.equals(FLOAT)) {
1150        return Type.FLOAT_TYPE;
1151      } else if (c.equals(LONG)) {
1152        return Type.LONG_TYPE;
1153      } else {
1154        throw new Error JavaDoc("should not happen");
1155      }
1156    } else {
1157      return Type.getType(getClassDescriptor(c));
1158    }
1159  }
1160}
Popular Tags