View Javadoc

    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;
    26  import java.io.FileOutputStream;
    27  import java.io.IOException;
    28  import java.lang.reflect.Constructor;
    29  import java.lang.reflect.Method;
    30  import java.lang.reflect.InvocationTargetException;
    31  import java.security.ProtectionDomain;
    32  import java.security.AccessController;
    33  import java.security.PrivilegedAction;
    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 CLASS_LOADER;
    54      private static Method CLASS_LOADER_DEFINE;
    55      private static final ProtectionDomain PROTECTION_DOMAIN;
    56
    57      static {
    58          PROTECTION_DOMAIN = (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() {
    59              public Object run() {
    60                  return AsmHelper.class.getProtectionDomain();
    61              }
    62          });
    63
    64          AccessController.doPrivileged(new PrivilegedAction() {
    65              public Object 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[]{
    70                                  String.class, byte[].class, int.class, int.class, ProtectionDomain.class
    71                              }
    72                      );
    73                      CLASS_LOADER_DEFINE.setAccessible(true);
    74                  } catch (Throwable t) {
    75                      throw new Error(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 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 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 constructor) {
    124         Class[] 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 dumpDir, final String className, final byte[] bytes)
    141             throws IOException {
    142         final File dir;
    143         if (className.lastIndexOf('/')>0) {
    144             dir = new File(dumpDir + File.separator + className.substring(0, className.lastIndexOf('/')));
    145         } else {
    146             dir = new File(dumpDir);
    147         }
    148         dir.mkdirs();
    149         String fileName = dumpDir + File.separator + className + ".class";
    150         if (AspectWerkzPreProcessor.VERBOSE) {
    151             System.out.println("AW INFO: dumping class " + className + " to " + dumpDir);
    152         }
    153         FileOutputStream os = new FileOutputStream(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 dumpDir, final String className, final ClassWriter cw)
    167             throws IOException {
    168         File dir = new File(dumpDir + File.separator + className.substring(0, className.lastIndexOf('/')));
    169         dir.mkdirs();
    170         String fileName = dumpDir + File.separator + className + ".class";
    171         if (AspectWerkzPreProcessor.VERBOSE) {
    172             System.out.println("AW INFO: dumping class " + className + " to " + dumpDir);
    173         }
    174         FileOutputStream os = new FileOutputStream(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 defineClass(ClassLoader loader, final byte[] bytes, final String name) {
    188         String 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[] args = new Object[]{
    198                 className, bytes, new Integer(0), new Integer(bytes.length), PROTECTION_DOMAIN
    199             };
    200             Class clazz = (Class) CLASS_LOADER_DEFINE.invoke(loader, args);
    201
    202             //CLASS_LOADER_DEFINE.setAccessible(false);
    203             return clazz;
    204
    205         } catch (InvocationTargetException e) {
    206             // JIT failovering for Thread concurrency
    207             // AW-222 (Tomcat and WLS were reported for AW-222)
    208             if (e.getTargetException() instanceof LinkageError) {
    209                 Class failoverJoinpointClass = forName(loader, className);
    210                 if (failoverJoinpointClass != null) {
    211                     return failoverJoinpointClass;
    212                 }
    213             }
    214             throw new WrappedRuntimeException(e);
    215         } catch (Exception 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 forName(ClassLoader loader, final String name) {
    228         String 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 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 name, final String 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 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 name, final String 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 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 convertArrayTypeName(final String typeName) {
    317         int index = typeName.lastIndexOf('[');
    318         if (index != -1) {
    319             StringBuffer arrayType = new StringBuffer();
    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 convertTypeDescToReflectDesc(final String typeDesc) {
    356         if (typeDesc == null) {
    357             return null;
    358         }
    359         String 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("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 convertReflectDescToTypeDesc(final String desc) {
    400         if (desc == null) {
    401             return null;
    402         }
    403         String 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 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 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 getMethodDescriptor(final MethodInfo method) {
    1007         ClassInfo[] parameters = method.getParameterTypes();
    1008         StringBuffer buf = new StringBuffer();
    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 getConstructorDescriptor(final ConstructorInfo constructor) {
    1025         ClassInfo[] parameters = constructor.getParameterTypes();
    1026         StringBuffer buf = new StringBuffer();
    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 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 getClassDescriptor(final ClassInfo c) {
    1056         StringBuffer buf = new StringBuffer();
    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 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("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 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("should not happen");
    1160             }
    1161         } else {
    1162             return Type.getType(getClassDescriptor(c));
    1163         }
    1164     }
    1165
    1166 }