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