001// ASM: a very small and fast Java bytecode manipulation framework
002// Copyright (c) 2000-2011 INRIA, France Telecom
003// All rights reserved.
004//
005// Redistribution and use in source and binary forms, with or without
006// modification, are permitted provided that the following conditions
007// are met:
008// 1. Redistributions of source code must retain the above copyright
009//    notice, this list of conditions and the following disclaimer.
010// 2. Redistributions in binary form must reproduce the above copyright
011//    notice, this list of conditions and the following disclaimer in the
012//    documentation and/or other materials provided with the distribution.
013// 3. Neither the name of the copyright holders nor the names of its
014//    contributors may be used to endorse or promote products derived from
015//    this software without specific prior written permission.
016//
017// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
021// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
022// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
023// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
025// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
026// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
027// THE POSSIBILITY OF SUCH DAMAGE.
028package io.ebean.enhance.asm.commons;
029
030import io.ebean.enhance.asm.ClassReader;
031import io.ebean.enhance.asm.ConstantDynamic;
032import io.ebean.enhance.asm.Handle;
033import io.ebean.enhance.asm.Label;
034import io.ebean.enhance.asm.MethodVisitor;
035import io.ebean.enhance.asm.Opcodes;
036import io.ebean.enhance.asm.Type;
037
038import java.util.ArrayList;
039import java.util.HashMap;
040import java.util.List;
041import java.util.Map;
042
043/**
044 * A {@link MethodVisitor} that keeps track of stack map frame changes between {@link
045 * #visitFrame(int, int, Object[], int, Object[])} calls. This adapter must be used with the {@link
046 * ClassReader#EXPAND_FRAMES} option. Each visit<i>X</i> instruction delegates to
047 * the next visitor in the chain, if any, and then simulates the effect of this instruction on the
048 * stack map frame, represented by {@link #locals} and {@link #stack}. The next visitor in the chain
049 * can get the state of the stack map frame <i>before</i> each instruction by reading the value of
050 * these fields in its visit<i>X</i> methods (this requires a reference to the AnalyzerAdapter that
051 * is before it in the chain). If this adapter is used with a class that does not contain stack map
052 * table attributes (i.e., pre Java 6 classes) then this adapter may not be able to compute the
053 * stack map frame for each instruction. In this case no exception is thrown but the {@link #locals}
054 * and {@link #stack} fields will be null for these instructions.
055 *
056 * @author Eric Bruneton
057 */
058public class AnalyzerAdapter extends MethodVisitor {
059
060  /**
061   * The local variable slots for the current execution frame. Primitive types are represented by
062   * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
063   * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or {@link Opcodes#UNINITIALIZED_THIS} (long and
064   * double are represented by two elements, the second one being TOP). Reference types are
065   * represented by String objects (representing internal names), and uninitialized types by Label
066   * objects (this label designates the NEW instruction that created this uninitialized value). This
067   * field is {@literal null} for unreachable instructions.
068   */
069  public List<Object> locals;
070
071  /**
072   * The operand stack slots for the current execution frame. Primitive types are represented by
073   * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
074   * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or {@link Opcodes#UNINITIALIZED_THIS} (long and
075   * double are represented by two elements, the second one being TOP). Reference types are
076   * represented by String objects (representing internal names), and uninitialized types by Label
077   * objects (this label designates the NEW instruction that created this uninitialized value). This
078   * field is {@literal null} for unreachable instructions.
079   */
080  public List<Object> stack;
081
082  /** The labels that designate the next instruction to be visited. May be {@literal null}. */
083  private List<Label> labels;
084
085  /**
086   * The uninitialized types in the current execution frame. This map associates internal names to
087   * Label objects. Each label designates a NEW instruction that created the currently uninitialized
088   * types, and the associated internal name represents the NEW operand, i.e. the final, initialized
089   * type value.
090   */
091  public Map<Object, Object> uninitializedTypes;
092
093  /** The maximum stack size of this method. */
094  private int maxStack;
095
096  /** The maximum number of local variables of this method. */
097  private int maxLocals;
098
099  /** The owner's class name. */
100  private String owner;
101
102  /**
103   * Constructs a new {@link AnalyzerAdapter}. <i>Subclasses must not use this constructor</i>.
104   * Instead, they must use the {@link #AnalyzerAdapter(int, String, int, String, String,
105   * MethodVisitor)} version.
106   *
107   * @param owner the owner's class name.
108   * @param access the method's access flags (see {@link Opcodes}).
109   * @param name the method's name.
110   * @param descriptor the method's descriptor (see {@link Type}).
111   * @param methodVisitor the method visitor to which this adapter delegates calls. May be {@literal
112   *     null}.
113   * @throws IllegalStateException If a subclass calls this constructor.
114   */
115  public AnalyzerAdapter(
116      final String owner,
117      final int access,
118      final String name,
119      final String descriptor,
120      final MethodVisitor methodVisitor) {
121    this(Opcodes.ASM7, owner, access, name, descriptor, methodVisitor);
122    if (getClass() != AnalyzerAdapter.class) {
123      throw new IllegalStateException();
124    }
125  }
126
127  /**
128   * Constructs a new {@link AnalyzerAdapter}.
129   *
130   * @param api the ASM API version implemented by this visitor. Must be one of {@link
131   *     Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
132   * @param owner the owner's class name.
133   * @param access the method's access flags (see {@link Opcodes}).
134   * @param name the method's name.
135   * @param descriptor the method's descriptor (see {@link Type}).
136   * @param methodVisitor the method visitor to which this adapter delegates calls. May be {@literal
137   *     null}.
138   */
139  protected AnalyzerAdapter(
140      final int api,
141      final String owner,
142      final int access,
143      final String name,
144      final String descriptor,
145      final MethodVisitor methodVisitor) {
146    super(api, methodVisitor);
147    this.owner = owner;
148    locals = new ArrayList<>();
149    stack = new ArrayList<>();
150    uninitializedTypes = new HashMap<>();
151
152    if ((access & Opcodes.ACC_STATIC) == 0) {
153      if ("<init>".equals(name)) {
154        locals.add(Opcodes.UNINITIALIZED_THIS);
155      } else {
156        locals.add(owner);
157      }
158    }
159    for (Type argumentType : Type.getArgumentTypes(descriptor)) {
160      switch (argumentType.getSort()) {
161        case Type.BOOLEAN:
162        case Type.CHAR:
163        case Type.BYTE:
164        case Type.SHORT:
165        case Type.INT:
166          locals.add(Opcodes.INTEGER);
167          break;
168        case Type.FLOAT:
169          locals.add(Opcodes.FLOAT);
170          break;
171        case Type.LONG:
172          locals.add(Opcodes.LONG);
173          locals.add(Opcodes.TOP);
174          break;
175        case Type.DOUBLE:
176          locals.add(Opcodes.DOUBLE);
177          locals.add(Opcodes.TOP);
178          break;
179        case Type.ARRAY:
180          locals.add(argumentType.getDescriptor());
181          break;
182        case Type.OBJECT:
183          locals.add(argumentType.getInternalName());
184          break;
185        default:
186          throw new AssertionError();
187      }
188    }
189    maxLocals = locals.size();
190  }
191
192  @Override
193  public void visitFrame(
194      final int type,
195      final int numLocal,
196      final Object[] local,
197      final int numStack,
198      final Object[] stack) {
199    if (type != Opcodes.F_NEW) { // Uncompressed frame.
200      throw new IllegalArgumentException(
201          "AnalyzerAdapter only accepts expanded frames (see ClassReader.EXPAND_FRAMES)");
202    }
203
204    super.visitFrame(type, numLocal, local, numStack, stack);
205
206    if (this.locals != null) {
207      this.locals.clear();
208      this.stack.clear();
209    } else {
210      this.locals = new ArrayList<>();
211      this.stack = new ArrayList<>();
212    }
213    visitFrameTypes(numLocal, local, this.locals);
214    visitFrameTypes(numStack, stack, this.stack);
215    maxLocals = Math.max(maxLocals, this.locals.size());
216    maxStack = Math.max(maxStack, this.stack.size());
217  }
218
219  private static void visitFrameTypes(
220      final int numTypes, final Object[] frameTypes, final List<Object> result) {
221    for (int i = 0; i < numTypes; ++i) {
222      Object frameType = frameTypes[i];
223      result.add(frameType);
224      if (frameType == Opcodes.LONG || frameType == Opcodes.DOUBLE) {
225        result.add(Opcodes.TOP);
226      }
227    }
228  }
229
230  @Override
231  public void visitInsn(final int opcode) {
232    super.visitInsn(opcode);
233    execute(opcode, 0, null);
234    if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) {
235      this.locals = null;
236      this.stack = null;
237    }
238  }
239
240  @Override
241  public void visitIntInsn(final int opcode, final int operand) {
242    super.visitIntInsn(opcode, operand);
243    execute(opcode, operand, null);
244  }
245
246  @Override
247  public void visitVarInsn(final int opcode, final int var) {
248    super.visitVarInsn(opcode, var);
249    boolean isLongOrDouble =
250        opcode == Opcodes.LLOAD
251            || opcode == Opcodes.DLOAD
252            || opcode == Opcodes.LSTORE
253            || opcode == Opcodes.DSTORE;
254    maxLocals = Math.max(maxLocals, var + (isLongOrDouble ? 2 : 1));
255    execute(opcode, var, null);
256  }
257
258  @Override
259  public void visitTypeInsn(final int opcode, final String type) {
260    if (opcode == Opcodes.NEW) {
261      if (labels == null) {
262        Label label = new Label();
263        labels = new ArrayList<>(3);
264        labels.add(label);
265        if (mv != null) {
266          mv.visitLabel(label);
267        }
268      }
269      for (Label label : labels) {
270        uninitializedTypes.put(label, type);
271      }
272    }
273    super.visitTypeInsn(opcode, type);
274    execute(opcode, 0, type);
275  }
276
277  @Override
278  public void visitFieldInsn(
279      final int opcode, final String owner, final String name, final String descriptor) {
280    super.visitFieldInsn(opcode, owner, name, descriptor);
281    execute(opcode, 0, descriptor);
282  }
283
284  @Override
285  public void visitMethodInsn(
286      final int opcodeAndSource,
287      final String owner,
288      final String name,
289      final String descriptor,
290      final boolean isInterface) {
291    if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) {
292      // Redirect the call to the deprecated version of this method.
293      super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
294      return;
295    }
296    super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
297    int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK;
298
299    if (this.locals == null) {
300      labels = null;
301      return;
302    }
303    pop(descriptor);
304    if (opcode != Opcodes.INVOKESTATIC) {
305      Object value = pop();
306      if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) {
307        Object initializedValue;
308        if (value == Opcodes.UNINITIALIZED_THIS) {
309          initializedValue = this.owner;
310        } else {
311          initializedValue = uninitializedTypes.get(value);
312        }
313        for (int i = 0; i < locals.size(); ++i) {
314          if (locals.get(i) == value) {
315            locals.set(i, initializedValue);
316          }
317        }
318        for (int i = 0; i < stack.size(); ++i) {
319          if (stack.get(i) == value) {
320            stack.set(i, initializedValue);
321          }
322        }
323      }
324    }
325    pushDescriptor(descriptor);
326    labels = null;
327  }
328
329  @Override
330  public void visitInvokeDynamicInsn(
331      final String name,
332      final String descriptor,
333      final Handle bootstrapMethodHandle,
334      final Object... bootstrapMethodArguments) {
335    super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
336    if (this.locals == null) {
337      labels = null;
338      return;
339    }
340    pop(descriptor);
341    pushDescriptor(descriptor);
342    labels = null;
343  }
344
345  @Override
346  public void visitJumpInsn(final int opcode, final Label label) {
347    super.visitJumpInsn(opcode, label);
348    execute(opcode, 0, null);
349    if (opcode == Opcodes.GOTO) {
350      this.locals = null;
351      this.stack = null;
352    }
353  }
354
355  @Override
356  public void visitLabel(final Label label) {
357    super.visitLabel(label);
358    if (labels == null) {
359      labels = new ArrayList<>(3);
360    }
361    labels.add(label);
362  }
363
364  @Override
365  public void visitLdcInsn(final Object value) {
366    super.visitLdcInsn(value);
367    if (this.locals == null) {
368      labels = null;
369      return;
370    }
371    if (value instanceof Integer) {
372      push(Opcodes.INTEGER);
373    } else if (value instanceof Long) {
374      push(Opcodes.LONG);
375      push(Opcodes.TOP);
376    } else if (value instanceof Float) {
377      push(Opcodes.FLOAT);
378    } else if (value instanceof Double) {
379      push(Opcodes.DOUBLE);
380      push(Opcodes.TOP);
381    } else if (value instanceof String) {
382      push("java/lang/String");
383    } else if (value instanceof Type) {
384      int sort = ((Type) value).getSort();
385      if (sort == Type.OBJECT || sort == Type.ARRAY) {
386        push("java/lang/Class");
387      } else if (sort == Type.METHOD) {
388        push("java/lang/invoke/MethodType");
389      } else {
390        throw new IllegalArgumentException();
391      }
392    } else if (value instanceof Handle) {
393      push("java/lang/invoke/MethodHandle");
394    } else if (value instanceof ConstantDynamic) {
395      pushDescriptor(((ConstantDynamic) value).getDescriptor());
396    } else {
397      throw new IllegalArgumentException();
398    }
399    labels = null;
400  }
401
402  @Override
403  public void visitIincInsn(final int var, final int increment) {
404    super.visitIincInsn(var, increment);
405    maxLocals = Math.max(maxLocals, var + 1);
406    execute(Opcodes.IINC, var, null);
407  }
408
409  @Override
410  public void visitTableSwitchInsn(
411    final int min, final int max, final Label dflt, final Label... labels) {
412    super.visitTableSwitchInsn(min, max, dflt, labels);
413    execute(Opcodes.TABLESWITCH, 0, null);
414    this.locals = null;
415    this.stack = null;
416  }
417
418  @Override
419  public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
420    super.visitLookupSwitchInsn(dflt, keys, labels);
421    execute(Opcodes.LOOKUPSWITCH, 0, null);
422    this.locals = null;
423    this.stack = null;
424  }
425
426  @Override
427  public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) {
428    super.visitMultiANewArrayInsn(descriptor, numDimensions);
429    execute(Opcodes.MULTIANEWARRAY, numDimensions, descriptor);
430  }
431
432  @Override
433  public void visitLocalVariable(
434      final String name,
435      final String descriptor,
436      final String signature,
437      final Label start,
438      final Label end,
439      final int index) {
440    char firstDescriptorChar = descriptor.charAt(0);
441    maxLocals =
442        Math.max(
443            maxLocals, index + (firstDescriptorChar == 'J' || firstDescriptorChar == 'D' ? 2 : 1));
444    super.visitLocalVariable(name, descriptor, signature, start, end, index);
445  }
446
447  @Override
448  public void visitMaxs(final int maxStack, final int maxLocals) {
449    if (mv != null) {
450      this.maxStack = Math.max(this.maxStack, maxStack);
451      this.maxLocals = Math.max(this.maxLocals, maxLocals);
452      mv.visitMaxs(this.maxStack, this.maxLocals);
453    }
454  }
455
456  // -----------------------------------------------------------------------------------------------
457
458  private Object get(final int local) {
459    maxLocals = Math.max(maxLocals, local + 1);
460    return local < locals.size() ? locals.get(local) : Opcodes.TOP;
461  }
462
463  private void set(final int local, final Object type) {
464    maxLocals = Math.max(maxLocals, local + 1);
465    while (local >= locals.size()) {
466      locals.add(Opcodes.TOP);
467    }
468    locals.set(local, type);
469  }
470
471  private void push(final Object type) {
472    stack.add(type);
473    maxStack = Math.max(maxStack, stack.size());
474  }
475
476  private void pushDescriptor(final String fieldOrMethodDescriptor) {
477    String descriptor =
478        fieldOrMethodDescriptor.charAt(0) == '('
479            ? Type.getReturnType(fieldOrMethodDescriptor).getDescriptor()
480            : fieldOrMethodDescriptor;
481    switch (descriptor.charAt(0)) {
482      case 'V':
483        return;
484      case 'Z':
485      case 'C':
486      case 'B':
487      case 'S':
488      case 'I':
489        push(Opcodes.INTEGER);
490        return;
491      case 'F':
492        push(Opcodes.FLOAT);
493        return;
494      case 'J':
495        push(Opcodes.LONG);
496        push(Opcodes.TOP);
497        return;
498      case 'D':
499        push(Opcodes.DOUBLE);
500        push(Opcodes.TOP);
501        return;
502      case '[':
503        push(descriptor);
504        break;
505      case 'L':
506        push(descriptor.substring(1, descriptor.length() - 1));
507        break;
508      default:
509        throw new AssertionError();
510    }
511  }
512
513  private Object pop() {
514    return stack.remove(stack.size() - 1);
515  }
516
517  private void pop(final int numSlots) {
518    int size = stack.size();
519    int end = size - numSlots;
520    for (int i = size - 1; i >= end; --i) {
521      stack.remove(i);
522    }
523  }
524
525  private void pop(final String descriptor) {
526    char firstDescriptorChar = descriptor.charAt(0);
527    if (firstDescriptorChar == '(') {
528      int numSlots = 0;
529      Type[] types = Type.getArgumentTypes(descriptor);
530      for (Type type : types) {
531        numSlots += type.getSize();
532      }
533      pop(numSlots);
534    } else if (firstDescriptorChar == 'J' || firstDescriptorChar == 'D') {
535      pop(2);
536    } else {
537      pop(1);
538    }
539  }
540
541  private void execute(final int opcode, final int intArg, final String stringArg) {
542    if (opcode == Opcodes.JSR || opcode == Opcodes.RET) {
543      throw new IllegalArgumentException("JSR/RET are not supported");
544    }
545    if (this.locals == null) {
546      labels = null;
547      return;
548    }
549    Object value1;
550    Object value2;
551    Object value3;
552    Object t4;
553    switch (opcode) {
554      case Opcodes.NOP:
555      case Opcodes.INEG:
556      case Opcodes.LNEG:
557      case Opcodes.FNEG:
558      case Opcodes.DNEG:
559      case Opcodes.I2B:
560      case Opcodes.I2C:
561      case Opcodes.I2S:
562      case Opcodes.GOTO:
563      case Opcodes.RETURN:
564        break;
565      case Opcodes.ACONST_NULL:
566        push(Opcodes.NULL);
567        break;
568      case Opcodes.ICONST_M1:
569      case Opcodes.ICONST_0:
570      case Opcodes.ICONST_1:
571      case Opcodes.ICONST_2:
572      case Opcodes.ICONST_3:
573      case Opcodes.ICONST_4:
574      case Opcodes.ICONST_5:
575      case Opcodes.BIPUSH:
576      case Opcodes.SIPUSH:
577        push(Opcodes.INTEGER);
578        break;
579      case Opcodes.LCONST_0:
580      case Opcodes.LCONST_1:
581        push(Opcodes.LONG);
582        push(Opcodes.TOP);
583        break;
584      case Opcodes.FCONST_0:
585      case Opcodes.FCONST_1:
586      case Opcodes.FCONST_2:
587        push(Opcodes.FLOAT);
588        break;
589      case Opcodes.DCONST_0:
590      case Opcodes.DCONST_1:
591        push(Opcodes.DOUBLE);
592        push(Opcodes.TOP);
593        break;
594      case Opcodes.ILOAD:
595      case Opcodes.FLOAD:
596      case Opcodes.ALOAD:
597        push(get(intArg));
598        break;
599      case Opcodes.LLOAD:
600      case Opcodes.DLOAD:
601        push(get(intArg));
602        push(Opcodes.TOP);
603        break;
604      case Opcodes.LALOAD:
605      case Opcodes.D2L:
606        pop(2);
607        push(Opcodes.LONG);
608        push(Opcodes.TOP);
609        break;
610      case Opcodes.DALOAD:
611      case Opcodes.L2D:
612        pop(2);
613        push(Opcodes.DOUBLE);
614        push(Opcodes.TOP);
615        break;
616      case Opcodes.AALOAD:
617        pop(1);
618        value1 = pop();
619        if (value1 instanceof String) {
620          pushDescriptor(((String) value1).substring(1));
621        } else if (value1 == Opcodes.NULL) {
622          push(value1);
623        } else {
624          push("java/lang/Object");
625        }
626        break;
627      case Opcodes.ISTORE:
628      case Opcodes.FSTORE:
629      case Opcodes.ASTORE:
630        value1 = pop();
631        set(intArg, value1);
632        if (intArg > 0) {
633          value2 = get(intArg - 1);
634          if (value2 == Opcodes.LONG || value2 == Opcodes.DOUBLE) {
635            set(intArg - 1, Opcodes.TOP);
636          }
637        }
638        break;
639      case Opcodes.LSTORE:
640      case Opcodes.DSTORE:
641        pop(1);
642        value1 = pop();
643        set(intArg, value1);
644        set(intArg + 1, Opcodes.TOP);
645        if (intArg > 0) {
646          value2 = get(intArg - 1);
647          if (value2 == Opcodes.LONG || value2 == Opcodes.DOUBLE) {
648            set(intArg - 1, Opcodes.TOP);
649          }
650        }
651        break;
652      case Opcodes.IASTORE:
653      case Opcodes.BASTORE:
654      case Opcodes.CASTORE:
655      case Opcodes.SASTORE:
656      case Opcodes.FASTORE:
657      case Opcodes.AASTORE:
658        pop(3);
659        break;
660      case Opcodes.LASTORE:
661      case Opcodes.DASTORE:
662        pop(4);
663        break;
664      case Opcodes.POP:
665      case Opcodes.IFEQ:
666      case Opcodes.IFNE:
667      case Opcodes.IFLT:
668      case Opcodes.IFGE:
669      case Opcodes.IFGT:
670      case Opcodes.IFLE:
671      case Opcodes.IRETURN:
672      case Opcodes.FRETURN:
673      case Opcodes.ARETURN:
674      case Opcodes.TABLESWITCH:
675      case Opcodes.LOOKUPSWITCH:
676      case Opcodes.ATHROW:
677      case Opcodes.MONITORENTER:
678      case Opcodes.MONITOREXIT:
679      case Opcodes.IFNULL:
680      case Opcodes.IFNONNULL:
681        pop(1);
682        break;
683      case Opcodes.POP2:
684      case Opcodes.IF_ICMPEQ:
685      case Opcodes.IF_ICMPNE:
686      case Opcodes.IF_ICMPLT:
687      case Opcodes.IF_ICMPGE:
688      case Opcodes.IF_ICMPGT:
689      case Opcodes.IF_ICMPLE:
690      case Opcodes.IF_ACMPEQ:
691      case Opcodes.IF_ACMPNE:
692      case Opcodes.LRETURN:
693      case Opcodes.DRETURN:
694        pop(2);
695        break;
696      case Opcodes.DUP:
697        value1 = pop();
698        push(value1);
699        push(value1);
700        break;
701      case Opcodes.DUP_X1:
702        value1 = pop();
703        value2 = pop();
704        push(value1);
705        push(value2);
706        push(value1);
707        break;
708      case Opcodes.DUP_X2:
709        value1 = pop();
710        value2 = pop();
711        value3 = pop();
712        push(value1);
713        push(value3);
714        push(value2);
715        push(value1);
716        break;
717      case Opcodes.DUP2:
718        value1 = pop();
719        value2 = pop();
720        push(value2);
721        push(value1);
722        push(value2);
723        push(value1);
724        break;
725      case Opcodes.DUP2_X1:
726        value1 = pop();
727        value2 = pop();
728        value3 = pop();
729        push(value2);
730        push(value1);
731        push(value3);
732        push(value2);
733        push(value1);
734        break;
735      case Opcodes.DUP2_X2:
736        value1 = pop();
737        value2 = pop();
738        value3 = pop();
739        t4 = pop();
740        push(value2);
741        push(value1);
742        push(t4);
743        push(value3);
744        push(value2);
745        push(value1);
746        break;
747      case Opcodes.SWAP:
748        value1 = pop();
749        value2 = pop();
750        push(value1);
751        push(value2);
752        break;
753      case Opcodes.IALOAD:
754      case Opcodes.BALOAD:
755      case Opcodes.CALOAD:
756      case Opcodes.SALOAD:
757      case Opcodes.IADD:
758      case Opcodes.ISUB:
759      case Opcodes.IMUL:
760      case Opcodes.IDIV:
761      case Opcodes.IREM:
762      case Opcodes.IAND:
763      case Opcodes.IOR:
764      case Opcodes.IXOR:
765      case Opcodes.ISHL:
766      case Opcodes.ISHR:
767      case Opcodes.IUSHR:
768      case Opcodes.L2I:
769      case Opcodes.D2I:
770      case Opcodes.FCMPL:
771      case Opcodes.FCMPG:
772        pop(2);
773        push(Opcodes.INTEGER);
774        break;
775      case Opcodes.LADD:
776      case Opcodes.LSUB:
777      case Opcodes.LMUL:
778      case Opcodes.LDIV:
779      case Opcodes.LREM:
780      case Opcodes.LAND:
781      case Opcodes.LOR:
782      case Opcodes.LXOR:
783        pop(4);
784        push(Opcodes.LONG);
785        push(Opcodes.TOP);
786        break;
787      case Opcodes.FALOAD:
788      case Opcodes.FADD:
789      case Opcodes.FSUB:
790      case Opcodes.FMUL:
791      case Opcodes.FDIV:
792      case Opcodes.FREM:
793      case Opcodes.L2F:
794      case Opcodes.D2F:
795        pop(2);
796        push(Opcodes.FLOAT);
797        break;
798      case Opcodes.DADD:
799      case Opcodes.DSUB:
800      case Opcodes.DMUL:
801      case Opcodes.DDIV:
802      case Opcodes.DREM:
803        pop(4);
804        push(Opcodes.DOUBLE);
805        push(Opcodes.TOP);
806        break;
807      case Opcodes.LSHL:
808      case Opcodes.LSHR:
809      case Opcodes.LUSHR:
810        pop(3);
811        push(Opcodes.LONG);
812        push(Opcodes.TOP);
813        break;
814      case Opcodes.IINC:
815        set(intArg, Opcodes.INTEGER);
816        break;
817      case Opcodes.I2L:
818      case Opcodes.F2L:
819        pop(1);
820        push(Opcodes.LONG);
821        push(Opcodes.TOP);
822        break;
823      case Opcodes.I2F:
824        pop(1);
825        push(Opcodes.FLOAT);
826        break;
827      case Opcodes.I2D:
828      case Opcodes.F2D:
829        pop(1);
830        push(Opcodes.DOUBLE);
831        push(Opcodes.TOP);
832        break;
833      case Opcodes.F2I:
834      case Opcodes.ARRAYLENGTH:
835      case Opcodes.INSTANCEOF:
836        pop(1);
837        push(Opcodes.INTEGER);
838        break;
839      case Opcodes.LCMP:
840      case Opcodes.DCMPL:
841      case Opcodes.DCMPG:
842        pop(4);
843        push(Opcodes.INTEGER);
844        break;
845      case Opcodes.GETSTATIC:
846        pushDescriptor(stringArg);
847        break;
848      case Opcodes.PUTSTATIC:
849        pop(stringArg);
850        break;
851      case Opcodes.GETFIELD:
852        pop(1);
853        pushDescriptor(stringArg);
854        break;
855      case Opcodes.PUTFIELD:
856        pop(stringArg);
857        pop();
858        break;
859      case Opcodes.NEW:
860        push(labels.get(0));
861        break;
862      case Opcodes.NEWARRAY:
863        pop();
864        switch (intArg) {
865          case Opcodes.T_BOOLEAN:
866            pushDescriptor("[Z");
867            break;
868          case Opcodes.T_CHAR:
869            pushDescriptor("[C");
870            break;
871          case Opcodes.T_BYTE:
872            pushDescriptor("[B");
873            break;
874          case Opcodes.T_SHORT:
875            pushDescriptor("[S");
876            break;
877          case Opcodes.T_INT:
878            pushDescriptor("[I");
879            break;
880          case Opcodes.T_FLOAT:
881            pushDescriptor("[F");
882            break;
883          case Opcodes.T_DOUBLE:
884            pushDescriptor("[D");
885            break;
886          case Opcodes.T_LONG:
887            pushDescriptor("[J");
888            break;
889          default:
890            throw new IllegalArgumentException("Invalid array type " + intArg);
891        }
892        break;
893      case Opcodes.ANEWARRAY:
894        pop();
895        pushDescriptor("[" + Type.getObjectType(stringArg));
896        break;
897      case Opcodes.CHECKCAST:
898        pop();
899        pushDescriptor(Type.getObjectType(stringArg).getDescriptor());
900        break;
901      case Opcodes.MULTIANEWARRAY:
902        pop(intArg);
903        pushDescriptor(stringArg);
904        break;
905      default:
906        throw new IllegalArgumentException("Invalid opcode " + opcode);
907    }
908    labels = null;
909  }
910}