View Javadoc

    1   /*
    2    $Id: BytecodeHelper.java,v 1.14 2004/07/10 03:31:39 bran Exp $
    3
    4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5
    6    Redistribution and use of this software and associated documentation
    7    ("Software"), with or without modification, are permitted provided
    8    that the following conditions are met:
    9
    10   1. Redistributions of source code must retain copyright
    11      statements and notices.  Redistributions must also contain a
    12      copy of this document.
    13
    14   2. Redistributions in binary form must reproduce the
    15      above copyright notice, this list of conditions and the
    16      following disclaimer in the documentation and/or other
    17      materials provided with the distribution.
    18
    19   3. The name "groovy" must not be used to endorse or promote
    20      products derived from this Software without prior written
    21      permission of The Codehaus.  For written permission,
    22      please contact info@codehaus.org.
    23
    24   4. Products derived from this Software may not be called "groovy"
    25      nor may "groovy" appear in their names without prior written
    26      permission of The Codehaus. "groovy" is a registered
    27      trademark of The Codehaus.
    28
    29   5. Due credit should be given to The Codehaus -
    30      http://groovy.codehaus.org/
    31
    32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    43   OF THE POSSIBILITY OF SUCH DAMAGE.
    44
    45   */
    46  package org.codehaus.groovy.classgen;
    47
    48  import groovy.lang.MetaMethod;
    49
    50  import java.lang.reflect.Method;
    51  import java.lang.reflect.Modifier;
    52  import java.math.BigDecimal;
    53  import java.math.BigInteger;
    54
    55  import org.codehaus.groovy.ast.FieldNode;
    56  import org.codehaus.groovy.ast.Parameter;
    57  import org.codehaus.groovy.runtime.InvokerHelper;
    58  import org.objectweb.asm.CodeVisitor;
    59  import org.objectweb.asm.Constants;
    60  import org.objectweb.asm.Label;
    61
    62
    63  /***
    64   * A helper class for bytecode generation with AsmClassGenerator2.
    65   *
    66   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    67   * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
    68   * @version $Revision: 1.14 $
    69   */
    70  public class BytecodeHelper implements Constants {
    71
    72      private CodeVisitor cv;
    73
    74      public CodeVisitor getCodeVisitor() {
    75          return cv;
    76      }
    77
    78      public BytecodeHelper(CodeVisitor cv) {
    79          this.cv = cv;
    80      }
    81
    82      /***
    83       * Generates the bytecode to autobox the current value on the stack
    84       */
    85      public void box(Class type) {
    86          if (type.isPrimitive() && type != void.class) {
    87              String returnString = "(" + getTypeDescription(type.getName()) + ")Ljava/lang/Object;";
    88              cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(InvokerHelper.class.getName()), "box", returnString);
    89          }
    90      }
    91
    92      /***
    93       * box the primitive value on the stack
    94       * @param cls
    95       */
    96      public void quickBoxIfNecessary(Class cls) {
    97          String type = cls.getName();
    98          String descr = getTypeDescription(type);
    99          if (cls == boolean.class) {
    100             boxBoolean();
    101         }
    102         else if (cls.isPrimitive() && cls != void.class) {
    103             // use a special integer pool in the invokerhelper
    104             if (cls == Integer.TYPE) {
    105                 cv.visitMethodInsn(
    106                         INVOKESTATIC,
    107                         getClassInternalName(InvokerHelper.class.getName()),
    108                         "integerValue",
    109                         "(I)Ljava/lang/Integer;"
    110                 );
    111                 return;
    112             }
    113
    114             String wrapperName = getObjectTypeForPrimitive(type);
    115             String internName = getClassInternalName(wrapperName);
    116             cv.visitTypeInsn(NEW, internName);
    117             cv.visitInsn(DUP);
    118             if (type.equals("double") || type.equals("long")) {
    119                 cv.visitInsn(DUP2_X2);
    120                 cv.visitInsn(POP2);
    121             } else {
    122                 cv.visitInsn(DUP2_X1);
    123                 cv.visitInsn(POP2);
    124             }
    125             cv.visitMethodInsn(INVOKESPECIAL, internName, "<init>", "(" + descr + ")V");
    126
    127 //            Operand opr = new Operand(ITEM_Object, wrapperName, "", "");
    128 //            _safePop();
    129 //            push(opr);
    130         }
    131     }
    132
    133     /***
    134      * unbox the ref on the stack
    135      * @param cls
    136      */
    137     public void quickUnboxIfNecessary(Class cls) {
    138         String type = cls.getName();
    139
    140         if (cls.isPrimitive() && cls != void.class) { // todo care when BigDecimal or BigIneteger on stack
    141             String wrapperName = getObjectTypeForPrimitive(type);
    142             String internName = getClassInternalName(wrapperName);
    143             if (cls == boolean.class) {
    144                 cv.visitTypeInsn(CHECKCAST, internName);
    145                 cv.visitMethodInsn(INVOKEVIRTUAL, internName, type + "Value", "()" + getTypeDescription(type));
    146             } else { // numbers
    147                 cv.visitTypeInsn(CHECKCAST, "java/lang/Number");
    148                 cv.visitMethodInsn(INVOKEVIRTUAL, /*internName*/"java/lang/Number", type + "Value", "()" + getTypeDescription(type));
    149             }
    150         }
    151
    152     }
    153
    154     public void box(String type) {
    155         if (isPrimitiveType(type) && !type.equals("void")) {
    156             String returnString = "(" + getTypeDescription(type) + ")Ljava/lang/Object;";
    157             cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(InvokerHelper.class.getName()), "box", returnString);
    158             // todo optimize this
    159         }
    160     }
    161
    162     /***
    163      * Generates the bytecode to unbox the current value on the stack
    164      */
    165     public void unbox(Class type) {
    166         if (type.isPrimitive() && type != void.class) {
    167             String returnString = "(Ljava/lang/Object;)" + getTypeDescription(type.getName());
    168             cv.visitMethodInsn(
    169                 INVOKESTATIC,
    170                 getClassInternalName(InvokerHelper.class.getName()),
    171                 type.getName() + "Unbox",
    172                 returnString);
    173         }
    174     }
    175
    176     /***
    177      * Generates the bytecode to unbox the current value on the stack
    178      */
    179     public void unbox(String type) {
    180         if (isPrimitiveType(type) && !type.equals("void")) {
    181             String returnString = "(Ljava/lang/Object;)" + getTypeDescription(type);
    182             cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(InvokerHelper.class.getName()), type + "Unbox", returnString);
    183         }
    184     }
    185
    186     public static boolean isPrimitiveType(String type) {
    187         return type != null
    188             && (type.equals("boolean")
    189                 || type.equals("byte")
    190                 || type.equals("char")
    191                 || type.equals("short")
    192                 || type.equals("int")
    193                 || type.equals("long")
    194                 || type.equals("float")
    195                 || type.equals("double"));
    196     }
    197
    198     /***
    199      * array types are special:
    200      * eg.: String[]: classname: [Ljava/lang/String;
    201      *      int[]: [I
    202      * @return the ASM type description
    203      */
    204     public static String getTypeDescription(String name) {
    205         // lets avoid class loading
    206         // return getType(name).getDescriptor();
    207         if (name == null) {
    208             return "Ljava/lang/Object;";
    209         }
    210         if (name.equals("void")) {
    211             return "V";
    212         }
    213
    214         if (name.startsWith("[")) { // todo need to take care of multi-dimentional array
    215             return name.replace('.', '/');
    216         }
    217
    218         String prefix = "";
    219         if (name.endsWith("[]")) {
    220             prefix = "[";
    221             name = name.substring(0, name.length() - 2);
    222         }
    223
    224         if (name.equals("int")) {
    225             return prefix + "I";
    226         }
    227         else if (name.equals("long")) {
    228             return prefix + "J";
    229         }
    230         else if (name.equals("short")) {
    231             return prefix + "S";
    232         }
    233         else if (name.equals("float")) {
    234             return prefix + "F";
    235         }
    236         else if (name.equals("double")) {
    237             return prefix + "D";
    238         }
    239         else if (name.equals("byte")) {
    240             return prefix + "B";
    241         }
    242         else if (name.equals("char")) {
    243             return prefix + "C";
    244         }
    245         else if (name.equals("boolean")) {
    246             return prefix + "Z";
    247         }
    248         return prefix + "L" + name.replace('.', '/') + ";";
    249     }
    250
    251     /***
    252      * @return the ASM internal name of the type
    253      */
    254     public static String getClassInternalName(String name) {
    255         if (name == null) {
    256             return "java/lang/Object";
    257         }
    258         String answer = name.replace('.', '/');
    259         if (answer.endsWith("[]")) {
    260             return "[" + answer.substring(0, answer.length() - 2);
    261         }
    262         return answer;
    263     }
    264
    265     /***
    266      * @return the regular class name of the type
    267      */
    268     public static String getClassRegularName(String name) {
    269         if (name == null) {
    270             return "java.lang.Object";
    271         }
    272         if (name.startsWith("L")) {
    273             name = name.substring(1);
    274             if (name.endsWith(";"))
    275                 name = name.substring(0, name.length() - 1);
    276         }
    277         String answer = name.replace('/', '.');
    278         return answer;
    279     }
    280
    281     /***
    282      * @return the ASM method type descriptor
    283      */
    284     public static String getMethodDescriptor(String returnTypeName, Parameter[] paramTypeNames) {
    285         // lets avoid class loading
    286         StringBuffer buffer = new StringBuffer("(");
    287         for (int i = 0; i < paramTypeNames.length; i++) {
    288             buffer.append(getTypeDescription(paramTypeNames[i].getType()));
    289         }
    290         buffer.append(")");
    291         buffer.append(getTypeDescription(returnTypeName));
    292         return buffer.toString();
    293     }
    294
    295     /***
    296      * @return the ASM method type descriptor
    297      */
    298     public static String getMethodDescriptor(Class returnType, Class[] paramTypes) {
    299         // lets avoid class loading
    300         StringBuffer buffer = new StringBuffer("(");
    301         for (int i = 0; i < paramTypes.length; i++) {
    302             buffer.append(getTypeDescription(paramTypes[i]));
    303         }
    304         buffer.append(")");
    305         buffer.append(getTypeDescription(returnType));
    306         return buffer.toString();
    307     }
    308
    309     public static String getMethodDescriptor(Method meth) {
    310         return getMethodDescriptor(meth.getReturnType(), meth.getParameterTypes());
    311     }
    312
    313     public static String getTypeDescription(Class type) {
    314         if (type.isArray()) {
    315             return type.getName().replace('.', '/');
    316         }
    317         else {
    318             return getTypeDescription(type.getName());
    319         }
    320     }
    321
    322     /***
    323      * @return an array of ASM internal names of the type
    324      */
    325     public static String[] getClassInternalNames(String[] names) {
    326         int size = names.length;
    327         String[] answer = new String[size];
    328         for (int i = 0; i < size; i++) {
    329             answer[i] = getClassInternalName(names[i]);
    330         }
    331         return answer;
    332     }
    333
    334     protected void pushConstant(boolean value) {
    335         if (value) {
    336             cv.visitInsn(ICONST_1);
    337         }
    338         else {
    339             cv.visitInsn(ICONST_0);
    340         }
    341     }
    342
    343     protected void pushConstant(int value) {
    344         switch (value) {
    345             case 0 :
    346                 cv.visitInsn(ICONST_0);
    347                 break;
    348             case 1 :
    349                 cv.visitInsn(ICONST_1);
    350                 break;
    351             case 2 :
    352                 cv.visitInsn(ICONST_2);
    353                 break;
    354             case 3 :
    355                 cv.visitInsn(ICONST_3);
    356                 break;
    357             case 4 :
    358                 cv.visitInsn(ICONST_4);
    359                 break;
    360             case 5 :
    361                 cv.visitInsn(ICONST_5);
    362                 break;
    363             default :
    364                 if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
    365                     cv.visitIntInsn(BIPUSH, value);
    366                 }
    367                 else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
    368                     cv.visitIntInsn(SIPUSH, value);
    369                 }
    370                 else {
    371                     cv.visitLdcInsn(new Integer(value));
    372                 }
    373         }
    374     }
    375
    376     public void doCast(String type) {
    377         if (!type.equals("java.lang.Object")) {
    378             if (isPrimitiveType(type) && !type.equals("void")) {
    379                 unbox(type);
    380             }
    381             else {
    382                 cv.visitTypeInsn(
    383                     CHECKCAST,
    384                     type.endsWith("[]") ? getTypeDescription(type) : getClassInternalName(type));
    385             }
    386         }
    387     }
    388
    389     public void doCast(Class type) {
    390         String name = type.getName();
    391         if (type.isArray()) {
    392             name = type.getComponentType().getName() + "[]";
    393         }
    394         doCast(name);
    395     }
    396
    397     public void load(String type, int idx) {
    398         if (type.equals("double")) {
    399             cv.visitVarInsn(DLOAD, idx);
    400         }
    401         else if (type.equals("float")) {
    402             cv.visitVarInsn(FLOAD, idx);
    403         }
    404         else if (type.equals("long")) {
    405             cv.visitVarInsn(LLOAD, idx);
    406         }
    407         else if (
    408             type.equals("boolean")
    409                 || type.equals("char")
    410                 || type.equals("byte")
    411                 || type.equals("int")
    412                 || type.equals("short")) {
    413             cv.visitVarInsn(ILOAD, idx);
    414         }
    415         else {
    416             cv.visitVarInsn(ALOAD, idx);
    417         }
    418     }
    419
    420     public void load(Variable v) {
    421     	load(v.getTypeName(), v.getIndex());
    422     }
    423
    424     public void store(String type, int idx) {
    425         if (type.equals("double")) {
    426             cv.visitVarInsn(DSTORE, idx);
    427         }
    428         else if (type.equals("float")) {
    429             cv.visitVarInsn(FSTORE, idx);
    430         }
    431         else if (type.equals("long")) {
    432             cv.visitVarInsn(LSTORE, idx);
    433         }
    434         else if (
    435             type.equals("boolean")
    436                 || type.equals("char")
    437                 || type.equals("byte")
    438                 || type.equals("int")
    439                 || type.equals("short")) {
    440             cv.visitVarInsn(ISTORE, idx);
    441         }
    442         else {
    443             cv.visitVarInsn(ASTORE, idx);
    444         }
    445     }
    446
    447     public void store(Variable v, boolean markStart) {
    448         String type = v.getTypeName();
    449         int idx = v.getIndex();
    450
    451         if (type.equals("double")) {
    452             cv.visitVarInsn(DSTORE, idx);
    453         }
    454         else if (type.equals("float")) {
    455             cv.visitVarInsn(FSTORE, idx);
    456         }
    457         else if (type.equals("long")) {
    458             cv.visitVarInsn(LSTORE, idx);
    459         }
    460         else if (
    461             type.equals("boolean")
    462                 || type.equals("char")
    463                 || type.equals("byte")
    464                 || type.equals("int")
    465                 || type.equals("short")) {
    466             cv.visitVarInsn(ISTORE, idx);
    467         }
    468         else {
    469             cv.visitVarInsn(ASTORE, idx);
    470         }
    471         if (AsmClassGenerator2.CREATE_DEBUG_INFO && markStart) {
    472             Label l = v.getStartLabel();
    473             if (l != null) {
    474                 cv.visitLabel(l);
    475             } else {
    476                 System.out.println("start label == null! what to do about this?");
    477             }
    478         }
    479     }
    480
    481     public void store(Variable v) {
    482         store(v, false);
    483     }
    484
    485
    486     public static String getObjectTypeForPrimitive(String type) {
    487         if (type.equals("boolean")) {
    488             return Boolean.class.getName();
    489         }
    490         else if (type.equals("byte")) {
    491             return Byte.class.getName();
    492         }
    493         else if (type.equals("char")) {
    494             return Character.class.getName();
    495         }
    496         else if (type.equals("short")) {
    497             return Short.class.getName();
    498         }
    499         else if (type.equals("int")) {
    500             return Integer.class.getName();
    501         }
    502         else if (type.equals("long")) {
    503             return Long.class.getName();
    504         }
    505         else if (type.equals("float")) {
    506             return Float.class.getName();
    507         }
    508         else if (type.equals("double")) {
    509             return Double.class.getName();
    510         }
    511         else {
    512             return type;
    513         }
    514     }
    515
    516     /***
    517      *
    518      * @param type  "int[]" etc
    519      * @return "[I" format
    520      */
    521     public static String getObjectArrayTypeForPrimitiveArray(String type) {
    522         String prefix = "";
    523         while (type.endsWith("[]")) {
    524             prefix += "[";
    525             type = type.substring(0, type.length() - 2);
    526         }
    527
    528         if (prefix.length() ==0)
    529             return type;
    530
    531         String suffix = getObjectTypeForPrimitive(type);
    532         return prefix + "L" + suffix + ";";
    533     }
    534
    535
    536
    537     /***
    538      * load the constant on the operand stack. primitives auto-boxed.
    539      */
    540     void loadConstant (Object value) {
    541         if (value == null) {
    542             cv.visitInsn(ACONST_NULL);
    543         }
    544         else if (value instanceof String) {
    545             cv.visitLdcInsn(value);
    546         }
    547         else if (value instanceof Number) {
    548             /*** todo it would be more efficient to generate class constants */
    549             Number n = (Number) value;
    550             String className = BytecodeHelper.getClassInternalName(value.getClass().getName());
    551             cv.visitTypeInsn(NEW, className);
    552             cv.visitInsn(DUP);
    553             String methodType;
    554             if (n instanceof Double) {
    555             	cv.visitLdcInsn(n);
    556             	methodType = "(D)V";
    557             }
    558             else if (n instanceof Float) {
    559             	cv.visitLdcInsn(n);
    560             	methodType = "(F)V";
    561             }
    562             else if (n instanceof Long) {
    563             	cv.visitLdcInsn(n);
    564             	methodType = "(J)V";
    565             }
    566             else if (n instanceof BigDecimal) {
    567             	cv.visitLdcInsn(n.toString());
    568             	methodType = "(Ljava/lang/String;)V";
    569             }
    570             else if (n instanceof BigInteger) {
    571             	cv.visitLdcInsn(n.toString());
    572             	methodType = "(Ljava/lang/String;)V";
    573             }
    574             else if (n instanceof Integer){
    575             	//cv.visitLdcInsn(n);
    576                 pushConstant(n.intValue());
    577             	methodType = "(I)V";
    578         	}
    579             else
    580             {
    581         		throw new ClassGeneratorException(
    582         				"Cannot generate bytecode for constant: " + value
    583         				+ " of type: " + value.getClass().getName()
    584         				+".  Numeric constant type not supported.");
    585         	}
    586             cv.visitMethodInsn(INVOKESPECIAL, className, "<init>", methodType);
    587         }
    588         else if (value instanceof Boolean) {
    589             Boolean bool = (Boolean) value;
    590             String text = (bool.booleanValue()) ? "TRUE" : "FALSE";
    591             cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", text, "Ljava/lang/Boolean;");
    592         }
    593         else if (value instanceof Class) {
    594             Class vc = (Class) value;
    595             if (vc.getName().equals("java.lang.Void")) {
    596                 // load nothing here for void
    597             } else {
    598                 throw new ClassGeneratorException(
    599                 "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName());
    600             }
    601         }
    602         else {
    603             throw new ClassGeneratorException(
    604                 "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName());
    605         }
    606     }
    607
    608
    609     /***
    610      * load the value of the variable on the operand stack. unbox it if it's a reference
    611      * @param variable
    612      * @param holder
    613      */
    614     public void loadVar(Variable variable, boolean holder) {
    615 		String type = variable.getTypeName();
    616 		int index = variable.getIndex();
    617 		if (holder) {
    618 			cv.visitVarInsn(ALOAD, index);
    619 			cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
    620 		} else {
    621 			cv.visitVarInsn(ALOAD, index); // todo? shall xload based on the type?
    622 		}
    623 	}
    624
    625     public void storeVar(Variable variable, boolean holder) {
    626         String  type   = variable.getTypeName();
    627         int     index  = variable.getIndex();
    628
    629     	if (holder) {
    630             //int tempIndex = visitASTOREInTemp("reference", type);
    631             cv.visitVarInsn(ALOAD, index);
    632             cv.visitInsn(SWAP);  // assuming the value on stack is single word
    633             //cv.visitVarInsn(ALOAD, tempIndex);
    634             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
    635         }
    636         else {
    637             store(variable.deriveBoxedVersion()); // todo br seems right hand values on the stack are always object refs, primitives boxed
    638 //            if (!varStored) {
    639 //                //visitVariableStartLabel(variable);
    640 //                varStored = true;
    641 //            }
    642         }
    643     }
    644
    645 //    private int visitASTOREInTemp(String name, String type) {
    646 //        Variable var  = defineVariable(createVariableName(name), type, false);
    647 //        int varIdx = var.getIndex();
    648 //        cv.visitVarInsn(ASTORE, varIdx);
    649 //        if (CREATE_DEBUG_INFO) cv.visitLabel(var.getStartLabel());
    650 //        return varIdx;
    651 //    }
    652
    653     public void putField(FieldNode fld) {
    654     	putField(fld, getClassInternalName(fld.getOwner()));
    655     }
    656
    657     public void putField(FieldNode fld, String ownerName) {
    658     	cv.visitFieldInsn(PUTFIELD, ownerName, fld.getName(), getTypeDescription(fld.getType()));
    659     }
    660
    661     public void loadThis() {
    662         cv.visitVarInsn(ALOAD, 0);
    663     }
    664
    665     public static Class boxOnPrimitive(Class cls) {
    666         Class ans = cls;
    667         if (ans == null)
    668             return null;
    669
    670         if (cls.isPrimitive() && cls != void.class) {
    671             if (cls == int.class) {
    672                 ans = Integer.class;
    673             }
    674             else if (cls == byte.class) {
    675                 ans = Byte.class;
    676             }
    677             else if (cls == char.class) {
    678                 ans = Character.class;
    679             }
    680             else if (cls == short.class) {
    681                 ans = Short.class;
    682             }
    683             else if (cls == boolean.class) {
    684                 ans = Boolean.class;
    685             }
    686             else if (cls == float.class) {
    687                 ans = Float.class;
    688             }
    689             else if (cls == long.class) {
    690                 ans = Long.class;
    691             }
    692             else if (cls == double.class) {
    693                 ans = Double.class;
    694             }
    695         }
    696         else if (cls.isArray()){
    697             // let's convert primitive array too
    698             int dimension = 0;
    699             Class elemType = null;
    700             do {
    701                 ++dimension;
    702                 elemType = cls.getComponentType();
    703             } while(elemType.isArray());
    704
    705             if (elemType.isPrimitive()) {
    706                 Class boxElem = null;
    707                 if (elemType == int.class) {
    708                     boxElem = Integer.class;
    709                 }
    710                 else if (elemType == byte.class) {
    711                     boxElem = Byte.class;
    712                 }
    713                 else if (elemType == char.class) {
    714                     boxElem = Character.class;
    715                 }
    716                 else if (elemType == short.class) {
    717                     boxElem = Short.class;
    718                 }
    719                 else if (elemType == boolean.class) {
    720                     boxElem = Boolean.class;
    721                 }
    722                 else if (elemType == float.class) {
    723                     boxElem = Float.class;
    724                 }
    725                 else if (elemType == long.class) {
    726                     boxElem = Long.class;
    727                 }
    728                 else if (elemType == double.class) {
    729                     boxElem = Double.class;
    730                 }
    731                 // I need to construct a new array type for the box version
    732                 String typeName = "";
    733                 for (int i = 0; i < dimension; i++){
    734                     typeName += "[";
    735                 }
    736                 typeName += "L" + boxElem.getName() + ";";
    737                 try {
    738                     return Class.forName(typeName);
    739                 } catch (ClassNotFoundException e) {
    740                     throw new RuntimeException(e); // should never have come here
    741                 }
    742             }
    743         }
    744         return ans;
    745     }
    746
    747     /***
    748      * create the bytecode to invoke a method
    749      * @param meth the method object to invoke
    750      */
    751     public void invoke(Method meth) {
    752         int op = Modifier.isStatic(meth.getModifiers()) ?
    753                     INVOKESTATIC :
    754                     (meth.getDeclaringClass().isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL);
    755
    756         cv.visitMethodInsn(
    757                 op,
    758                 getClassInternalName(meth.getDeclaringClass().getName()),
    759                 meth.getName(),
    760                 getMethodDescriptor(meth)
    761                 );
    762     }
    763
    764     /***
    765      * convert boolean to Boolean
    766      */
    767     public void boxBoolean() {
    768         Label l0 = new Label();
    769         cv.visitJumpInsn(IFEQ, l0);
    770         cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
    771         Label l1 = new Label();
    772         cv.visitJumpInsn(GOTO, l1);
    773         cv.visitLabel(l0);
    774         cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
    775         cv.visitLabel(l1);
    776     }
    777
    778     public static String getMethodDescriptor(MetaMethod metamethod) {
    779         return getMethodDescriptor(metamethod.getReturnType(), metamethod.getParameterTypes());
    780     }
    781
    782     /***
    783      * load a message on the stack and remove it right away. Good for put a mark in the generated bytecode for debugging purpose.
    784      * @param msg
    785      */
    786     public void mark(String msg) {
    787         cv.visitLdcInsn(msg);
    788         cv.visitInsn(POP);
    789     }
    790
    791     /***
    792      * returns a name that Class.forName() can take. Notablely for arrays:
    793      * [I, [Ljava.lang.String; etc
    794      * Regular object type:  java.lang.String
    795      * @param name
    796      * @return
    797      */
    798     public static String formatNameForClassLoading(String name) {
    799         if (name.equals("int")
    800         		|| name.equals("long")
    801 				|| name.equals("short")
    802 				|| name.equals("float")
    803 				|| name.equals("double")
    804 				|| name.equals("byte")
    805 				|| name.equals("char")
    806 				|| name.equals("boolean")
    807 				|| name.equals("void")
    808         	) {
    809             return name;
    810         }
    811
    812         if (name == null) {
    813             return "java.lang.Object;";
    814         }
    815
    816         if (name.startsWith("[")) {
    817             return name.replace('/', '.');
    818         }
    819
    820         if (name.startsWith("L")) {
    821         	name = name.substring(1);
    822         	if (name.endsWith(";")) {
    823         		name = name.substring(0, name.length() - 1);
    824         	}
    825         	return name.replace('/', '.');
    826         }
    827
    828         String prefix = "";
    829         if (name.endsWith("[]")) { // todo need process multi
    830             prefix = "[";
    831             name = name.substring(0, name.length() - 2);
    832             if (name.equals("int")) {
    833                 return prefix + "I";
    834             }
    835             else if (name.equals("long")) {
    836                 return prefix + "J";
    837             }
    838             else if (name.equals("short")) {
    839                 return prefix + "S";
    840             }
    841             else if (name.equals("float")) {
    842                 return prefix + "F";
    843             }
    844             else if (name.equals("double")) {
    845                 return prefix + "D";
    846             }
    847             else if (name.equals("byte")) {
    848                 return prefix + "B";
    849             }
    850             else if (name.equals("char")) {
    851                 return prefix + "C";
    852             }
    853             else if (name.equals("boolean")) {
    854                 return prefix + "Z";
    855             }
    856             else {
    857             	return prefix + "L" + name.replace('/', '.') + ";";
    858             }
    859         }
    860         return name.replace('/', '.');
    861
    862     }
    863
    864     public void dup() {
    865         cv.visitInsn(DUP);
    866     }
    867 }