KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > transform > inlining > AsmHelper


1 /**************************************************************************************
2  * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
3  * http://aspectwerkz.codehaus.org *
4  * ---------------------------------------------------------------------------------- *
5  * The software in this package is published under the terms of the LGPL license *
6  * a copy of which has been included with this distribution in the license.txt file. *
7  **************************************************************************************/

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

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

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

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

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

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

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

187     public static Class JavaDoc defineClass(ClassLoader JavaDoc loader, final byte[] bytes, final String JavaDoc name) {
188         String JavaDoc className = name.replace('/', '.');
189         try {
190             if (loader == null) {
191                 loader = ContextClassLoader.getLoader();
192             }
193
194             // TODO: what if we don't have rights to set this method to
195
// accessible on this specific CL? Load it in System CL?
196
//CLASS_LOADER_DEFINE.setAccessible(true);
197
Object JavaDoc[] args = new Object JavaDoc[]{
198                 className, bytes, new Integer JavaDoc(0), new Integer JavaDoc(bytes.length), PROTECTION_DOMAIN
199             };
200             Class JavaDoc clazz = (Class JavaDoc) CLASS_LOADER_DEFINE.invoke(loader, args);
201
202             //CLASS_LOADER_DEFINE.setAccessible(false);
203
return clazz;
204
205         } catch (InvocationTargetException JavaDoc e) {
206             // JIT failovering for Thread concurrency
207
// AW-222 (Tomcat and WLS were reported for AW-222)
208
if (e.getTargetException() instanceof LinkageError JavaDoc) {
209                 Class JavaDoc failoverJoinpointClass = forName(loader, className);
210                 if (failoverJoinpointClass != null) {
211                     return failoverJoinpointClass;
212                 }
213             }
214             throw new WrappedRuntimeException(e);
215         } catch (Exception JavaDoc e) {
216             throw new WrappedRuntimeException(e);
217         }
218     }
219
220     /**
221      * Tries to load a class if unsuccessful returns null.
222      *
223      * @param loader the class loader
224      * @param name the name of the class
225      * @return the class
226      */

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

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

265     public static int calculateConstructorHash(final String JavaDoc desc) {
266         return AsmHelper.calculateMethodHash(INIT_METHOD_NAME, desc);
267     }
268
269 // /**
270
// * Calculates the joinpoint constructor call/execution hash.
271
// * It depends on the callee class name else we won't be able to distinguish joinpoint on different targets.
272
// *
273
// * @param declaringClassName
274
// * @param desc
275
// * @return
276
// */
277
// public static int calculateConstructorJoinPointHash(final String declaringClassName, final String desc) {
278
// int hash = 17;
279
// hash = (37 * hash) + AsmHelper.calculateClassHash("L" + declaringClassName + ";");
280
// hash = (37 * hash) + AsmHelper.calculateMethodHash(INIT_METHOD_NAME, desc);
281
// return hash;
282
// }
283

284     /**
285      * Calculates the field hash.
286      *
287      * @param name
288      * @param desc
289      * @return
290      */

291     public static int calculateFieldHash(final String JavaDoc name, final String JavaDoc desc) {
292         int hash = 17;
293         hash = (37 * hash) + name.hashCode();
294         Type type = Type.getType(desc);
295         hash = (37 * hash) + AsmHelper.convertTypeDescToReflectDesc(type.getDescriptor()).hashCode();
296         return hash;
297     }
298
299     /**
300      * Calculates the class hash.
301      *
302      * @param declaringType
303      * @return
304      */

305     public static int calculateClassHash(final String JavaDoc declaringType) {
306         return AsmHelper.convertTypeDescToReflectDesc(declaringType).hashCode();
307     }
308
309     /**
310      * Converts an internal Java array type name ([Lblabla) to the a the format used by the expression matcher
311      * (blabla[])
312      *
313      * @param typeName is type name
314      * @return
315      */

316     public static String JavaDoc convertArrayTypeName(final String JavaDoc typeName) {
317         int index = typeName.lastIndexOf('[');
318         if (index != -1) {
319             StringBuffer JavaDoc arrayType = new StringBuffer JavaDoc();
320             if (typeName.endsWith("I")) {
321                 arrayType.append("int");
322             } else if (typeName.endsWith("J")) {
323                 arrayType.append("long");
324             } else if (typeName.endsWith("S")) {
325                 arrayType.append("short");
326             } else if (typeName.endsWith("F")) {
327                 arrayType.append("float");
328             } else if (typeName.endsWith("D")) {
329                 arrayType.append("double");
330             } else if (typeName.endsWith("Z")) {
331                 arrayType.append("boolean");
332             } else if (typeName.endsWith("C")) {
333                 arrayType.append("char");
334             } else if (typeName.endsWith("B")) {
335                 arrayType.append("byte");
336             } else {
337                 arrayType.append(typeName.substring(index + 2, typeName.length() - 1));
338             }
339             for (int i = 0; i < (index + 1); i++) {
340                 arrayType.append("[]");
341             }
342             return arrayType.toString();
343         } else {
344             return typeName;
345         }
346     }
347
348     /**
349      * Converts an ASM type descriptor" (I, [I, [Ljava/lang/String;, Ljava/lang/String;) to a Java.reflect one (int, [I,
350      * [Ljava.lang.String;, java.lang.String)
351      *
352      * @param typeDesc
353      * @return the Java.reflect string representation
354      */

355     public static String JavaDoc convertTypeDescToReflectDesc(final String JavaDoc typeDesc) {
356         if (typeDesc == null) {
357             return null;
358         }
359         String JavaDoc result = null;
360         // change needed for array types only
361
if (typeDesc.startsWith("[")) {
362             result = typeDesc;
363         } else {
364             // support for single dimension type
365
if (typeDesc.startsWith("L") && typeDesc.endsWith(";")) {
366                 result = typeDesc.substring(1, typeDesc.length() - 1);
367             } else {
368                 // primitive type, single dimension
369
if (typeDesc.equals("I")) {
370                     result = "int";
371                 } else if (typeDesc.equals("J")) {
372                     result = "long";
373                 } else if (typeDesc.equals("S")) {
374                     result = "short";
375                 } else if (typeDesc.equals("F")) {
376                     result = "float";
377                 } else if (typeDesc.equals("D")) {
378                     result = "double";
379                 } else if (typeDesc.equals("Z")) {
380                     result = "boolean";
381                 } else if (typeDesc.equals("C")) {
382                     result = "char";
383                 } else if (typeDesc.equals("B")) {
384                     result = "byte";
385                 } else {
386                     throw new RuntimeException JavaDoc("unknown primitive type " + typeDesc);
387                 }
388             }
389         }
390         return result.replace('/', '.');
391     }
392
393     /**
394      * Converts a java reflect type desc to ASM type desc.
395      *
396      * @param desc
397      * @return
398      */

399     public static String JavaDoc convertReflectDescToTypeDesc(final String JavaDoc desc) {
400         if (desc == null) {
401             return null;
402         }
403         String JavaDoc typeDesc = desc;
404         int dimension = 0;
405         char[] arr = desc.toCharArray();
406         for (int i = 0; i < arr.length; i++) {
407             if (arr[i] == ']') {
408                 dimension++;
409             }
410         }
411         typeDesc = desc.substring(0, desc.length() - dimension * 2);
412         if (typeDesc.equals("int")) {
413             typeDesc = "I";
414         } else if (typeDesc.equals("short")) {
415             typeDesc = "S";
416         } else if (typeDesc.equals("long")) {
417             typeDesc = "J";
418         } else if (typeDesc.equals("float")) {
419             typeDesc = "F";
420         } else if (typeDesc.equals("double")) {
421             typeDesc = "D";
422         } else if (typeDesc.equals("byte")) {
423             typeDesc = "B";
424         } else if (typeDesc.equals("char")) {
425             typeDesc = "C";
426         } else if (typeDesc.equals("boolean")) {
427             typeDesc = "Z";
428         } else {
429             typeDesc = 'L' + typeDesc + ';';
430         }
431         for (int i = 0; i < dimension; i++) {
432             typeDesc = '[' + typeDesc;
433         }
434         return typeDesc.replace('.', '/');
435     }
436
437     /**
438      * Adds the correct return statement.
439      *
440      * @param mv
441      * @param type
442      */

443     public static void addReturnStatement(final CodeVisitor mv, final Type type) {
444         switch (type.getSort()) {
445             case Type.VOID:
446                 mv.visitInsn(RETURN);
447                 break;
448             case Type.LONG:
449                 mv.visitInsn(LRETURN);
450                 break;
451             case Type.INT:
452                 mv.visitInsn(IRETURN);
453                 break;
454             case Type.SHORT:
455                 mv.visitInsn(IRETURN);
456                 break;
457             case Type.DOUBLE:
458                 mv.visitInsn(DRETURN);
459                 break;
460             case Type.FLOAT:
461                 mv.visitInsn(FRETURN);
462                 break;
463             case Type.BYTE:
464                 mv.visitInsn(IRETURN);
465                 break;
466             case Type.BOOLEAN:
467                 mv.visitInsn(IRETURN);
468                 break;
469             case Type.CHAR:
470                 mv.visitInsn(IRETURN);
471                 break;
472             case Type.ARRAY:
473                 mv.visitInsn(ARETURN);
474                 break;
475             case Type.OBJECT:
476                 mv.visitInsn(ARETURN);
477                 break;
478         }
479     }
480
481     /**
482      * Loads argument types.
483      *
484      * @param mv
485      * @param argumentTypes
486      */

487     public static void loadArgumentTypes(final CodeVisitor mv, final Type[] argumentTypes, final boolean staticMethod) {
488         int index;
489         if (staticMethod) {
490             index = 0;
491         } else {
492             index = 1;
493         }
494         for (int i = 0; i < argumentTypes.length; i++) {
495             index = loadType(mv, index, argumentTypes[i]);
496         }
497     }
498
499     /**
500      * Loads a type.
501      *
502      * @param cv
503      * @param index
504      * @param type
505      * @return the incremented index
506      */

507     public static int loadType(final CodeVisitor cv, int index, final Type type) {
508         switch (type.getSort()) {
509             case Type.LONG:
510                 cv.visitVarInsn(LLOAD, index++);
511                 index++;
512                 break;
513             case Type.INT:
514                 cv.visitVarInsn(ILOAD, index++);
515                 break;
516             case Type.SHORT:
517                 cv.visitVarInsn(ILOAD, index++);
518                 break;
519             case Type.DOUBLE:
520                 cv.visitVarInsn(DLOAD, index++);
521                 index++;
522                 break;
523             case Type.FLOAT:
524                 cv.visitVarInsn(FLOAD, index++);
525                 break;
526             case Type.BYTE:
527                 cv.visitVarInsn(ILOAD, index++);
528                 break;
529             case Type.BOOLEAN:
530                 cv.visitVarInsn(ILOAD, index++);
531                 break;
532             case Type.CHAR:
533                 cv.visitVarInsn(ILOAD, index++);
534                 break;
535             case Type.ARRAY:
536                 cv.visitVarInsn(ALOAD, index++);
537                 break;
538             case Type.OBJECT:
539                 cv.visitVarInsn(ALOAD, index++);
540                 break;
541         }
542         return index;
543     }
544
545     /**
546      * Stores a type.
547      *
548      * @param cv
549      * @param index
550      * @param type
551      * @return the incremented index
552      */

553     public static int storeType(final CodeVisitor cv, int index, final Type type) {
554         switch (type.getSort()) {
555             case Type.VOID:
556                 break;
557             case Type.LONG:
558                 cv.visitVarInsn(LSTORE, index++);
559                 index++;
560                 break;
561             case Type.INT:
562                 cv.visitVarInsn(ISTORE, index++);
563                 break;
564             case Type.SHORT:
565                 cv.visitVarInsn(ISTORE, index++);
566                 break;
567             case Type.DOUBLE:
568                 cv.visitVarInsn(DSTORE, index++);
569                 index++;
570                 break;
571             case Type.FLOAT:
572                 cv.visitVarInsn(FSTORE, index++);
573                 break;
574             case Type.BYTE:
575                 cv.visitVarInsn(ISTORE, index++);
576                 break;
577             case Type.BOOLEAN:
578                 cv.visitVarInsn(ISTORE, index++);
579                 break;
580             case Type.CHAR:
581                 cv.visitVarInsn(ISTORE, index++);
582                 break;
583             case Type.ARRAY:
584                 cv.visitVarInsn(ASTORE, index++);
585                 break;
586             case Type.OBJECT:
587                 cv.visitVarInsn(ASTORE, index++);
588                 break;
589         }
590         return index;
591     }
592
593     /**
594      * Creates and adds the correct parameter index for integer types.
595      *
596      * @param cv
597      * @param index
598      */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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