001    /*
002     $Id: BytecodeHelper.java 4287 2006-12-01 13:00:13Z blackdrag $
003    
004     Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005    
006     Redistribution and use of this software and associated documentation
007     ("Software"), with or without modification, are permitted provided
008     that the following conditions are met:
009    
010     1. Redistributions of source code must retain copyright
011        statements and notices.  Redistributions must also contain a
012        copy of this document.
013    
014     2. Redistributions in binary form must reproduce the
015        above copyright notice, this list of conditions and the
016        following disclaimer in the documentation and/or other
017        materials provided with the distribution.
018    
019     3. The name "groovy" must not be used to endorse or promote
020        products derived from this Software without prior written
021        permission of The Codehaus.  For written permission,
022        please contact info@codehaus.org.
023    
024     4. Products derived from this Software may not be called "groovy"
025        nor may "groovy" appear in their names without prior written
026        permission of The Codehaus. "groovy" is a registered
027        trademark of The Codehaus.
028    
029     5. Due credit should be given to The Codehaus -
030        http://groovy.codehaus.org/
031    
032     THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033     ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034     NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036     THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043     OF THE POSSIBILITY OF SUCH DAMAGE.
044    
045     */
046    package org.codehaus.groovy.classgen;
047    
048    import java.math.BigDecimal;
049    import java.math.BigInteger;
050    
051    import org.codehaus.groovy.ast.ClassHelper;
052    import org.codehaus.groovy.ast.ClassNode;
053    import org.codehaus.groovy.ast.FieldNode;
054    import org.codehaus.groovy.ast.Parameter;
055    import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
056    import org.objectweb.asm.MethodVisitor;
057    import org.objectweb.asm.Opcodes;
058    import org.objectweb.asm.Label;
059    
060    /**
061     * A helper class for bytecode generation with AsmClassGenerator.
062     * 
063     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
064     * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
065     * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
066     * @version $Revision: 4287 $
067     */
068    public class BytecodeHelper implements Opcodes {
069    
070        private MethodVisitor cv;
071    
072        public MethodVisitor getMethodVisitor() {
073            return cv;
074        }
075    
076        public BytecodeHelper(MethodVisitor cv) {
077            this.cv = cv;
078        }
079        
080        /**
081         * box the primitive value on the stack
082         * @param type
083         */
084        public void quickBoxIfNecessary(ClassNode type) {
085            String descr = getTypeDescription(type);
086            if (type == ClassHelper.boolean_TYPE) {
087                boxBoolean();
088            }
089            else if (ClassHelper.isPrimitiveType(type) && type != ClassHelper.VOID_TYPE) {
090                ClassNode wrapper = ClassHelper.getWrapper(type);
091                String internName = getClassInternalName(wrapper);
092                cv.visitTypeInsn(NEW, internName);
093                cv.visitInsn(DUP);
094                if (type==ClassHelper.double_TYPE || type==ClassHelper.long_TYPE) {
095                    cv.visitInsn(DUP2_X2);
096                    cv.visitInsn(POP2);
097                } else {
098                    cv.visitInsn(DUP2_X1);
099                    cv.visitInsn(POP2);
100                }
101                cv.visitMethodInsn(INVOKESPECIAL, internName, "<init>", "(" + descr + ")V");
102            }
103        }
104        
105        public void quickUnboxIfNecessary(ClassNode type){
106            if (ClassHelper.isPrimitiveType(type) && type != ClassHelper.VOID_TYPE) { // todo care when BigDecimal or BigIneteger on stack
107                ClassNode wrapper = ClassHelper.getWrapper(type);
108                String internName = getClassInternalName(wrapper);
109                if (type == ClassHelper.boolean_TYPE) {
110                    cv.visitTypeInsn(CHECKCAST, internName);
111                    cv.visitMethodInsn(INVOKEVIRTUAL, internName, type.getName() + "Value", "()" + getTypeDescription(type));
112                } else { // numbers
113                    cv.visitTypeInsn(CHECKCAST, "java/lang/Number");
114                    cv.visitMethodInsn(INVOKEVIRTUAL, /*internName*/"java/lang/Number", type.getName() + "Value", "()" + getTypeDescription(type));
115                }
116            }
117        }
118        
119        /**
120         * Generates the bytecode to autobox the current value on the stack
121         */
122        public void box(Class type) {
123            if (type.isPrimitive() && type != void.class) {
124                String returnString = "(" + getTypeDescription(type) + ")Ljava/lang/Object;";
125                cv.visitMethodInsn(INVOKESTATIC, getClassInternalName(DefaultTypeTransformation.class.getName()), "box", returnString);
126            }
127        }
128    
129        public void box(ClassNode type) {
130            if (type.isPrimaryClassNode()) return;
131            box(type.getTypeClass());
132        }
133    
134        /**
135         * Generates the bytecode to unbox the current value on the stack
136         */
137        public void unbox(Class type) {
138            if (type.isPrimitive() && type != Void.TYPE) {
139                String returnString = "(Ljava/lang/Object;)" + getTypeDescription(type);
140                cv.visitMethodInsn(
141                    INVOKESTATIC,
142                    getClassInternalName(DefaultTypeTransformation.class.getName()),
143                    type.getName() + "Unbox",
144                    returnString);
145            }
146        }
147        
148        public void unbox(ClassNode type) {
149            if (type.isPrimaryClassNode()) return;
150            unbox(type.getTypeClass());
151        }
152    
153        public static String getClassInternalName(ClassNode t){
154            if (t.isPrimaryClassNode()){
155                    return getClassInternalName(t.getName());
156            }
157            return getClassInternalName(t.getTypeClass());
158        }
159        
160        public static String getClassInternalName(Class t) {
161            return org.objectweb.asm.Type.getInternalName(t);
162        }
163        
164        /**
165         * @return the ASM internal name of the type
166         */
167        public static String getClassInternalName(String name) {
168            return name.replace('.', '/');
169        }
170        
171        /**
172         * @return the ASM method type descriptor
173         */
174        public static String getMethodDescriptor(ClassNode returnType, Parameter[] parameters) {
175            StringBuffer buffer = new StringBuffer("(");
176            for (int i = 0; i < parameters.length; i++) {
177                buffer.append(getTypeDescription(parameters[i].getType()));
178            }
179            buffer.append(")");
180            buffer.append(getTypeDescription(returnType));
181            return buffer.toString();
182        }
183    
184        /**
185         * @return the ASM method type descriptor
186         */
187        public static String getMethodDescriptor(Class returnType, Class[] paramTypes) {
188            // lets avoid class loading
189            StringBuffer buffer = new StringBuffer("(");
190            for (int i = 0; i < paramTypes.length; i++) {
191                buffer.append(getTypeDescription(paramTypes[i]));
192            }
193            buffer.append(")");
194            buffer.append(getTypeDescription(returnType));
195            return buffer.toString();
196        }
197    
198        public static String getTypeDescription(Class c) {
199            return org.objectweb.asm.Type.getDescriptor(c);
200        }
201        
202        /**
203         * array types are special:
204         * eg.: String[]: classname: [Ljava.lang.String;
205         *      Object:   classname: java.lang.Object
206         *      int[] :   classname: [I
207         * unlike getTypeDescription '.' is not replaces by '/'. 
208         * it seems that makes problems for
209         * the class loading if '.' is replaced by '/'
210         * @return the ASM type description for class loading
211         */
212        public static String getClassLoadingTypeDescription(ClassNode c) {
213            StringBuffer buf = new StringBuffer();
214            boolean array = false;
215            while (true) {
216                if (c.isArray()) {
217                    buf.append('[');
218                    c = c.getComponentType();
219                    array = true;
220                } else {
221                    if (ClassHelper.isPrimitiveType(c)) {
222                        buf.append(getTypeDescription(c));
223                    } else {
224                        if (array) buf.append('L');
225                        buf.append(c.getName());
226                        if(array) buf.append(';');
227                    }
228                    return buf.toString();
229                }
230            }
231        }
232        
233        /**
234         * array types are special:
235         * eg.: String[]: classname: [Ljava/lang/String;
236         *      int[]: [I
237         * @return the ASM type description
238         */
239        public static String getTypeDescription(ClassNode c) {
240            StringBuffer buf = new StringBuffer();
241            ClassNode d = c;
242            while (true) {
243                if (ClassHelper.isPrimitiveType(d)) {
244                    char car;
245                    if (d == ClassHelper.int_TYPE) {
246                        car = 'I';
247                    } else if (d == ClassHelper.VOID_TYPE) {
248                        car = 'V';
249                    } else if (d == ClassHelper.boolean_TYPE) {
250                        car = 'Z';
251                    } else if (d == ClassHelper.byte_TYPE) {
252                        car = 'B';
253                    } else if (d == ClassHelper.char_TYPE) {
254                        car = 'C';
255                    } else if (d == ClassHelper.short_TYPE) {
256                        car = 'S';
257                    } else if (d == ClassHelper.double_TYPE) {
258                        car = 'D';
259                    } else if (d == ClassHelper.float_TYPE) {
260                        car = 'F';
261                    } else /* long */{
262                        car = 'J';
263                    }
264                    buf.append(car);
265                    return buf.toString();
266                } else if (d.isArray()) {
267                    buf.append('[');
268                    d = d.getComponentType();
269                } else {
270                    buf.append('L');
271                    String name = d.getName();
272                    int len = name.length();
273                    for (int i = 0; i < len; ++i) {
274                        char car = name.charAt(i);
275                        buf.append(car == '.' ? '/' : car);
276                    }
277                    buf.append(';');
278                    return buf.toString();
279                }
280            }
281        }
282    
283        /**
284         * @return an array of ASM internal names of the type
285         */
286        public static String[] getClassInternalNames(ClassNode[] names) {
287            int size = names.length;
288            String[] answer = new String[size];
289            for (int i = 0; i < size; i++) {
290                answer[i] = getClassInternalName(names[i]);
291            }
292            return answer;
293        }
294    
295        protected void pushConstant(boolean value) {
296            if (value) {
297                cv.visitInsn(ICONST_1);
298            }
299            else {
300                cv.visitInsn(ICONST_0);
301            }
302        }
303    
304        protected void pushConstant(int value) {
305            switch (value) {
306                case 0 :
307                    cv.visitInsn(ICONST_0);
308                    break;
309                case 1 :
310                    cv.visitInsn(ICONST_1);
311                    break;
312                case 2 :
313                    cv.visitInsn(ICONST_2);
314                    break;
315                case 3 :
316                    cv.visitInsn(ICONST_3);
317                    break;
318                case 4 :
319                    cv.visitInsn(ICONST_4);
320                    break;
321                case 5 :
322                    cv.visitInsn(ICONST_5);
323                    break;
324                default :
325                    if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
326                        cv.visitIntInsn(BIPUSH, value);
327                    }
328                    else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
329                        cv.visitIntInsn(SIPUSH, value);
330                    }
331                    else {
332                        cv.visitLdcInsn(new Integer(value));
333                    }
334            }
335        }
336    
337        public void doCast(Class type) {
338            if (type!=Object.class) {
339                if (type.isPrimitive() && type!=Void.TYPE) {
340                    unbox(type);
341                }
342                else {
343                    cv.visitTypeInsn(
344                        CHECKCAST,
345                        type.isArray() ? getTypeDescription(type) : getClassInternalName(type.getName()));
346                }
347            }
348        }
349        
350        public void doCast(ClassNode type) {
351            if (type==ClassHelper.OBJECT_TYPE) return;
352            if (ClassHelper.isPrimitiveType(type) && type!=ClassHelper.VOID_TYPE) {
353                unbox(type);
354            }
355            else {
356                cv.visitTypeInsn(
357                        CHECKCAST,
358                        type.isArray() ? getTypeDescription(type) : getClassInternalName(type));
359            }
360        }
361    
362        public void load(ClassNode type, int idx) {
363            if (type==ClassHelper.double_TYPE) {
364                cv.visitVarInsn(DLOAD, idx);
365            }
366            else if (type==ClassHelper.float_TYPE) {
367                cv.visitVarInsn(FLOAD, idx);
368            }
369            else if (type==ClassHelper.long_TYPE) {
370                cv.visitVarInsn(LLOAD, idx);
371            }
372            else if (
373                type==ClassHelper.boolean_TYPE
374                    || type==ClassHelper.char_TYPE
375                    || type==ClassHelper.byte_TYPE
376                    || type==ClassHelper.int_TYPE
377                    || type==ClassHelper.short_TYPE)
378            {    
379                cv.visitVarInsn(ILOAD, idx);
380            }
381            else {
382                cv.visitVarInsn(ALOAD, idx);
383            }
384        }
385    
386        public void load(Variable v) {
387            load(v.getType(), v.getIndex());
388        }
389    
390        public void store(Variable v, boolean markStart) {
391            ClassNode type = v.getType();
392            unbox(type);
393            int idx = v.getIndex();
394    
395            if (type==ClassHelper.double_TYPE) {
396                cv.visitVarInsn(DSTORE, idx);
397            }
398            else if (type==ClassHelper.float_TYPE) {
399                cv.visitVarInsn(FSTORE, idx);
400            }
401            else if (type==ClassHelper.long_TYPE) {
402                cv.visitVarInsn(LSTORE, idx);
403            }
404            else if (
405                    type==ClassHelper.boolean_TYPE
406                    || type==ClassHelper.char_TYPE
407                    || type==ClassHelper.byte_TYPE
408                    || type==ClassHelper.int_TYPE
409                    || type==ClassHelper.short_TYPE) {
410                cv.visitVarInsn(ISTORE, idx);
411            }
412            else {
413                cv.visitVarInsn(ASTORE, idx);
414            }
415        }
416    
417        public void store(Variable v) {
418            store(v, false);
419        }
420    
421        /**
422         * load the constant on the operand stack. primitives auto-boxed.
423         */
424        void loadConstant (Object value) {
425            if (value == null) {
426                cv.visitInsn(ACONST_NULL);
427            }
428            else if (value instanceof String) {
429                cv.visitLdcInsn(value);
430            }
431            else if (value instanceof Character) {
432                String className = "java/lang/Character";
433                cv.visitTypeInsn(NEW, className);
434                cv.visitInsn(DUP);
435                cv.visitLdcInsn(value);
436                String methodType = "(C)V";
437                cv.visitMethodInsn(INVOKESPECIAL, className, "<init>", methodType);
438            }
439            else if (value instanceof Number) {
440                /** todo it would be more efficient to generate class constants */
441                Number n = (Number) value;
442                String className = BytecodeHelper.getClassInternalName(value.getClass().getName());
443                cv.visitTypeInsn(NEW, className);
444                cv.visitInsn(DUP);
445                String methodType;
446                if (n instanceof Integer) {
447                    //pushConstant(n.intValue());
448                    cv.visitLdcInsn(n);
449                    methodType = "(I)V";
450                    }
451                else if (n instanceof Double) {
452                    cv.visitLdcInsn(n);
453                    methodType = "(D)V";
454                }
455                else if (n instanceof Float) {
456                    cv.visitLdcInsn(n);
457                    methodType = "(F)V";
458                }
459                else if (n instanceof Long) {
460                    cv.visitLdcInsn(n);
461                    methodType = "(J)V";
462                }
463                else if (n instanceof BigDecimal) {
464                    cv.visitLdcInsn(n.toString());
465                    methodType = "(Ljava/lang/String;)V";
466                }
467                else if (n instanceof BigInteger) {
468                    cv.visitLdcInsn(n.toString());
469                    methodType = "(Ljava/lang/String;)V";
470                }
471                else if (n instanceof Short) {
472                    cv.visitLdcInsn(n);
473                    methodType = "(S)V";
474                }
475                else if (n instanceof Byte) {
476                    cv.visitLdcInsn(n);
477                    methodType = "(B)V";
478                }
479                else {
480                    throw new ClassGeneratorException(
481                                   "Cannot generate bytecode for constant: " + value
482                                 + " of type: " + value.getClass().getName()
483                                 + ".  Numeric constant type not supported.");
484                }
485                cv.visitMethodInsn(INVOKESPECIAL, className, "<init>", methodType);
486            }
487            else if (value instanceof Boolean) {
488                Boolean bool = (Boolean) value;
489                String text = (bool.booleanValue()) ? "TRUE" : "FALSE";
490                cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", text, "Ljava/lang/Boolean;");
491            }
492            else if (value instanceof Class) {
493                Class vc = (Class) value;
494                if (vc.getName().equals("java.lang.Void")) {
495                    // load nothing here for void
496                } else {
497                    throw new ClassGeneratorException(
498                    "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName());
499                }
500            }
501            else {
502                throw new ClassGeneratorException(
503                    "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName());
504            }
505        }
506    
507    
508        /**
509         * load the value of the variable on the operand stack. unbox it if it's a reference
510         * @param variable
511         */
512        public void loadVar(Variable variable) {
513                    int index = variable.getIndex();
514                    if (variable.isHolder()) {
515                            cv.visitVarInsn(ALOAD, index);
516                            cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
517                    } else {
518                load(variable);
519                if (variable!=Variable.THIS_VARIABLE && variable!=Variable.SUPER_VARIABLE) {
520                    box(variable.getType());
521                }
522                    }
523            }
524        
525        public void storeVar(Variable variable) {
526            String  type   = variable.getTypeName();
527            int     index  = variable.getIndex();
528            
529            if (variable.isHolder()) {
530                cv.visitVarInsn(ALOAD, index);
531                cv.visitInsn(SWAP);  
532                cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
533            }
534            else {
535                store(variable,false);
536            }
537        }
538    
539        public void putField(FieldNode fld) {
540            putField(fld, getClassInternalName(fld.getOwner()));
541        }
542    
543        public void putField(FieldNode fld, String ownerName) {
544            cv.visitFieldInsn(PUTFIELD, ownerName, fld.getName(), getTypeDescription(fld.getType()));
545        }
546        
547        public void swapObjectWith(ClassNode type) {
548            if (type==ClassHelper.long_TYPE || type==ClassHelper.double_TYPE) {
549                cv.visitInsn(DUP_X2);
550                cv.visitInsn(POP);
551            } else {
552                cv.visitInsn(SWAP);
553            }
554        }
555        
556        public void swapWithObject(ClassNode type) {
557            if (type==ClassHelper.long_TYPE || type==ClassHelper.double_TYPE) {
558                cv.visitInsn(DUP2_X1);
559                cv.visitInsn(POP2);
560            } else {
561                cv.visitInsn(SWAP);
562            }
563        }
564    
565        public static ClassNode boxOnPrimitive(ClassNode type) {
566            if (!type.isArray()) return ClassHelper.getWrapper(type);
567            return boxOnPrimitive(type.getComponentType()).makeArray();
568        }
569    
570        /**
571         * convert boolean to Boolean
572         */
573        public void boxBoolean() {
574            Label l0 = new Label();
575            cv.visitJumpInsn(IFEQ, l0);
576            cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
577            Label l1 = new Label();
578            cv.visitJumpInsn(GOTO, l1);
579            cv.visitLabel(l0);
580            cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
581            cv.visitLabel(l1);
582        }
583    
584        /**
585         * negate a boolean on stack. true->false, false->true
586         */
587        public void negateBoolean(){
588            // code to negate the primitive boolean
589            Label endLabel = new Label();
590            Label falseLabel = new Label();
591            cv.visitJumpInsn(IFNE,falseLabel);
592            cv.visitInsn(ICONST_1);
593            cv.visitJumpInsn(GOTO,endLabel);
594            cv.visitLabel(falseLabel);
595            cv.visitInsn(ICONST_0);
596            cv.visitLabel(endLabel);
597        }
598    
599        /**
600         * load a message on the stack and remove it right away. Good for put a mark in the generated bytecode for debugging purpose.
601         * @param msg
602         */
603        public void mark(String msg) {
604            cv.visitLdcInsn(msg);
605            cv.visitInsn(POP);
606        }
607        
608        /**
609         * returns a name that Class.forName() can take. Notablely for arrays:
610         * [I, [Ljava.lang.String; etc
611         * Regular object type:  java.lang.String
612         * @param name
613         */
614        public static String formatNameForClassLoading(String name) {
615            if (name.equals("int")
616                            || name.equals("long")
617                                    || name.equals("short")
618                                    || name.equals("float")
619                                    || name.equals("double")
620                                    || name.equals("byte")
621                                    || name.equals("char")
622                                    || name.equals("boolean")
623                                    || name.equals("void")
624                    ) {
625                return name;
626            }
627    
628            if (name == null) {
629                return "java.lang.Object;";
630            }
631    
632            if (name.startsWith("[")) {
633                return name.replace('/', '.');
634            }
635            
636            if (name.startsWith("L")) {
637                    name = name.substring(1);
638                    if (name.endsWith(";")) {
639                            name = name.substring(0, name.length() - 1);
640                    }
641                    return name.replace('/', '.');
642            }
643    
644            String prefix = "";
645            if (name.endsWith("[]")) { // todo need process multi
646                prefix = "[";
647                name = name.substring(0, name.length() - 2);
648                if (name.equals("int")) {
649                    return prefix + "I";
650                }
651                else if (name.equals("long")) {
652                    return prefix + "J";
653                }
654                else if (name.equals("short")) {
655                    return prefix + "S";
656                }
657                else if (name.equals("float")) {
658                    return prefix + "F";
659                }
660                else if (name.equals("double")) {
661                    return prefix + "D";
662                }
663                else if (name.equals("byte")) {
664                    return prefix + "B";
665                }
666                else if (name.equals("char")) {
667                    return prefix + "C";
668                }
669                else if (name.equals("boolean")) {
670                    return prefix + "Z";
671                }
672                else {
673                    return prefix + "L" + name.replace('/', '.') + ";";
674                }
675            }
676            return name.replace('/', '.');
677    
678        }
679    
680        public void dup() {
681            cv.visitInsn(DUP);
682        }
683    
684        public void doReturn(ClassNode returnType) {
685            if (returnType==ClassHelper.double_TYPE) {
686                cv.visitInsn(DRETURN);
687            } else if (returnType==ClassHelper.float_TYPE) {
688                cv.visitInsn(FRETURN);
689            } else if (returnType==ClassHelper.long_TYPE) {
690                cv.visitInsn(LRETURN);
691            } else if (
692                       returnType==ClassHelper.boolean_TYPE
693                    || returnType==ClassHelper.char_TYPE
694                    || returnType==ClassHelper.byte_TYPE
695                    || returnType==ClassHelper.int_TYPE
696                    || returnType==ClassHelper.short_TYPE) 
697            { 
698                //byte,short,boolean,int are all IRETURN
699                cv.visitInsn(IRETURN);
700            } else if (returnType==ClassHelper.VOID_TYPE){
701                cv.visitInsn(RETURN);
702            } else {
703                cv.visitInsn(ARETURN);
704            }
705            
706        }
707        
708    }