View Javadoc

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