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