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 java.util.ArrayList;
031import java.util.Arrays;
032import java.util.List;
033
034import io.ebean.enhance.asm.ClassVisitor;
035import io.ebean.enhance.asm.ConstantDynamic;
036import io.ebean.enhance.asm.Handle;
037import io.ebean.enhance.asm.Label;
038import io.ebean.enhance.asm.MethodVisitor;
039import io.ebean.enhance.asm.Opcodes;
040import io.ebean.enhance.asm.Type;
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(/* latest api = */ Opcodes.ASM9, 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 the {@code
215   *     ASM}<i>x</i> values in {@link Opcodes}.
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      InstructionAdapter.cast(mv, from, to);
763    }
764  }
765
766  // -----------------------------------------------------------------------------------------------
767  // Instructions to do boxing and unboxing operations
768  // -----------------------------------------------------------------------------------------------
769
770  private static Type getBoxedType(final Type type) {
771    switch (type.getSort()) {
772      case Type.BYTE:
773        return BYTE_TYPE;
774      case Type.BOOLEAN:
775        return BOOLEAN_TYPE;
776      case Type.SHORT:
777        return SHORT_TYPE;
778      case Type.CHAR:
779        return CHARACTER_TYPE;
780      case Type.INT:
781        return INTEGER_TYPE;
782      case Type.FLOAT:
783        return FLOAT_TYPE;
784      case Type.LONG:
785        return LONG_TYPE;
786      case Type.DOUBLE:
787        return DOUBLE_TYPE;
788      default:
789        return type;
790    }
791  }
792
793  /**
794   * Generates the instructions to box the top stack value. This value is replaced by its boxed
795   * equivalent on top of the stack.
796   *
797   * @param type the type of the top stack value.
798   */
799  public void box(final Type type) {
800    if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
801      return;
802    }
803    if (type == Type.VOID_TYPE) {
804      push((String) null);
805    } else {
806      Type boxedType = getBoxedType(type);
807      newInstance(boxedType);
808      if (type.getSize() == 2) {
809        // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
810        dupX2();
811        dupX2();
812        pop();
813      } else {
814        // p -> po -> opo -> oop -> o
815        dupX1();
816        swap();
817      }
818      invokeConstructor(boxedType, new Method("<init>", Type.VOID_TYPE, new Type[] {type}));
819    }
820  }
821
822  /**
823   * Generates the instructions to box the top stack value using Java 5's valueOf() method. This
824   * value is replaced by its boxed equivalent on top of the stack.
825   *
826   * @param type the type of the top stack value.
827   */
828  public void valueOf(final Type type) {
829    if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
830      return;
831    }
832    if (type == Type.VOID_TYPE) {
833      push((String) null);
834    } else {
835      Type boxedType = getBoxedType(type);
836      invokeStatic(boxedType, new Method("valueOf", boxedType, new Type[] {type}));
837    }
838  }
839
840  /**
841   * Generates the instructions to unbox the top stack value. This value is replaced by its unboxed
842   * equivalent on top of the stack.
843   *
844   * @param type the type of the top stack value.
845   */
846  public void unbox(final Type type) {
847    Type boxedType = NUMBER_TYPE;
848    Method unboxMethod;
849    switch (type.getSort()) {
850      case Type.VOID:
851        return;
852      case Type.CHAR:
853        boxedType = CHARACTER_TYPE;
854        unboxMethod = CHAR_VALUE;
855        break;
856      case Type.BOOLEAN:
857        boxedType = BOOLEAN_TYPE;
858        unboxMethod = BOOLEAN_VALUE;
859        break;
860      case Type.DOUBLE:
861        unboxMethod = DOUBLE_VALUE;
862        break;
863      case Type.FLOAT:
864        unboxMethod = FLOAT_VALUE;
865        break;
866      case Type.LONG:
867        unboxMethod = LONG_VALUE;
868        break;
869      case Type.INT:
870      case Type.SHORT:
871      case Type.BYTE:
872        unboxMethod = INT_VALUE;
873        break;
874      default:
875        unboxMethod = null;
876        break;
877    }
878    if (unboxMethod == null) {
879      checkCast(type);
880    } else {
881      checkCast(boxedType);
882      invokeVirtual(boxedType, unboxMethod);
883    }
884  }
885
886  // -----------------------------------------------------------------------------------------------
887  // Instructions to jump to other instructions
888  // -----------------------------------------------------------------------------------------------
889
890  /**
891   * Constructs a new {@link Label}.
892   *
893   * @return a new {@link Label}.
894   */
895  public Label newLabel() {
896    return new Label();
897  }
898
899  /**
900   * Marks the current code position with the given label.
901   *
902   * @param label a label.
903   */
904  public void mark(final Label label) {
905    mv.visitLabel(label);
906  }
907
908  /**
909   * Marks the current code position with a new label.
910   *
911   * @return the label that was created to mark the current code position.
912   */
913  public Label mark() {
914    Label label = new Label();
915    mv.visitLabel(label);
916    return label;
917  }
918
919  /**
920   * Generates the instructions to jump to a label based on the comparison of the top two stack
921   * values.
922   *
923   * @param type the type of the top two stack values.
924   * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE.
925   * @param label where to jump if the comparison result is {@literal true}.
926   */
927  public void ifCmp(final Type type, final int mode, final Label label) {
928    switch (type.getSort()) {
929      case Type.LONG:
930        mv.visitInsn(Opcodes.LCMP);
931        break;
932      case Type.DOUBLE:
933        mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL : Opcodes.DCMPG);
934        break;
935      case Type.FLOAT:
936        mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL : Opcodes.FCMPG);
937        break;
938      case Type.ARRAY:
939      case Type.OBJECT:
940        if (mode == EQ) {
941          mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
942          return;
943        } else if (mode == NE) {
944          mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
945          return;
946        } else {
947          throw new IllegalArgumentException("Bad comparison for type " + type);
948        }
949      default:
950        int intOp = -1;
951        switch (mode) {
952          case EQ:
953            intOp = Opcodes.IF_ICMPEQ;
954            break;
955          case NE:
956            intOp = Opcodes.IF_ICMPNE;
957            break;
958          case GE:
959            intOp = Opcodes.IF_ICMPGE;
960            break;
961          case LT:
962            intOp = Opcodes.IF_ICMPLT;
963            break;
964          case LE:
965            intOp = Opcodes.IF_ICMPLE;
966            break;
967          case GT:
968            intOp = Opcodes.IF_ICMPGT;
969            break;
970          default:
971            throw new IllegalArgumentException("Bad comparison mode " + mode);
972        }
973        mv.visitJumpInsn(intOp, label);
974        return;
975    }
976    mv.visitJumpInsn(mode, label);
977  }
978
979  /**
980   * Generates the instructions to jump to a label based on the comparison of the top two integer
981   * stack values.
982   *
983   * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE.
984   * @param label where to jump if the comparison result is {@literal true}.
985   */
986  public void ifICmp(final int mode, final Label label) {
987    ifCmp(Type.INT_TYPE, mode, label);
988  }
989
990  /**
991   * Generates the instructions to jump to a label based on the comparison of the top integer stack
992   * value with zero.
993   *
994   * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE.
995   * @param label where to jump if the comparison result is {@literal true}.
996   */
997  public void ifZCmp(final int mode, final Label label) {
998    mv.visitJumpInsn(mode, label);
999  }
1000
1001  /**
1002   * Generates the instruction to jump to the given label if the top stack value is null.
1003   *
1004   * @param label where to jump if the condition is {@literal true}.
1005   */
1006  public void ifNull(final Label label) {
1007    mv.visitJumpInsn(Opcodes.IFNULL, label);
1008  }
1009
1010  /**
1011   * Generates the instruction to jump to the given label if the top stack value is not null.
1012   *
1013   * @param label where to jump if the condition is {@literal true}.
1014   */
1015  public void ifNonNull(final Label label) {
1016    mv.visitJumpInsn(Opcodes.IFNONNULL, label);
1017  }
1018
1019  /**
1020   * Generates the instruction to jump to the given label.
1021   *
1022   * @param label where to jump if the condition is {@literal true}.
1023   */
1024  public void goTo(final Label label) {
1025    mv.visitJumpInsn(Opcodes.GOTO, label);
1026  }
1027
1028  /**
1029   * Generates a RET instruction.
1030   *
1031   * @param local a local variable identifier, as returned by {@link
1032   *     LocalVariablesSorter#newLocal(Type)}.
1033   */
1034  public void ret(final int local) {
1035    mv.visitVarInsn(Opcodes.RET, local);
1036  }
1037
1038  /**
1039   * Generates the instructions for a switch statement.
1040   *
1041   * @param keys the switch case keys.
1042   * @param generator a generator to generate the code for the switch cases.
1043   */
1044  public void tableSwitch(final int[] keys, final TableSwitchGenerator generator) {
1045    float density;
1046    if (keys.length == 0) {
1047      density = 0;
1048    } else {
1049      density = (float) keys.length / (keys[keys.length - 1] - keys[0] + 1);
1050    }
1051    tableSwitch(keys, generator, density >= 0.5f);
1052  }
1053
1054  /**
1055   * Generates the instructions for a switch statement.
1056   *
1057   * @param keys the switch case keys.
1058   * @param generator a generator to generate the code for the switch cases.
1059   * @param useTable {@literal true} to use a TABLESWITCH instruction, or {@literal false} to use a
1060   *     LOOKUPSWITCH instruction.
1061   */
1062  public void tableSwitch(
1063      final int[] keys, final TableSwitchGenerator generator, final boolean useTable) {
1064    for (int i = 1; i < keys.length; ++i) {
1065      if (keys[i] < keys[i - 1]) {
1066        throw new IllegalArgumentException("keys must be sorted in ascending order");
1067      }
1068    }
1069    Label defaultLabel = newLabel();
1070    Label endLabel = newLabel();
1071    if (keys.length > 0) {
1072      int numKeys = keys.length;
1073      if (useTable) {
1074        int min = keys[0];
1075        int max = keys[numKeys - 1];
1076        int range = max - min + 1;
1077        Label[] labels = new Label[range];
1078        Arrays.fill(labels, defaultLabel);
1079        for (int i = 0; i < numKeys; ++i) {
1080          labels[keys[i] - min] = newLabel();
1081        }
1082        mv.visitTableSwitchInsn(min, max, defaultLabel, labels);
1083        for (int i = 0; i < range; ++i) {
1084          Label label = labels[i];
1085          if (label != defaultLabel) {
1086            mark(label);
1087            generator.generateCase(i + min, endLabel);
1088          }
1089        }
1090      } else {
1091        Label[] labels = new Label[numKeys];
1092        for (int i = 0; i < numKeys; ++i) {
1093          labels[i] = newLabel();
1094        }
1095        mv.visitLookupSwitchInsn(defaultLabel, keys, labels);
1096        for (int i = 0; i < numKeys; ++i) {
1097          mark(labels[i]);
1098          generator.generateCase(keys[i], endLabel);
1099        }
1100      }
1101    }
1102    mark(defaultLabel);
1103    generator.generateDefault();
1104    mark(endLabel);
1105  }
1106
1107  /** Generates the instruction to return the top stack value to the caller. */
1108  public void returnValue() {
1109    mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
1110  }
1111
1112  // -----------------------------------------------------------------------------------------------
1113  // Instructions to load and store fields
1114  // -----------------------------------------------------------------------------------------------
1115
1116  /**
1117   * Generates a get field or set field instruction.
1118   *
1119   * @param opcode the instruction's opcode.
1120   * @param ownerType the class in which the field is defined.
1121   * @param name the name of the field.
1122   * @param fieldType the type of the field.
1123   */
1124  private void fieldInsn(
1125      final int opcode, final Type ownerType, final String name, final Type fieldType) {
1126    mv.visitFieldInsn(opcode, ownerType.getInternalName(), name, fieldType.getDescriptor());
1127  }
1128
1129  /**
1130   * Generates the instruction to push the value of a static field on the stack.
1131   *
1132   * @param owner the class in which the field is defined.
1133   * @param name the name of the field.
1134   * @param type the type of the field.
1135   */
1136  public void getStatic(final Type owner, final String name, final Type type) {
1137    fieldInsn(Opcodes.GETSTATIC, owner, name, type);
1138  }
1139
1140  /**
1141   * Generates the instruction to store the top stack value in a static field.
1142   *
1143   * @param owner the class in which the field is defined.
1144   * @param name the name of the field.
1145   * @param type the type of the field.
1146   */
1147  public void putStatic(final Type owner, final String name, final Type type) {
1148    fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
1149  }
1150
1151  /**
1152   * Generates the instruction to push the value of a non static field on the stack.
1153   *
1154   * @param owner the class in which the field is defined.
1155   * @param name the name of the field.
1156   * @param type the type of the field.
1157   */
1158  public void getField(final Type owner, final String name, final Type type) {
1159    fieldInsn(Opcodes.GETFIELD, owner, name, type);
1160  }
1161
1162  /**
1163   * Generates the instruction to store the top stack value in a non static field.
1164   *
1165   * @param owner the class in which the field is defined.
1166   * @param name the name of the field.
1167   * @param type the type of the field.
1168   */
1169  public void putField(final Type owner, final String name, final Type type) {
1170    fieldInsn(Opcodes.PUTFIELD, owner, name, type);
1171  }
1172
1173  // -----------------------------------------------------------------------------------------------
1174  // Instructions to invoke methods
1175  // -----------------------------------------------------------------------------------------------
1176
1177  /**
1178   * Generates an invoke method instruction.
1179   *
1180   * @param opcode the instruction's opcode.
1181   * @param type the class in which the method is defined.
1182   * @param method the method to be invoked.
1183   * @param isInterface whether the 'type' class is an interface or not.
1184   */
1185  private void invokeInsn(
1186      final int opcode, final Type type, final Method method, final boolean isInterface) {
1187    String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
1188    mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor(), isInterface);
1189  }
1190
1191  /**
1192   * Generates the instruction to invoke a normal method.
1193   *
1194   * @param owner the class in which the method is defined.
1195   * @param method the method to be invoked.
1196   */
1197  public void invokeVirtual(final Type owner, final Method method) {
1198    invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method, false);
1199  }
1200
1201  /**
1202   * Generates the instruction to invoke a constructor.
1203   *
1204   * @param type the class in which the constructor is defined.
1205   * @param method the constructor to be invoked.
1206   */
1207  public void invokeConstructor(final Type type, final Method method) {
1208    invokeInsn(Opcodes.INVOKESPECIAL, type, method, false);
1209  }
1210
1211  /**
1212   * Generates the instruction to invoke a static method.
1213   *
1214   * @param owner the class in which the method is defined.
1215   * @param method the method to be invoked.
1216   */
1217  public void invokeStatic(final Type owner, final Method method) {
1218    invokeInsn(Opcodes.INVOKESTATIC, owner, method, false);
1219  }
1220
1221  /**
1222   * Generates the instruction to invoke an interface method.
1223   *
1224   * @param owner the class in which the method is defined.
1225   * @param method the method to be invoked.
1226   */
1227  public void invokeInterface(final Type owner, final Method method) {
1228    invokeInsn(Opcodes.INVOKEINTERFACE, owner, method, true);
1229  }
1230
1231  /**
1232   * Generates an invokedynamic instruction.
1233   *
1234   * @param name the method's name.
1235   * @param descriptor the method's descriptor (see {@link Type}).
1236   * @param bootstrapMethodHandle the bootstrap method.
1237   * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be
1238   *     an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link
1239   *     Type} or {@link Handle} value. This method is allowed to modify the content of the array so
1240   *     a caller should expect that this array may change.
1241   */
1242  public void invokeDynamic(
1243      final String name,
1244      final String descriptor,
1245      final Handle bootstrapMethodHandle,
1246      final Object... bootstrapMethodArguments) {
1247    mv.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
1248  }
1249
1250  // -----------------------------------------------------------------------------------------------
1251  // Instructions to create objects and arrays
1252  // -----------------------------------------------------------------------------------------------
1253
1254  /**
1255   * Generates a type dependent instruction.
1256   *
1257   * @param opcode the instruction's opcode.
1258   * @param type the instruction's operand.
1259   */
1260  private void typeInsn(final int opcode, final Type type) {
1261    mv.visitTypeInsn(opcode, type.getInternalName());
1262  }
1263
1264  /**
1265   * Generates the instruction to create a new object.
1266   *
1267   * @param type the class of the object to be created.
1268   */
1269  public void newInstance(final Type type) {
1270    typeInsn(Opcodes.NEW, type);
1271  }
1272
1273  /**
1274   * Generates the instruction to create a new array.
1275   *
1276   * @param type the type of the array elements.
1277   */
1278  public void newArray(final Type type) {
1279    InstructionAdapter.newarray(mv, type);
1280  }
1281
1282  // -----------------------------------------------------------------------------------------------
1283  // Miscellaneous instructions
1284  // -----------------------------------------------------------------------------------------------
1285
1286  /** Generates the instruction to compute the length of an array. */
1287  public void arrayLength() {
1288    mv.visitInsn(Opcodes.ARRAYLENGTH);
1289  }
1290
1291  /** Generates the instruction to throw an exception. */
1292  public void throwException() {
1293    mv.visitInsn(Opcodes.ATHROW);
1294  }
1295
1296  /**
1297   * Generates the instructions to create and throw an exception. The exception class must have a
1298   * constructor with a single String argument.
1299   *
1300   * @param type the class of the exception to be thrown.
1301   * @param message the detailed message of the exception.
1302   */
1303  public void throwException(final Type type, final String message) {
1304    newInstance(type);
1305    dup();
1306    push(message);
1307    invokeConstructor(type, Method.getMethod("void <init> (String)"));
1308    throwException();
1309  }
1310
1311  /**
1312   * Generates the instruction to check that the top stack value is of the given type.
1313   *
1314   * @param type a class or interface type.
1315   */
1316  public void checkCast(final Type type) {
1317    if (!type.equals(OBJECT_TYPE)) {
1318      typeInsn(Opcodes.CHECKCAST, type);
1319    }
1320  }
1321
1322  /**
1323   * Generates the instruction to test if the top stack value is of the given type.
1324   *
1325   * @param type a class or interface type.
1326   */
1327  public void instanceOf(final Type type) {
1328    typeInsn(Opcodes.INSTANCEOF, type);
1329  }
1330
1331  /** Generates the instruction to get the monitor of the top stack value. */
1332  public void monitorEnter() {
1333    mv.visitInsn(Opcodes.MONITORENTER);
1334  }
1335
1336  /** Generates the instruction to release the monitor of the top stack value. */
1337  public void monitorExit() {
1338    mv.visitInsn(Opcodes.MONITOREXIT);
1339  }
1340
1341  // -----------------------------------------------------------------------------------------------
1342  // Non instructions
1343  // -----------------------------------------------------------------------------------------------
1344
1345  /** Marks the end of the visited method. */
1346  public void endMethod() {
1347    if ((access & Opcodes.ACC_ABSTRACT) == 0) {
1348      mv.visitMaxs(0, 0);
1349    }
1350    mv.visitEnd();
1351  }
1352
1353  /**
1354   * Marks the start of an exception handler.
1355   *
1356   * @param start beginning of the exception handler's scope (inclusive).
1357   * @param end end of the exception handler's scope (exclusive).
1358   * @param exception internal name of the type of exceptions handled by the handler.
1359   */
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}