001    
002    /*
003     $Id: BytecodeHelper.java,v 1.19 2005/05/27 10:13:09 russel Exp $
004    
005     Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
006    
007     Redistribution and use of this software and associated documentation
008     ("Software"), with or without modification, are permitted provided
009     that the following conditions are met:
010    
011     1. Redistributions of source code must retain copyright
012        statements and notices.  Redistributions must also contain a
013        copy of this document.
014    
015     2. Redistributions in binary form must reproduce the
016        above copyright notice, this list of conditions and the
017        following disclaimer in the documentation and/or other
018        materials provided with the distribution.
019    
020     3. The name "groovy" must not be used to endorse or promote
021        products derived from this Software without prior written
022        permission of The Codehaus.  For written permission,
023        please contact info@codehaus.org.
024    
025     4. Products derived from this Software may not be called "groovy"
026        nor may "groovy" appear in their names without prior written
027        permission of The Codehaus. "groovy" is a registered
028        trademark of The Codehaus.
029    
030     5. Due credit should be given to The Codehaus -
031        http://groovy.codehaus.org/
032    
033     THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
034     ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
035     NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
036     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
037     THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
038     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
039     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
040     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
041     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
042     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
043     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
044     OF THE POSSIBILITY OF SUCH DAMAGE.
045    
046     */
047    package org.codehaus.groovy.classgen;
048    
049    import groovy.lang.MetaMethod;
050    
051    import java.lang.reflect.Method;
052    import java.lang.reflect.Modifier;
053    import java.math.BigDecimal;
054    import java.math.BigInteger;
055    
056    import org.codehaus.groovy.ast.FieldNode;
057    import org.codehaus.groovy.ast.Parameter;
058    import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
059    import org.objectweb.asm.MethodVisitor;
060    import org.objectweb.asm.Opcodes;
061    import org.objectweb.asm.Label;
062    
063    
064    /**
065     * A helper class for bytecode generation with AsmClassGenerator.
066     * 
067     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
068     * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
069     * @version $Revision: 1.19 $
070     */
071    public class BytecodeHelper implements Opcodes {
072    
073        private MethodVisitor cv;
074    
075        public MethodVisitor getMethodVisitor() {
076            return cv;
077        }
078    
079        public BytecodeHelper(MethodVisitor cv) {
080            this.cv = cv;
081        }
082    
083        /**
084         * Generates the bytecode to autobox the current value on the stack
085         */
086        public void box(Class type) {
087            if (type.isPrimitive() && type != void.class) {
088                String returnString = "(" + getTypeDescription(type.getName()) + ")Ljava/lang/Object;";
089                cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(ScriptBytecodeAdapter.class.getName()), "box", returnString);
090            }
091        }
092    
093        /**
094         * box the primitive value on the stack
095         * @param cls
096         */
097        public void quickBoxIfNecessary(Class cls) {
098            String type = cls.getName();
099            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    }