001/***
002 * ASM: a very small and fast Java bytecode manipulation framework
003 * Copyright (c) 2000-2011 INRIA, France Telecom
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions
008 * are met:
009 * 1. Redistributions of source code must retain the above copyright
010 *    notice, this list of conditions and the following disclaimer.
011 * 2. Redistributions in binary form must reproduce the above copyright
012 *    notice, this list of conditions and the following disclaimer in the
013 *    documentation and/or other materials provided with the distribution.
014 * 3. Neither the name of the copyright holders nor the names of its
015 *    contributors may be used to endorse or promote products derived from
016 *    this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
028 * THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package io.ebean.enhance.asm.commons;
031
032import io.ebean.enhance.asm.ClassVisitor;
033import io.ebean.enhance.asm.Handle;
034import io.ebean.enhance.asm.Label;
035import io.ebean.enhance.asm.MethodVisitor;
036import io.ebean.enhance.asm.Opcodes;
037import io.ebean.enhance.asm.Type;
038
039import java.util.ArrayList;
040import java.util.Arrays;
041import java.util.List;
042
043/**
044 * A {@link MethodVisitor} with convenient methods to generate
045 * code. For example, using this adapter, the class below
046 * 
047 * <pre>
048 * public class Example {
049 *     public static void main(String[] args) {
050 *         System.out.println(&quot;Hello world!&quot;);
051 *     }
052 * }
053 * </pre>
054 * 
055 * can be generated as follows:
056 * 
057 * <pre>
058 * ClassWriter cw = new ClassWriter(true);
059 * cw.visit(V1_1, ACC_PUBLIC, &quot;Example&quot;, null, &quot;java/lang/Object&quot;, null);
060 * 
061 * Method m = Method.getMethod(&quot;void &lt;init&gt; ()&quot;);
062 * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
063 * mg.loadThis();
064 * mg.invokeConstructor(Type.getType(Object.class), m);
065 * mg.returnValue();
066 * mg.endMethod();
067 * 
068 * m = Method.getMethod(&quot;void main (String[])&quot;);
069 * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
070 * mg.getStatic(Type.getType(System.class), &quot;out&quot;, Type.getType(PrintStream.class));
071 * mg.push(&quot;Hello world!&quot;);
072 * mg.invokeVirtual(Type.getType(PrintStream.class),
073 *         Method.getMethod(&quot;void println (String)&quot;));
074 * mg.returnValue();
075 * mg.endMethod();
076 * 
077 * cw.visitEnd();
078 * </pre>
079 * 
080 * @author Juozas Baliuka
081 * @author Chris Nokleberg
082 * @author Eric Bruneton
083 * @author Prashant Deva
084 */
085public class GeneratorAdapter extends LocalVariablesSorter {
086
087    private static final String CLDESC = "Ljava/lang/Class;";
088
089    private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
090
091    private static final Type BOOLEAN_TYPE = Type
092            .getObjectType("java/lang/Boolean");
093
094    private static final Type SHORT_TYPE = Type
095            .getObjectType("java/lang/Short");
096
097    private static final Type CHARACTER_TYPE = Type
098            .getObjectType("java/lang/Character");
099
100    private static final Type INTEGER_TYPE = Type
101            .getObjectType("java/lang/Integer");
102
103    private static final Type FLOAT_TYPE = Type
104            .getObjectType("java/lang/Float");
105
106    private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long");
107
108    private static final Type DOUBLE_TYPE = Type
109            .getObjectType("java/lang/Double");
110
111    private static final Type NUMBER_TYPE = Type
112            .getObjectType("java/lang/Number");
113
114    private static final Type OBJECT_TYPE = Type
115            .getObjectType("java/lang/Object");
116
117    private static final Method BOOLEAN_VALUE = Method
118            .getMethod("boolean booleanValue()");
119
120    private static final Method CHAR_VALUE = Method
121            .getMethod("char charValue()");
122
123    private static final Method INT_VALUE = Method.getMethod("int intValue()");
124
125    private static final Method FLOAT_VALUE = Method
126            .getMethod("float floatValue()");
127
128    private static final Method LONG_VALUE = Method
129            .getMethod("long longValue()");
130
131    private static final Method DOUBLE_VALUE = Method
132            .getMethod("double doubleValue()");
133
134    /**
135     * Constant for the {@link #math math} method.
136     */
137    public static final int ADD = Opcodes.IADD;
138
139    /**
140     * Constant for the {@link #math math} method.
141     */
142    public static final int SUB = Opcodes.ISUB;
143
144    /**
145     * Constant for the {@link #math math} method.
146     */
147    public static final int MUL = Opcodes.IMUL;
148
149    /**
150     * Constant for the {@link #math math} method.
151     */
152    public static final int DIV = Opcodes.IDIV;
153
154    /**
155     * Constant for the {@link #math math} method.
156     */
157    public static final int REM = Opcodes.IREM;
158
159    /**
160     * Constant for the {@link #math math} method.
161     */
162    public static final int NEG = Opcodes.INEG;
163
164    /**
165     * Constant for the {@link #math math} method.
166     */
167    public static final int SHL = Opcodes.ISHL;
168
169    /**
170     * Constant for the {@link #math math} method.
171     */
172    public static final int SHR = Opcodes.ISHR;
173
174    /**
175     * Constant for the {@link #math math} method.
176     */
177    public static final int USHR = Opcodes.IUSHR;
178
179    /**
180     * Constant for the {@link #math math} method.
181     */
182    public static final int AND = Opcodes.IAND;
183
184    /**
185     * Constant for the {@link #math math} method.
186     */
187    public static final int OR = Opcodes.IOR;
188
189    /**
190     * Constant for the {@link #math math} method.
191     */
192    public static final int XOR = Opcodes.IXOR;
193
194    /**
195     * Constant for the {@link #ifCmp ifCmp} method.
196     */
197    public static final int EQ = Opcodes.IFEQ;
198
199    /**
200     * Constant for the {@link #ifCmp ifCmp} method.
201     */
202    public static final int NE = Opcodes.IFNE;
203
204    /**
205     * Constant for the {@link #ifCmp ifCmp} method.
206     */
207    public static final int LT = Opcodes.IFLT;
208
209    /**
210     * Constant for the {@link #ifCmp ifCmp} method.
211     */
212    public static final int GE = Opcodes.IFGE;
213
214    /**
215     * Constant for the {@link #ifCmp ifCmp} method.
216     */
217    public static final int GT = Opcodes.IFGT;
218
219    /**
220     * Constant for the {@link #ifCmp ifCmp} method.
221     */
222    public static final int LE = Opcodes.IFLE;
223
224    /**
225     * Access flags of the method visited by this adapter.
226     */
227    private final int access;
228
229    /**
230     * Return type of the method visited by this adapter.
231     */
232    private final Type returnType;
233
234    /**
235     * Argument types of the method visited by this adapter.
236     */
237    private final Type[] argumentTypes;
238
239    /**
240     * Types of the local variables of the method visited by this adapter.
241     */
242    private final List<Type> localTypes = new ArrayList<Type>();
243
244    /**
245     * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this
246     * constructor</i>. Instead, they must use the
247     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
248     * version.
249     * 
250     * @param mv
251     *            the method visitor to which this adapter delegates calls.
252     * @param access
253     *            the method's access flags (see {@link Opcodes}).
254     * @param name
255     *            the method's name.
256     * @param desc
257     *            the method's descriptor (see {@link Type Type}).
258     * @throws IllegalStateException
259     *             If a subclass calls this constructor.
260     */
261    public GeneratorAdapter(final MethodVisitor mv, final int access,
262                            final String name, final String desc) {
263        this(Opcodes.ASM6, mv, access, name, desc);
264        if (getClass() != GeneratorAdapter.class) {
265            throw new IllegalStateException();
266        }
267    }
268
269    /**
270     * Creates a new {@link GeneratorAdapter}.
271     * 
272     * @param api
273     *            the ASM API version implemented by this visitor. Must be one
274     *            of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
275     * @param mv
276     *            the method visitor to which this adapter delegates calls.
277     * @param access
278     *            the method's access flags (see {@link Opcodes}).
279     * @param name
280     *            the method's name.
281     * @param desc
282     *            the method's descriptor (see {@link Type Type}).
283     */
284    protected GeneratorAdapter(final int api, final MethodVisitor mv,
285            final int access, final String name, final String desc) {
286        super(api, access, desc, mv);
287        this.access = access;
288        this.returnType = Type.getReturnType(desc);
289        this.argumentTypes = Type.getArgumentTypes(desc);
290    }
291
292    /**
293     * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this
294     * constructor</i>. Instead, they must use the
295     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
296     * version.
297     * 
298     * @param access
299     *            access flags of the adapted method.
300     * @param method
301     *            the adapted method.
302     * @param mv
303     *            the method visitor to which this adapter delegates calls.
304     */
305    public GeneratorAdapter(final int access, final Method method,
306            final MethodVisitor mv) {
307        this(mv, access, null, method.getDescriptor());
308    }
309
310    /**
311     * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this
312     * constructor</i>. Instead, they must use the
313     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
314     * version.
315     * 
316     * @param access
317     *            access flags of the adapted method.
318     * @param method
319     *            the adapted method.
320     * @param signature
321     *            the signature of the adapted method (may be <tt>null</tt>).
322     * @param exceptions
323     *            the exceptions thrown by the adapted method (may be
324     *            <tt>null</tt>).
325     * @param cv
326     *            the class visitor to which this adapter delegates calls.
327     */
328    public GeneratorAdapter(final int access, final Method method,
329            final String signature, final Type[] exceptions,
330            final ClassVisitor cv) {
331        this(access, method, cv
332                .visitMethod(access, method.getName(), method.getDescriptor(),
333                        signature, getInternalNames(exceptions)));
334    }
335
336    /**
337     * Returns the internal names of the given types.
338     * 
339     * @param types
340     *            a set of types.
341     * @return the internal names of the given types.
342     */
343    private static String[] getInternalNames(final Type[] types) {
344        if (types == null) {
345            return null;
346        }
347        String[] names = new String[types.length];
348        for (int i = 0; i < names.length; ++i) {
349            names[i] = types[i].getInternalName();
350        }
351        return names;
352    }
353
354    // ------------------------------------------------------------------------
355    // Instructions to push constants on the stack
356    // ------------------------------------------------------------------------
357
358    /**
359     * Generates the instruction to push the given value on the stack.
360     * 
361     * @param value
362     *            the value to be pushed on the stack.
363     */
364    public void push(final boolean value) {
365        push(value ? 1 : 0);
366    }
367
368    /**
369     * Generates the instruction to push the given value on the stack.
370     * 
371     * @param value
372     *            the value to be pushed on the stack.
373     */
374    public void push(final int value) {
375        if (value >= -1 && value <= 5) {
376            mv.visitInsn(Opcodes.ICONST_0 + value);
377        } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
378            mv.visitIntInsn(Opcodes.BIPUSH, value);
379        } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
380            mv.visitIntInsn(Opcodes.SIPUSH, value);
381        } else {
382            mv.visitLdcInsn(value);
383        }
384    }
385
386    /**
387     * Generates the instruction to push the given value on the stack.
388     * 
389     * @param value
390     *            the value to be pushed on the stack.
391     */
392    public void push(final long value) {
393        if (value == 0L || value == 1L) {
394            mv.visitInsn(Opcodes.LCONST_0 + (int) value);
395        } else {
396            mv.visitLdcInsn(value);
397        }
398    }
399
400    /**
401     * Generates the instruction to push the given value on the stack.
402     * 
403     * @param value
404     *            the value to be pushed on the stack.
405     */
406    public void push(final float value) {
407        int bits = Float.floatToIntBits(value);
408        if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
409            mv.visitInsn(Opcodes.FCONST_0 + (int) value);
410        } else {
411            mv.visitLdcInsn(value);
412        }
413    }
414
415    /**
416     * Generates the instruction to push the given value on the stack.
417     * 
418     * @param value
419     *            the value to be pushed on the stack.
420     */
421    public void push(final double value) {
422        long bits = Double.doubleToLongBits(value);
423        if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
424            mv.visitInsn(Opcodes.DCONST_0 + (int) value);
425        } else {
426            mv.visitLdcInsn(value);
427        }
428    }
429
430    /**
431     * Generates the instruction to push the given value on the stack.
432     * 
433     * @param value
434     *            the value to be pushed on the stack. May be <tt>null</tt>.
435     */
436    public void push(final String value) {
437        if (value == null) {
438            mv.visitInsn(Opcodes.ACONST_NULL);
439        } else {
440            mv.visitLdcInsn(value);
441        }
442    }
443
444    /**
445     * Generates the instruction to push the given value on the stack.
446     * 
447     * @param value
448     *            the value to be pushed on the stack.
449     */
450    public void push(final Type value) {
451        if (value == null) {
452            mv.visitInsn(Opcodes.ACONST_NULL);
453        } else {
454            switch (value.getSort()) {
455            case Type.BOOLEAN:
456                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean",
457                        "TYPE", CLDESC);
458                break;
459            case Type.CHAR:
460                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character",
461                        "TYPE", CLDESC);
462                break;
463            case Type.BYTE:
464                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE",
465                        CLDESC);
466                break;
467            case Type.SHORT:
468                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE",
469                        CLDESC);
470                break;
471            case Type.INT:
472                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer",
473                        "TYPE", CLDESC);
474                break;
475            case Type.FLOAT:
476                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE",
477                        CLDESC);
478                break;
479            case Type.LONG:
480                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE",
481                        CLDESC);
482                break;
483            case Type.DOUBLE:
484                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double",
485                        "TYPE", CLDESC);
486                break;
487            default:
488                mv.visitLdcInsn(value);
489            }
490        }
491    }
492
493    /**
494     * Generates the instruction to push a handle on the stack.
495     * 
496     * @param handle
497     *            the handle to be pushed on the stack.
498     */
499    public void push(final Handle handle) {
500        mv.visitLdcInsn(handle);
501    }
502
503    // ------------------------------------------------------------------------
504    // Instructions to load and store method arguments
505    // ------------------------------------------------------------------------
506
507    /**
508     * Returns the index of the given method argument in the frame's local
509     * variables array.
510     * 
511     * @param arg
512     *            the index of a method argument.
513     * @return the index of the given method argument in the frame's local
514     *         variables array.
515     */
516    private int getArgIndex(final int arg) {
517        int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;
518        for (int i = 0; i < arg; i++) {
519            index += argumentTypes[i].getSize();
520        }
521        return index;
522    }
523
524    /**
525     * Generates the instruction to push a local variable on the stack.
526     * 
527     * @param type
528     *            the type of the local variable to be loaded.
529     * @param index
530     *            an index in the frame's local variables array.
531     */
532    private void loadInsn(final Type type, final int index) {
533        mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
534    }
535
536    /**
537     * Generates the instruction to store the top stack value in a local
538     * variable.
539     * 
540     * @param type
541     *            the type of the local variable to be stored.
542     * @param index
543     *            an index in the frame's local variables array.
544     */
545    private void storeInsn(final Type type, final int index) {
546        mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);
547    }
548
549    /**
550     * Generates the instruction to load 'this' on the stack.
551     */
552    public void loadThis() {
553        if ((access & Opcodes.ACC_STATIC) != 0) {
554            throw new IllegalStateException(
555                    "no 'this' pointer within static method");
556        }
557        mv.visitVarInsn(Opcodes.ALOAD, 0);
558    }
559
560    /**
561     * Generates the instruction to load the given method argument on the stack.
562     * 
563     * @param arg
564     *            the index of a method argument.
565     */
566    public void loadArg(final int arg) {
567        loadInsn(argumentTypes[arg], getArgIndex(arg));
568    }
569
570    /**
571     * Generates the instructions to load the given method arguments on the
572     * stack.
573     * 
574     * @param arg
575     *            the index of the first method argument to be loaded.
576     * @param count
577     *            the number of method arguments to be loaded.
578     */
579    public void loadArgs(final int arg, final int count) {
580        int index = getArgIndex(arg);
581        for (int i = 0; i < count; ++i) {
582            Type t = argumentTypes[arg + i];
583            loadInsn(t, index);
584            index += t.getSize();
585        }
586    }
587
588    /**
589     * Generates the instructions to load all the method arguments on the stack.
590     */
591    public void loadArgs() {
592        loadArgs(0, argumentTypes.length);
593    }
594
595    /**
596     * Generates the instructions to load all the method arguments on the stack,
597     * as a single object array.
598     */
599    public void loadArgArray() {
600        push(argumentTypes.length);
601        newArray(OBJECT_TYPE);
602        for (int i = 0; i < argumentTypes.length; i++) {
603            dup();
604            push(i);
605            loadArg(i);
606            box(argumentTypes[i]);
607            arrayStore(OBJECT_TYPE);
608        }
609    }
610
611    /**
612     * Generates the instruction to store the top stack value in the given
613     * method argument.
614     * 
615     * @param arg
616     *            the index of a method argument.
617     */
618    public void storeArg(final int arg) {
619        storeInsn(argumentTypes[arg], getArgIndex(arg));
620    }
621
622    // ------------------------------------------------------------------------
623    // Instructions to load and store local variables
624    // ------------------------------------------------------------------------
625
626    /**
627     * Returns the type of the given local variable.
628     * 
629     * @param local
630     *            a local variable identifier, as returned by
631     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
632     * @return the type of the given local variable.
633     */
634    public Type getLocalType(final int local) {
635        return localTypes.get(local - firstLocal);
636    }
637
638    @Override
639    protected void setLocalType(final int local, final Type type) {
640        int index = local - firstLocal;
641        while (localTypes.size() < index + 1) {
642            localTypes.add(null);
643        }
644        localTypes.set(index, type);
645    }
646
647    /**
648     * Generates the instruction to load the given local variable on the stack.
649     * 
650     * @param local
651     *            a local variable identifier, as returned by
652     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
653     */
654    public void loadLocal(final int local) {
655        loadInsn(getLocalType(local), local);
656    }
657
658    /**
659     * Generates the instruction to load the given local variable on the stack.
660     * 
661     * @param local
662     *            a local variable identifier, as returned by
663     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
664     * @param type
665     *            the type of this local variable.
666     */
667    public void loadLocal(final int local, final Type type) {
668        setLocalType(local, type);
669        loadInsn(type, local);
670    }
671
672    /**
673     * Generates the instruction to store the top stack value in the given local
674     * variable.
675     * 
676     * @param local
677     *            a local variable identifier, as returned by
678     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
679     */
680    public void storeLocal(final int local) {
681        storeInsn(getLocalType(local), local);
682    }
683
684    /**
685     * Generates the instruction to store the top stack value in the given local
686     * variable.
687     * 
688     * @param local
689     *            a local variable identifier, as returned by
690     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
691     * @param type
692     *            the type of this local variable.
693     */
694    public void storeLocal(final int local, final Type type) {
695        setLocalType(local, type);
696        storeInsn(type, local);
697    }
698
699    /**
700     * Generates the instruction to load an element from an array.
701     * 
702     * @param type
703     *            the type of the array element to be loaded.
704     */
705    public void arrayLoad(final Type type) {
706        mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
707    }
708
709    /**
710     * Generates the instruction to store an element in an array.
711     * 
712     * @param type
713     *            the type of the array element to be stored.
714     */
715    public void arrayStore(final Type type) {
716        mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
717    }
718
719    // ------------------------------------------------------------------------
720    // Instructions to manage the stack
721    // ------------------------------------------------------------------------
722
723    /**
724     * Generates a POP instruction.
725     */
726    public void pop() {
727        mv.visitInsn(Opcodes.POP);
728    }
729
730    /**
731     * Generates a POP2 instruction.
732     */
733    public void pop2() {
734        mv.visitInsn(Opcodes.POP2);
735    }
736
737    /**
738     * Generates a DUP instruction.
739     */
740    public void dup() {
741        mv.visitInsn(Opcodes.DUP);
742    }
743
744    /**
745     * Generates a DUP2 instruction.
746     */
747    public void dup2() {
748        mv.visitInsn(Opcodes.DUP2);
749    }
750
751    /**
752     * Generates a DUP_X1 instruction.
753     */
754    public void dupX1() {
755        mv.visitInsn(Opcodes.DUP_X1);
756    }
757
758    /**
759     * Generates a DUP_X2 instruction.
760     */
761    public void dupX2() {
762        mv.visitInsn(Opcodes.DUP_X2);
763    }
764
765    /**
766     * Generates a DUP2_X1 instruction.
767     */
768    public void dup2X1() {
769        mv.visitInsn(Opcodes.DUP2_X1);
770    }
771
772    /**
773     * Generates a DUP2_X2 instruction.
774     */
775    public void dup2X2() {
776        mv.visitInsn(Opcodes.DUP2_X2);
777    }
778
779    /**
780     * Generates a SWAP instruction.
781     */
782    public void swap() {
783        mv.visitInsn(Opcodes.SWAP);
784    }
785
786    /**
787     * Generates the instructions to swap the top two stack values.
788     * 
789     * @param prev
790     *            type of the top - 1 stack value.
791     * @param type
792     *            type of the top stack value.
793     */
794    public void swap(final Type prev, final Type type) {
795        if (type.getSize() == 1) {
796            if (prev.getSize() == 1) {
797                swap(); // same as dupX1(), pop();
798            } else {
799                dupX2();
800                pop();
801            }
802        } else {
803            if (prev.getSize() == 1) {
804                dup2X1();
805                pop2();
806            } else {
807                dup2X2();
808                pop2();
809            }
810        }
811    }
812
813    // ------------------------------------------------------------------------
814    // Instructions to do mathematical and logical operations
815    // ------------------------------------------------------------------------
816
817    /**
818     * Generates the instruction to do the specified mathematical or logical
819     * operation.
820     * 
821     * @param op
822     *            a mathematical or logical operation. Must be one of ADD, SUB,
823     *            MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
824     * @param type
825     *            the type of the operand(s) for this operation.
826     */
827    public void math(final int op, final Type type) {
828        mv.visitInsn(type.getOpcode(op));
829    }
830
831    /**
832     * Generates the instructions to compute the bitwise negation of the top
833     * stack value.
834     */
835    public void not() {
836        mv.visitInsn(Opcodes.ICONST_1);
837        mv.visitInsn(Opcodes.IXOR);
838    }
839
840    /**
841     * Generates the instruction to increment the given local variable.
842     * 
843     * @param local
844     *            the local variable to be incremented.
845     * @param amount
846     *            the amount by which the local variable must be incremented.
847     */
848    public void iinc(final int local, final int amount) {
849        mv.visitIincInsn(local, amount);
850    }
851
852    /**
853     * Generates the instructions to cast a numerical value from one type to
854     * another.
855     * 
856     * @param from
857     *            the type of the top stack value
858     * @param to
859     *            the type into which this value must be cast.
860     */
861    public void cast(final Type from, final Type to) {
862        if (from != to) {
863            if (from == Type.DOUBLE_TYPE) {
864                if (to == Type.FLOAT_TYPE) {
865                    mv.visitInsn(Opcodes.D2F);
866                } else if (to == Type.LONG_TYPE) {
867                    mv.visitInsn(Opcodes.D2L);
868                } else {
869                    mv.visitInsn(Opcodes.D2I);
870                    cast(Type.INT_TYPE, to);
871                }
872            } else if (from == Type.FLOAT_TYPE) {
873                if (to == Type.DOUBLE_TYPE) {
874                    mv.visitInsn(Opcodes.F2D);
875                } else if (to == Type.LONG_TYPE) {
876                    mv.visitInsn(Opcodes.F2L);
877                } else {
878                    mv.visitInsn(Opcodes.F2I);
879                    cast(Type.INT_TYPE, to);
880                }
881            } else if (from == Type.LONG_TYPE) {
882                if (to == Type.DOUBLE_TYPE) {
883                    mv.visitInsn(Opcodes.L2D);
884                } else if (to == Type.FLOAT_TYPE) {
885                    mv.visitInsn(Opcodes.L2F);
886                } else {
887                    mv.visitInsn(Opcodes.L2I);
888                    cast(Type.INT_TYPE, to);
889                }
890            } else {
891                if (to == Type.BYTE_TYPE) {
892                    mv.visitInsn(Opcodes.I2B);
893                } else if (to == Type.CHAR_TYPE) {
894                    mv.visitInsn(Opcodes.I2C);
895                } else if (to == Type.DOUBLE_TYPE) {
896                    mv.visitInsn(Opcodes.I2D);
897                } else if (to == Type.FLOAT_TYPE) {
898                    mv.visitInsn(Opcodes.I2F);
899                } else if (to == Type.LONG_TYPE) {
900                    mv.visitInsn(Opcodes.I2L);
901                } else if (to == Type.SHORT_TYPE) {
902                    mv.visitInsn(Opcodes.I2S);
903                }
904            }
905        }
906    }
907
908    // ------------------------------------------------------------------------
909    // Instructions to do boxing and unboxing operations
910    // ------------------------------------------------------------------------
911
912    private static Type getBoxedType(final Type type) {
913        switch (type.getSort()) {
914        case Type.BYTE:
915            return BYTE_TYPE;
916        case Type.BOOLEAN:
917            return BOOLEAN_TYPE;
918        case Type.SHORT:
919            return SHORT_TYPE;
920        case Type.CHAR:
921            return CHARACTER_TYPE;
922        case Type.INT:
923            return INTEGER_TYPE;
924        case Type.FLOAT:
925            return FLOAT_TYPE;
926        case Type.LONG:
927            return LONG_TYPE;
928        case Type.DOUBLE:
929            return DOUBLE_TYPE;
930        }
931        return type;
932    }
933
934    /**
935     * Generates the instructions to box the top stack value. This value is
936     * replaced by its boxed equivalent on top of the stack.
937     * 
938     * @param type
939     *            the type of the top stack value.
940     */
941    public void box(final Type type) {
942        if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
943            return;
944        }
945        if (type == Type.VOID_TYPE) {
946            push((String) null);
947        } else {
948            Type boxed = getBoxedType(type);
949            newInstance(boxed);
950            if (type.getSize() == 2) {
951                // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
952                dupX2();
953                dupX2();
954                pop();
955            } else {
956                // p -> po -> opo -> oop -> o
957                dupX1();
958                swap();
959            }
960            invokeConstructor(boxed, new Method("<init>", Type.VOID_TYPE,
961                    new Type[] { type }));
962        }
963    }
964
965    /**
966     * Generates the instructions to box the top stack value using Java 5's
967     * valueOf() method. This value is replaced by its boxed equivalent on top
968     * of the stack.
969     * 
970     * @param type
971     *            the type of the top stack value.
972     */
973    public void valueOf(final Type type) {
974        if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
975            return;
976        }
977        if (type == Type.VOID_TYPE) {
978            push((String) null);
979        } else {
980            Type boxed = getBoxedType(type);
981            invokeStatic(boxed, new Method("valueOf", boxed,
982                    new Type[] { type }));
983        }
984    }
985
986    /**
987     * Generates the instructions to unbox the top stack value. This value is
988     * replaced by its unboxed equivalent on top of the stack.
989     * 
990     * @param type
991     *            the type of the top stack value.
992     */
993    public void unbox(final Type type) {
994        Type t = NUMBER_TYPE;
995        Method sig = null;
996        switch (type.getSort()) {
997        case Type.VOID:
998            return;
999        case Type.CHAR:
1000            t = CHARACTER_TYPE;
1001            sig = CHAR_VALUE;
1002            break;
1003        case Type.BOOLEAN:
1004            t = BOOLEAN_TYPE;
1005            sig = BOOLEAN_VALUE;
1006            break;
1007        case Type.DOUBLE:
1008            sig = DOUBLE_VALUE;
1009            break;
1010        case Type.FLOAT:
1011            sig = FLOAT_VALUE;
1012            break;
1013        case Type.LONG:
1014            sig = LONG_VALUE;
1015            break;
1016        case Type.INT:
1017        case Type.SHORT:
1018        case Type.BYTE:
1019            sig = INT_VALUE;
1020        }
1021        if (sig == null) {
1022            checkCast(type);
1023        } else {
1024            checkCast(t);
1025            invokeVirtual(t, sig);
1026        }
1027    }
1028
1029    // ------------------------------------------------------------------------
1030    // Instructions to jump to other instructions
1031    // ------------------------------------------------------------------------
1032
1033    /**
1034     * Creates a new {@link Label}.
1035     * 
1036     * @return a new {@link Label}.
1037     */
1038    public Label newLabel() {
1039        return new Label();
1040    }
1041
1042    /**
1043     * Marks the current code position with the given label.
1044     * 
1045     * @param label
1046     *            a label.
1047     */
1048    public void mark(final Label label) {
1049        mv.visitLabel(label);
1050    }
1051
1052    /**
1053     * Marks the current code position with a new label.
1054     * 
1055     * @return the label that was created to mark the current code position.
1056     */
1057    public Label mark() {
1058        Label label = new Label();
1059        mv.visitLabel(label);
1060        return label;
1061    }
1062
1063    /**
1064     * Generates the instructions to jump to a label based on the comparison of
1065     * the top two stack values.
1066     * 
1067     * @param type
1068     *            the type of the top two stack values.
1069     * @param mode
1070     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
1071     *            LE.
1072     * @param label
1073     *            where to jump if the comparison result is <tt>true</tt>.
1074     */
1075    public void ifCmp(final Type type, final int mode, final Label label) {
1076        switch (type.getSort()) {
1077        case Type.LONG:
1078            mv.visitInsn(Opcodes.LCMP);
1079            break;
1080        case Type.DOUBLE:
1081            mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL
1082                    : Opcodes.DCMPG);
1083            break;
1084        case Type.FLOAT:
1085            mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL
1086                    : Opcodes.FCMPG);
1087            break;
1088        case Type.ARRAY:
1089        case Type.OBJECT:
1090            switch (mode) {
1091            case EQ:
1092                mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
1093                return;
1094            case NE:
1095                mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
1096                return;
1097            }
1098            throw new IllegalArgumentException("Bad comparison for type "
1099                    + type);
1100        default:
1101            int intOp = -1;
1102            switch (mode) {
1103            case EQ:
1104                intOp = Opcodes.IF_ICMPEQ;
1105                break;
1106            case NE:
1107                intOp = Opcodes.IF_ICMPNE;
1108                break;
1109            case GE:
1110                intOp = Opcodes.IF_ICMPGE;
1111                break;
1112            case LT:
1113                intOp = Opcodes.IF_ICMPLT;
1114                break;
1115            case LE:
1116                intOp = Opcodes.IF_ICMPLE;
1117                break;
1118            case GT:
1119                intOp = Opcodes.IF_ICMPGT;
1120                break;
1121            }
1122            mv.visitJumpInsn(intOp, label);
1123            return;
1124        }
1125        mv.visitJumpInsn(mode, label);
1126    }
1127
1128    /**
1129     * Generates the instructions to jump to a label based on the comparison of
1130     * the top two integer stack values.
1131     * 
1132     * @param mode
1133     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
1134     *            LE.
1135     * @param label
1136     *            where to jump if the comparison result is <tt>true</tt>.
1137     */
1138    public void ifICmp(final int mode, final Label label) {
1139        ifCmp(Type.INT_TYPE, mode, label);
1140    }
1141
1142    /**
1143     * Generates the instructions to jump to a label based on the comparison of
1144     * the top integer stack value with zero.
1145     * 
1146     * @param mode
1147     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
1148     *            LE.
1149     * @param label
1150     *            where to jump if the comparison result is <tt>true</tt>.
1151     */
1152    public void ifZCmp(final int mode, final Label label) {
1153        mv.visitJumpInsn(mode, label);
1154    }
1155
1156    /**
1157     * Generates the instruction to jump to the given label if the top stack
1158     * value is null.
1159     * 
1160     * @param label
1161     *            where to jump if the condition is <tt>true</tt>.
1162     */
1163    public void ifNull(final Label label) {
1164        mv.visitJumpInsn(Opcodes.IFNULL, label);
1165    }
1166
1167    /**
1168     * Generates the instruction to jump to the given label if the top stack
1169     * value is not null.
1170     * 
1171     * @param label
1172     *            where to jump if the condition is <tt>true</tt>.
1173     */
1174    public void ifNonNull(final Label label) {
1175        mv.visitJumpInsn(Opcodes.IFNONNULL, label);
1176    }
1177
1178    /**
1179     * Generates the instruction to jump to the given label.
1180     * 
1181     * @param label
1182     *            where to jump if the condition is <tt>true</tt>.
1183     */
1184    public void goTo(final Label label) {
1185        mv.visitJumpInsn(Opcodes.GOTO, label);
1186    }
1187
1188    /**
1189     * Generates a RET instruction.
1190     * 
1191     * @param local
1192     *            a local variable identifier, as returned by
1193     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
1194     */
1195    public void ret(final int local) {
1196        mv.visitVarInsn(Opcodes.RET, local);
1197    }
1198
1199    /**
1200     * Generates the instructions for a switch statement.
1201     * 
1202     * @param keys
1203     *            the switch case keys.
1204     * @param generator
1205     *            a generator to generate the code for the switch cases.
1206     */
1207    public void tableSwitch(final int[] keys,
1208            final TableSwitchGenerator generator) {
1209        float density;
1210        if (keys.length == 0) {
1211            density = 0;
1212        } else {
1213            density = (float) keys.length
1214                    / (keys[keys.length - 1] - keys[0] + 1);
1215        }
1216        tableSwitch(keys, generator, density >= 0.5f);
1217    }
1218
1219    /**
1220     * Generates the instructions for a switch statement.
1221     * 
1222     * @param keys
1223     *            the switch case keys.
1224     * @param generator
1225     *            a generator to generate the code for the switch cases.
1226     * @param useTable
1227     *            <tt>true</tt> to use a TABLESWITCH instruction, or
1228     *            <tt>false</tt> to use a LOOKUPSWITCH instruction.
1229     */
1230    public void tableSwitch(final int[] keys,
1231                            final TableSwitchGenerator generator, final boolean useTable) {
1232        for (int i = 1; i < keys.length; ++i) {
1233            if (keys[i] < keys[i - 1]) {
1234                throw new IllegalArgumentException(
1235                        "keys must be sorted ascending");
1236            }
1237        }
1238        Label def = newLabel();
1239        Label end = newLabel();
1240        if (keys.length > 0) {
1241            int len = keys.length;
1242            int min = keys[0];
1243            int max = keys[len - 1];
1244            int range = max - min + 1;
1245            if (useTable) {
1246                Label[] labels = new Label[range];
1247                Arrays.fill(labels, def);
1248                for (int i = 0; i < len; ++i) {
1249                    labels[keys[i] - min] = newLabel();
1250                }
1251                mv.visitTableSwitchInsn(min, max, def, labels);
1252                for (int i = 0; i < range; ++i) {
1253                    Label label = labels[i];
1254                    if (label != def) {
1255                        mark(label);
1256                        generator.generateCase(i + min, end);
1257                    }
1258                }
1259            } else {
1260                Label[] labels = new Label[len];
1261                for (int i = 0; i < len; ++i) {
1262                    labels[i] = newLabel();
1263                }
1264                mv.visitLookupSwitchInsn(def, keys, labels);
1265                for (int i = 0; i < len; ++i) {
1266                    mark(labels[i]);
1267                    generator.generateCase(keys[i], end);
1268                }
1269            }
1270        }
1271        mark(def);
1272        generator.generateDefault();
1273        mark(end);
1274    }
1275
1276    /**
1277     * Generates the instruction to return the top stack value to the caller.
1278     */
1279    public void returnValue() {
1280        mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
1281    }
1282
1283    // ------------------------------------------------------------------------
1284    // Instructions to load and store fields
1285    // ------------------------------------------------------------------------
1286
1287    /**
1288     * Generates a get field or set field instruction.
1289     * 
1290     * @param opcode
1291     *            the instruction's opcode.
1292     * @param ownerType
1293     *            the class in which the field is defined.
1294     * @param name
1295     *            the name of the field.
1296     * @param fieldType
1297     *            the type of the field.
1298     */
1299    private void fieldInsn(final int opcode, final Type ownerType,
1300            final String name, final Type fieldType) {
1301        mv.visitFieldInsn(opcode, ownerType.getInternalName(), name,
1302                fieldType.getDescriptor());
1303    }
1304
1305    /**
1306     * Generates the instruction to push the value of a static field on the
1307     * stack.
1308     * 
1309     * @param owner
1310     *            the class in which the field is defined.
1311     * @param name
1312     *            the name of the field.
1313     * @param type
1314     *            the type of the field.
1315     */
1316    public void getStatic(final Type owner, final String name, final Type type) {
1317        fieldInsn(Opcodes.GETSTATIC, owner, name, type);
1318    }
1319
1320    /**
1321     * Generates the instruction to store the top stack value in a static field.
1322     * 
1323     * @param owner
1324     *            the class in which the field is defined.
1325     * @param name
1326     *            the name of the field.
1327     * @param type
1328     *            the type of the field.
1329     */
1330    public void putStatic(final Type owner, final String name, final Type type) {
1331        fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
1332    }
1333
1334    /**
1335     * Generates the instruction to push the value of a non static field on the
1336     * stack.
1337     * 
1338     * @param owner
1339     *            the class in which the field is defined.
1340     * @param name
1341     *            the name of the field.
1342     * @param type
1343     *            the type of the field.
1344     */
1345    public void getField(final Type owner, final String name, final Type type) {
1346        fieldInsn(Opcodes.GETFIELD, owner, name, type);
1347    }
1348
1349    /**
1350     * Generates the instruction to store the top stack value in a non static
1351     * field.
1352     * 
1353     * @param owner
1354     *            the class in which the field is defined.
1355     * @param name
1356     *            the name of the field.
1357     * @param type
1358     *            the type of the field.
1359     */
1360    public void putField(final Type owner, final String name, final Type type) {
1361        fieldInsn(Opcodes.PUTFIELD, owner, name, type);
1362    }
1363
1364    // ------------------------------------------------------------------------
1365    // Instructions to invoke methods
1366    // ------------------------------------------------------------------------
1367
1368    /**
1369     * Generates an invoke method instruction.
1370     * 
1371     * @param opcode
1372     *            the instruction's opcode.
1373     * @param type
1374     *            the class in which the method is defined.
1375     * @param method
1376     *            the method to be invoked.
1377     */
1378    private void invokeInsn(final int opcode, final Type type,
1379                            final Method method, final boolean itf) {
1380        String owner = type.getSort() == Type.ARRAY ? type.getDescriptor()
1381                : type.getInternalName();
1382        mv.visitMethodInsn(opcode, owner, method.getName(),
1383                method.getDescriptor(), itf);
1384    }
1385
1386    /**
1387     * Generates the instruction to invoke a normal method.
1388     * 
1389     * @param owner
1390     *            the class in which the method is defined.
1391     * @param method
1392     *            the method to be invoked.
1393     */
1394    public void invokeVirtual(final Type owner, final Method method) {
1395        invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method, false);
1396    }
1397
1398    /**
1399     * Generates the instruction to invoke a constructor.
1400     * 
1401     * @param type
1402     *            the class in which the constructor is defined.
1403     * @param method
1404     *            the constructor to be invoked.
1405     */
1406    public void invokeConstructor(final Type type, final Method method) {
1407        invokeInsn(Opcodes.INVOKESPECIAL, type, method, false);
1408    }
1409
1410    /**
1411     * Generates the instruction to invoke a static method.
1412     * 
1413     * @param owner
1414     *            the class in which the method is defined.
1415     * @param method
1416     *            the method to be invoked.
1417     */
1418    public void invokeStatic(final Type owner, final Method method) {
1419        invokeInsn(Opcodes.INVOKESTATIC, owner, method, false);
1420    }
1421
1422    /**
1423     * Generates the instruction to invoke an interface method.
1424     * 
1425     * @param owner
1426     *            the class in which the method is defined.
1427     * @param method
1428     *            the method to be invoked.
1429     */
1430    public void invokeInterface(final Type owner, final Method method) {
1431        invokeInsn(Opcodes.INVOKEINTERFACE, owner, method, true);
1432    }
1433
1434    /**
1435     * Generates an invokedynamic instruction.
1436     * 
1437     * @param name
1438     *            the method's name.
1439     * @param desc
1440     *            the method's descriptor (see {@link Type Type}).
1441     * @param bsm
1442     *            the bootstrap method.
1443     * @param bsmArgs
1444     *            the bootstrap method constant arguments. Each argument must be
1445     *            an {@link Integer}, {@link Float}, {@link Long},
1446     *            {@link Double}, {@link String}, {@link Type} or {@link Handle}
1447     *            value. This method is allowed to modify the content of the
1448     *            array so a caller should expect that this array may change.
1449     */
1450    public void invokeDynamic(String name, String desc, Handle bsm,
1451            Object... bsmArgs) {
1452        mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
1453    }
1454
1455    // ------------------------------------------------------------------------
1456    // Instructions to create objects and arrays
1457    // ------------------------------------------------------------------------
1458
1459    /**
1460     * Generates a type dependent instruction.
1461     * 
1462     * @param opcode
1463     *            the instruction's opcode.
1464     * @param type
1465     *            the instruction's operand.
1466     */
1467    private void typeInsn(final int opcode, final Type type) {
1468        mv.visitTypeInsn(opcode, type.getInternalName());
1469    }
1470
1471    /**
1472     * Generates the instruction to create a new object.
1473     * 
1474     * @param type
1475     *            the class of the object to be created.
1476     */
1477    public void newInstance(final Type type) {
1478        typeInsn(Opcodes.NEW, type);
1479    }
1480
1481    /**
1482     * Generates the instruction to create a new array.
1483     * 
1484     * @param type
1485     *            the type of the array elements.
1486     */
1487    public void newArray(final Type type) {
1488        int typ;
1489        switch (type.getSort()) {
1490        case Type.BOOLEAN:
1491            typ = Opcodes.T_BOOLEAN;
1492            break;
1493        case Type.CHAR:
1494            typ = Opcodes.T_CHAR;
1495            break;
1496        case Type.BYTE:
1497            typ = Opcodes.T_BYTE;
1498            break;
1499        case Type.SHORT:
1500            typ = Opcodes.T_SHORT;
1501            break;
1502        case Type.INT:
1503            typ = Opcodes.T_INT;
1504            break;
1505        case Type.FLOAT:
1506            typ = Opcodes.T_FLOAT;
1507            break;
1508        case Type.LONG:
1509            typ = Opcodes.T_LONG;
1510            break;
1511        case Type.DOUBLE:
1512            typ = Opcodes.T_DOUBLE;
1513            break;
1514        default:
1515            typeInsn(Opcodes.ANEWARRAY, type);
1516            return;
1517        }
1518        mv.visitIntInsn(Opcodes.NEWARRAY, typ);
1519    }
1520
1521    // ------------------------------------------------------------------------
1522    // Miscelaneous instructions
1523    // ------------------------------------------------------------------------
1524
1525    /**
1526     * Generates the instruction to compute the length of an array.
1527     */
1528    public void arrayLength() {
1529        mv.visitInsn(Opcodes.ARRAYLENGTH);
1530    }
1531
1532    /**
1533     * Generates the instruction to throw an exception.
1534     */
1535    public void throwException() {
1536        mv.visitInsn(Opcodes.ATHROW);
1537    }
1538
1539    /**
1540     * Generates the instructions to create and throw an exception. The
1541     * exception class must have a constructor with a single String argument.
1542     * 
1543     * @param type
1544     *            the class of the exception to be thrown.
1545     * @param msg
1546     *            the detailed message of the exception.
1547     */
1548    public void throwException(final Type type, final String msg) {
1549        newInstance(type);
1550        dup();
1551        push(msg);
1552        invokeConstructor(type, Method.getMethod("void <init> (String)"));
1553        throwException();
1554    }
1555
1556    /**
1557     * Generates the instruction to check that the top stack value is of the
1558     * given type.
1559     * 
1560     * @param type
1561     *            a class or interface type.
1562     */
1563    public void checkCast(final Type type) {
1564        if (!type.equals(OBJECT_TYPE)) {
1565            typeInsn(Opcodes.CHECKCAST, type);
1566        }
1567    }
1568
1569    /**
1570     * Generates the instruction to test if the top stack value is of the given
1571     * type.
1572     * 
1573     * @param type
1574     *            a class or interface type.
1575     */
1576    public void instanceOf(final Type type) {
1577        typeInsn(Opcodes.INSTANCEOF, type);
1578    }
1579
1580    /**
1581     * Generates the instruction to get the monitor of the top stack value.
1582     */
1583    public void monitorEnter() {
1584        mv.visitInsn(Opcodes.MONITORENTER);
1585    }
1586
1587    /**
1588     * Generates the instruction to release the monitor of the top stack value.
1589     */
1590    public void monitorExit() {
1591        mv.visitInsn(Opcodes.MONITOREXIT);
1592    }
1593
1594    // ------------------------------------------------------------------------
1595    // Non instructions
1596    // ------------------------------------------------------------------------
1597
1598    /**
1599     * Marks the end of the visited method.
1600     */
1601    public void endMethod() {
1602        if ((access & Opcodes.ACC_ABSTRACT) == 0) {
1603            mv.visitMaxs(0, 0);
1604        }
1605        mv.visitEnd();
1606    }
1607
1608    /**
1609     * Marks the start of an exception handler.
1610     * 
1611     * @param start
1612     *            beginning of the exception handler's scope (inclusive).
1613     * @param end
1614     *            end of the exception handler's scope (exclusive).
1615     * @param exception
1616     *            internal name of the type of exceptions handled by the
1617     *            handler.
1618     */
1619    public void catchException(final Label start, final Label end,
1620                               final Type exception) {
1621        Label doCatch = new Label();
1622        if (exception == null) {
1623            mv.visitTryCatchBlock(start, end, doCatch, null);
1624        } else {
1625            mv.visitTryCatchBlock(start, end, doCatch,
1626                    exception.getInternalName());
1627        }
1628        mark(doCatch);
1629    }
1630}