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