• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // ASM: a very small and fast Java bytecode manipulation framework
2 // Copyright (c) 2000-2011 INRIA, France Telecom
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the distribution.
13 // 3. Neither the name of the copyright holders nor the names of its
14 //    contributors may be used to endorse or promote products derived from
15 //    this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 // THE POSSIBILITY OF SUCH DAMAGE.
28 package org.objectweb.asm.commons;
29 
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import org.objectweb.asm.ConstantDynamic;
35 import org.objectweb.asm.Handle;
36 import org.objectweb.asm.Label;
37 import org.objectweb.asm.MethodVisitor;
38 import org.objectweb.asm.Opcodes;
39 import org.objectweb.asm.Type;
40 
41 /**
42  * A {@link MethodVisitor} that keeps track of stack map frame changes between {@link
43  * #visitFrame(int, int, Object[], int, Object[])} calls. This adapter must be used with the {@link
44  * org.objectweb.asm.ClassReader#EXPAND_FRAMES} option. Each visit<i>X</i> instruction delegates to
45  * the next visitor in the chain, if any, and then simulates the effect of this instruction on the
46  * stack map frame, represented by {@link #locals} and {@link #stack}. The next visitor in the chain
47  * can get the state of the stack map frame <i>before</i> each instruction by reading the value of
48  * these fields in its visit<i>X</i> methods (this requires a reference to the AnalyzerAdapter that
49  * is before it in the chain). If this adapter is used with a class that does not contain stack map
50  * table attributes (i.e., pre Java 6 classes) then this adapter may not be able to compute the
51  * stack map frame for each instruction. In this case no exception is thrown but the {@link #locals}
52  * and {@link #stack} fields will be null for these instructions.
53  *
54  * @author Eric Bruneton
55  */
56 public class AnalyzerAdapter extends MethodVisitor {
57 
58   /**
59    * The local variable slots for the current execution frame. Primitive types are represented by
60    * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
61    * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or {@link Opcodes#UNINITIALIZED_THIS} (long and
62    * double are represented by two elements, the second one being TOP). Reference types are
63    * represented by String objects (representing internal names, see {@link
64    * Type#getInternalName()}), and uninitialized types by Label objects (this label designates the
65    * NEW instruction that created this uninitialized value). This field is {@literal null} for
66    * unreachable instructions.
67    */
68   public List<Object> locals;
69 
70   /**
71    * The operand stack slots for the current execution frame. Primitive types are represented by
72    * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
73    * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or {@link Opcodes#UNINITIALIZED_THIS} (long and
74    * double are represented by two elements, the second one being TOP). Reference types are
75    * represented by String objects (representing internal names, see {@link
76    * Type#getInternalName()}), and uninitialized types by Label objects (this label designates the
77    * NEW instruction that created this uninitialized value). This field is {@literal null} for
78    * unreachable instructions.
79    */
80   public List<Object> stack;
81 
82   /** The labels that designate the next instruction to be visited. May be {@literal null}. */
83   private List<Label> labels;
84 
85   /**
86    * The uninitialized types in the current execution frame. This map associates internal names to
87    * Label objects (see {@link Type#getInternalName()}). Each label designates a NEW instruction
88    * that created the currently uninitialized types, and the associated internal name represents the
89    * NEW operand, i.e. the final, initialized type value.
90    */
91   public Map<Object, Object> uninitializedTypes;
92 
93   /** The maximum stack size of this method. */
94   private int maxStack;
95 
96   /** The maximum number of local variables of this method. */
97   private int maxLocals;
98 
99   /** 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    */
AnalyzerAdapter( final String owner, final int access, final String name, final String descriptor, final MethodVisitor methodVisitor)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(/* latest api = */ Opcodes.ASM9, 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 the {@code
131    *     ASM}<i>x</i> values in {@link Opcodes}.
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    */
AnalyzerAdapter( final int api, final String owner, final int access, final String name, final String descriptor, final MethodVisitor methodVisitor)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
visitFrame( final int type, final int numLocal, final Object[] local, final int numStack, final Object[] stack)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 
visitFrameTypes( final int numTypes, final Object[] frameTypes, final List<Object> result)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
visitInsn(final int opcode)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
visitIntInsn(final int opcode, final int operand)241   public void visitIntInsn(final int opcode, final int operand) {
242     super.visitIntInsn(opcode, operand);
243     execute(opcode, operand, null);
244   }
245 
246   @Override
visitVarInsn(final int opcode, final int varIndex)247   public void visitVarInsn(final int opcode, final int varIndex) {
248     super.visitVarInsn(opcode, varIndex);
249     boolean isLongOrDouble =
250         opcode == Opcodes.LLOAD
251             || opcode == Opcodes.DLOAD
252             || opcode == Opcodes.LSTORE
253             || opcode == Opcodes.DSTORE;
254     maxLocals = Math.max(maxLocals, varIndex + (isLongOrDouble ? 2 : 1));
255     execute(opcode, varIndex, null);
256   }
257 
258   @Override
visitTypeInsn(final int opcode, final String type)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
visitFieldInsn( final int opcode, final String owner, final String name, final String descriptor)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
visitMethodInsn( final int opcodeAndSource, final String owner, final String name, final String descriptor, final boolean isInterface)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
visitInvokeDynamicInsn( final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments)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
visitJumpInsn(final int opcode, final Label label)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
visitLabel(final Label label)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
visitLdcInsn(final Object value)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
visitIincInsn(final int varIndex, final int increment)403   public void visitIincInsn(final int varIndex, final int increment) {
404     super.visitIincInsn(varIndex, increment);
405     maxLocals = Math.max(maxLocals, varIndex + 1);
406     execute(Opcodes.IINC, varIndex, null);
407   }
408 
409   @Override
visitTableSwitchInsn( final int min, final int max, final Label dflt, final Label... labels)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
visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels)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
visitMultiANewArrayInsn(final String descriptor, final int numDimensions)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
visitLocalVariable( final String name, final String descriptor, final String signature, final Label start, final Label end, final int index)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
visitMaxs(final int maxStack, final int maxLocals)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 
get(final int local)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 
set(final int local, final Object type)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 
push(final Object type)471   private void push(final Object type) {
472     stack.add(type);
473     maxStack = Math.max(maxStack, stack.size());
474   }
475 
pushDescriptor(final String fieldOrMethodDescriptor)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 
pop()513   private Object pop() {
514     return stack.remove(stack.size() - 1);
515   }
516 
pop(final int numSlots)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 
pop(final String descriptor)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 
execute(final int opcode, final int intArg, final String stringArg)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 }
911