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 }