Java tutorial
// ASM: a very small and fast Java bytecode manipulation framework // Copyright (c) 2000-2011 INRIA, France Telecom // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // 3. Neither the name of the copyright holders nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. package org.objectweb.asm.commons; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.objectweb.asm.ConstantDynamic; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** * A {@link MethodVisitor} that keeps track of stack map frame changes between {@link * #visitFrame(int, int, Object[], int, Object[])} calls. This adapter must be used with the {@link * org.objectweb.asm.ClassReader#EXPAND_FRAMES} option. Each visit<i>X</i> instruction delegates to * the next visitor in the chain, if any, and then simulates the effect of this instruction on the * stack map frame, represented by {@link #locals} and {@link #stack}. The next visitor in the chain * can get the state of the stack map frame <i>before</i> each instruction by reading the value of * these fields in its visit<i>X</i> methods (this requires a reference to the AnalyzerAdapter that * is before it in the chain). If this adapter is used with a class that does not contain stack map * table attributes (i.e., pre Java 6 classes) then this adapter may not be able to compute the * stack map frame for each instruction. In this case no exception is thrown but the {@link #locals} * and {@link #stack} fields will be null for these instructions. * * @author Eric Bruneton */ public class AnalyzerAdapter extends MethodVisitor { /** * The local variable slots for the current execution frame. Primitive types are represented by * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or {@link Opcodes#UNINITIALIZED_THIS} (long and * double are represented by two elements, the second one being TOP). Reference types are * represented by String objects (representing internal names), and uninitialized types by Label * objects (this label designates the NEW instruction that created this uninitialized value). This * field is {@literal null} for unreachable instructions. */ public List<Object> locals; /** * The operand stack slots for the current execution frame. Primitive types are represented by * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or {@link Opcodes#UNINITIALIZED_THIS} (long and * double are represented by two elements, the second one being TOP). Reference types are * represented by String objects (representing internal names), and uninitialized types by Label * objects (this label designates the NEW instruction that created this uninitialized value). This * field is {@literal null} for unreachable instructions. */ public List<Object> stack; /** The labels that designate the next instruction to be visited. May be {@literal null}. */ private List<Label> labels; /** * The uninitialized types in the current execution frame. This map associates internal names to * Label objects. Each label designates a NEW instruction that created the currently uninitialized * types, and the associated internal name represents the NEW operand, i.e. the final, initialized * type value. */ public Map<Object, Object> uninitializedTypes; /** The maximum stack size of this method. */ private int maxStack; /** The maximum number of local variables of this method. */ private int maxLocals; /** The owner's class name. */ private String owner; /** * Constructs a new {@link AnalyzerAdapter}. <i>Subclasses must not use this constructor</i>. * Instead, they must use the {@link #AnalyzerAdapter(int, String, int, String, String, * MethodVisitor)} version. * * @param owner the owner's class name. * @param access the method's access flags (see {@link Opcodes}). * @param name the method's name. * @param descriptor the method's descriptor (see {@link Type}). * @param methodVisitor the method visitor to which this adapter delegates calls. May be {@literal * null}. * @throws IllegalStateException If a subclass calls this constructor. */ public AnalyzerAdapter(final String owner, final int access, final String name, final String descriptor, final MethodVisitor methodVisitor) { this(/* latest api = */ Opcodes.ASM7, owner, access, name, descriptor, methodVisitor); if (getClass() != AnalyzerAdapter.class) { throw new IllegalStateException(); } } /** * Constructs a new {@link AnalyzerAdapter}. * * @param api the ASM API version implemented by this visitor. Must be one of {@link * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. * @param owner the owner's class name. * @param access the method's access flags (see {@link Opcodes}). * @param name the method's name. * @param descriptor the method's descriptor (see {@link Type}). * @param methodVisitor the method visitor to which this adapter delegates calls. May be {@literal * null}. */ protected AnalyzerAdapter(final int api, final String owner, final int access, final String name, final String descriptor, final MethodVisitor methodVisitor) { super(api, methodVisitor); this.owner = owner; locals = new ArrayList<>(); stack = new ArrayList<>(); uninitializedTypes = new HashMap<>(); if ((access & Opcodes.ACC_STATIC) == 0) { if ("<init>".equals(name)) { locals.add(Opcodes.UNINITIALIZED_THIS); } else { locals.add(owner); } } for (Type argumentType : Type.getArgumentTypes(descriptor)) { switch (argumentType.getSort()) { case Type.BOOLEAN: case Type.CHAR: case Type.BYTE: case Type.SHORT: case Type.INT: locals.add(Opcodes.INTEGER); break; case Type.FLOAT: locals.add(Opcodes.FLOAT); break; case Type.LONG: locals.add(Opcodes.LONG); locals.add(Opcodes.TOP); break; case Type.DOUBLE: locals.add(Opcodes.DOUBLE); locals.add(Opcodes.TOP); break; case Type.ARRAY: locals.add(argumentType.getDescriptor()); break; case Type.OBJECT: locals.add(argumentType.getInternalName()); break; default: throw new AssertionError(); } } maxLocals = locals.size(); } @Override public void visitFrame(final int type, final int numLocal, final Object[] local, final int numStack, final Object[] stack) { if (type != Opcodes.F_NEW) { // Uncompressed frame. throw new IllegalArgumentException( "AnalyzerAdapter only accepts expanded frames (see ClassReader.EXPAND_FRAMES)"); } super.visitFrame(type, numLocal, local, numStack, stack); if (this.locals != null) { this.locals.clear(); this.stack.clear(); } else { this.locals = new ArrayList<>(); this.stack = new ArrayList<>(); } visitFrameTypes(numLocal, local, this.locals); visitFrameTypes(numStack, stack, this.stack); maxLocals = Math.max(maxLocals, this.locals.size()); maxStack = Math.max(maxStack, this.stack.size()); } private static void visitFrameTypes(final int numTypes, final Object[] frameTypes, final List<Object> result) { for (int i = 0; i < numTypes; ++i) { Object frameType = frameTypes[i]; result.add(frameType); if (frameType == Opcodes.LONG || frameType == Opcodes.DOUBLE) { result.add(Opcodes.TOP); } } } @Override public void visitInsn(final int opcode) { super.visitInsn(opcode); execute(opcode, 0, null); if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) { this.locals = null; this.stack = null; } } @Override public void visitIntInsn(final int opcode, final int operand) { super.visitIntInsn(opcode, operand); execute(opcode, operand, null); } @Override public void visitVarInsn(final int opcode, final int var) { super.visitVarInsn(opcode, var); boolean isLongOrDouble = opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE; maxLocals = Math.max(maxLocals, var + (isLongOrDouble ? 2 : 1)); execute(opcode, var, null); } @Override public void visitTypeInsn(final int opcode, final String type) { if (opcode == Opcodes.NEW) { if (labels == null) { Label label = new Label(); labels = new ArrayList<>(3); labels.add(label); if (mv != null) { mv.visitLabel(label); } } for (Label label : labels) { uninitializedTypes.put(label, type); } } super.visitTypeInsn(opcode, type); execute(opcode, 0, type); } @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String descriptor) { super.visitFieldInsn(opcode, owner, name, descriptor); execute(opcode, 0, descriptor); } @Override public void visitMethodInsn(final int opcodeAndSource, final String owner, final String name, final String descriptor, final boolean isInterface) { if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) { // Redirect the call to the deprecated version of this method. super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); return; } super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK; if (this.locals == null) { labels = null; return; } pop(descriptor); if (opcode != Opcodes.INVOKESTATIC) { Object value = pop(); if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) { Object initializedValue; if (value == Opcodes.UNINITIALIZED_THIS) { initializedValue = this.owner; } else { initializedValue = uninitializedTypes.get(value); } for (int i = 0; i < locals.size(); ++i) { if (locals.get(i) == value) { locals.set(i, initializedValue); } } for (int i = 0; i < stack.size(); ++i) { if (stack.get(i) == value) { stack.set(i, initializedValue); } } } } pushDescriptor(descriptor); labels = null; } @Override public void visitInvokeDynamicInsn(final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); if (this.locals == null) { labels = null; return; } pop(descriptor); pushDescriptor(descriptor); labels = null; } @Override public void visitJumpInsn(final int opcode, final Label label) { super.visitJumpInsn(opcode, label); execute(opcode, 0, null); if (opcode == Opcodes.GOTO) { this.locals = null; this.stack = null; } } @Override public void visitLabel(final Label label) { super.visitLabel(label); if (labels == null) { labels = new ArrayList<>(3); } labels.add(label); } @Override public void visitLdcInsn(final Object value) { super.visitLdcInsn(value); if (this.locals == null) { labels = null; return; } if (value instanceof Integer) { push(Opcodes.INTEGER); } else if (value instanceof Long) { push(Opcodes.LONG); push(Opcodes.TOP); } else if (value instanceof Float) { push(Opcodes.FLOAT); } else if (value instanceof Double) { push(Opcodes.DOUBLE); push(Opcodes.TOP); } else if (value instanceof String) { push("java/lang/String"); } else if (value instanceof Type) { int sort = ((Type) value).getSort(); if (sort == Type.OBJECT || sort == Type.ARRAY) { push("java/lang/Class"); } else if (sort == Type.METHOD) { push("java/lang/invoke/MethodType"); } else { throw new IllegalArgumentException(); } } else if (value instanceof Handle) { push("java/lang/invoke/MethodHandle"); } else if (value instanceof ConstantDynamic) { pushDescriptor(((ConstantDynamic) value).getDescriptor()); } else { throw new IllegalArgumentException(); } labels = null; } @Override public void visitIincInsn(final int var, final int increment) { super.visitIincInsn(var, increment); maxLocals = Math.max(maxLocals, var + 1); execute(Opcodes.IINC, var, null); } @Override public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { super.visitTableSwitchInsn(min, max, dflt, labels); execute(Opcodes.TABLESWITCH, 0, null); this.locals = null; this.stack = null; } @Override public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { super.visitLookupSwitchInsn(dflt, keys, labels); execute(Opcodes.LOOKUPSWITCH, 0, null); this.locals = null; this.stack = null; } @Override public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { super.visitMultiANewArrayInsn(descriptor, numDimensions); execute(Opcodes.MULTIANEWARRAY, numDimensions, descriptor); } @Override public void visitLocalVariable(final String name, final String descriptor, final String signature, final Label start, final Label end, final int index) { char firstDescriptorChar = descriptor.charAt(0); maxLocals = Math.max(maxLocals, index + (firstDescriptorChar == 'J' || firstDescriptorChar == 'D' ? 2 : 1)); super.visitLocalVariable(name, descriptor, signature, start, end, index); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { if (mv != null) { this.maxStack = Math.max(this.maxStack, maxStack); this.maxLocals = Math.max(this.maxLocals, maxLocals); mv.visitMaxs(this.maxStack, this.maxLocals); } } // ----------------------------------------------------------------------------------------------- private Object get(final int local) { maxLocals = Math.max(maxLocals, local + 1); return local < locals.size() ? locals.get(local) : Opcodes.TOP; } private void set(final int local, final Object type) { maxLocals = Math.max(maxLocals, local + 1); while (local >= locals.size()) { locals.add(Opcodes.TOP); } locals.set(local, type); } private void push(final Object type) { stack.add(type); maxStack = Math.max(maxStack, stack.size()); } private void pushDescriptor(final String fieldOrMethodDescriptor) { String descriptor = fieldOrMethodDescriptor.charAt(0) == '(' ? Type.getReturnType(fieldOrMethodDescriptor).getDescriptor() : fieldOrMethodDescriptor; switch (descriptor.charAt(0)) { case 'V': return; case 'Z': case 'C': case 'B': case 'S': case 'I': push(Opcodes.INTEGER); return; case 'F': push(Opcodes.FLOAT); return; case 'J': push(Opcodes.LONG); push(Opcodes.TOP); return; case 'D': push(Opcodes.DOUBLE); push(Opcodes.TOP); return; case '[': push(descriptor); break; case 'L': push(descriptor.substring(1, descriptor.length() - 1)); break; default: throw new AssertionError(); } } private Object pop() { return stack.remove(stack.size() - 1); } private void pop(final int numSlots) { int size = stack.size(); int end = size - numSlots; for (int i = size - 1; i >= end; --i) { stack.remove(i); } } private void pop(final String descriptor) { char firstDescriptorChar = descriptor.charAt(0); if (firstDescriptorChar == '(') { int numSlots = 0; Type[] types = Type.getArgumentTypes(descriptor); for (Type type : types) { numSlots += type.getSize(); } pop(numSlots); } else if (firstDescriptorChar == 'J' || firstDescriptorChar == 'D') { pop(2); } else { pop(1); } } private void execute(final int opcode, final int intArg, final String stringArg) { if (opcode == Opcodes.JSR || opcode == Opcodes.RET) { throw new IllegalArgumentException("JSR/RET are not supported"); } if (this.locals == null) { labels = null; return; } Object value1; Object value2; Object value3; Object t4; switch (opcode) { case Opcodes.NOP: case Opcodes.INEG: case Opcodes.LNEG: case Opcodes.FNEG: case Opcodes.DNEG: case Opcodes.I2B: case Opcodes.I2C: case Opcodes.I2S: case Opcodes.GOTO: case Opcodes.RETURN: break; case Opcodes.ACONST_NULL: push(Opcodes.NULL); break; case Opcodes.ICONST_M1: case Opcodes.ICONST_0: case Opcodes.ICONST_1: case Opcodes.ICONST_2: case Opcodes.ICONST_3: case Opcodes.ICONST_4: case Opcodes.ICONST_5: case Opcodes.BIPUSH: case Opcodes.SIPUSH: push(Opcodes.INTEGER); break; case Opcodes.LCONST_0: case Opcodes.LCONST_1: push(Opcodes.LONG); push(Opcodes.TOP); break; case Opcodes.FCONST_0: case Opcodes.FCONST_1: case Opcodes.FCONST_2: push(Opcodes.FLOAT); break; case Opcodes.DCONST_0: case Opcodes.DCONST_1: push(Opcodes.DOUBLE); push(Opcodes.TOP); break; case Opcodes.ILOAD: case Opcodes.FLOAD: case Opcodes.ALOAD: push(get(intArg)); break; case Opcodes.LLOAD: case Opcodes.DLOAD: push(get(intArg)); push(Opcodes.TOP); break; case Opcodes.LALOAD: case Opcodes.D2L: pop(2); push(Opcodes.LONG); push(Opcodes.TOP); break; case Opcodes.DALOAD: case Opcodes.L2D: pop(2); push(Opcodes.DOUBLE); push(Opcodes.TOP); break; case Opcodes.AALOAD: pop(1); value1 = pop(); if (value1 instanceof String) { pushDescriptor(((String) value1).substring(1)); } else if (value1 == Opcodes.NULL) { push(value1); } else { push("java/lang/Object"); } break; case Opcodes.ISTORE: case Opcodes.FSTORE: case Opcodes.ASTORE: value1 = pop(); set(intArg, value1); if (intArg > 0) { value2 = get(intArg - 1); if (value2 == Opcodes.LONG || value2 == Opcodes.DOUBLE) { set(intArg - 1, Opcodes.TOP); } } break; case Opcodes.LSTORE: case Opcodes.DSTORE: pop(1); value1 = pop(); set(intArg, value1); set(intArg + 1, Opcodes.TOP); if (intArg > 0) { value2 = get(intArg - 1); if (value2 == Opcodes.LONG || value2 == Opcodes.DOUBLE) { set(intArg - 1, Opcodes.TOP); } } break; case Opcodes.IASTORE: case Opcodes.BASTORE: case Opcodes.CASTORE: case Opcodes.SASTORE: case Opcodes.FASTORE: case Opcodes.AASTORE: pop(3); break; case Opcodes.LASTORE: case Opcodes.DASTORE: pop(4); break; case Opcodes.POP: case Opcodes.IFEQ: case Opcodes.IFNE: case Opcodes.IFLT: case Opcodes.IFGE: case Opcodes.IFGT: case Opcodes.IFLE: case Opcodes.IRETURN: case Opcodes.FRETURN: case Opcodes.ARETURN: case Opcodes.TABLESWITCH: case Opcodes.LOOKUPSWITCH: case Opcodes.ATHROW: case Opcodes.MONITORENTER: case Opcodes.MONITOREXIT: case Opcodes.IFNULL: case Opcodes.IFNONNULL: pop(1); break; case Opcodes.POP2: case Opcodes.IF_ICMPEQ: case Opcodes.IF_ICMPNE: case Opcodes.IF_ICMPLT: case Opcodes.IF_ICMPGE: case Opcodes.IF_ICMPGT: case Opcodes.IF_ICMPLE: case Opcodes.IF_ACMPEQ: case Opcodes.IF_ACMPNE: case Opcodes.LRETURN: case Opcodes.DRETURN: pop(2); break; case Opcodes.DUP: value1 = pop(); push(value1); push(value1); break; case Opcodes.DUP_X1: value1 = pop(); value2 = pop(); push(value1); push(value2); push(value1); break; case Opcodes.DUP_X2: value1 = pop(); value2 = pop(); value3 = pop(); push(value1); push(value3); push(value2); push(value1); break; case Opcodes.DUP2: value1 = pop(); value2 = pop(); push(value2); push(value1); push(value2); push(value1); break; case Opcodes.DUP2_X1: value1 = pop(); value2 = pop(); value3 = pop(); push(value2); push(value1); push(value3); push(value2); push(value1); break; case Opcodes.DUP2_X2: value1 = pop(); value2 = pop(); value3 = pop(); t4 = pop(); push(value2); push(value1); push(t4); push(value3); push(value2); push(value1); break; case Opcodes.SWAP: value1 = pop(); value2 = pop(); push(value1); push(value2); break; case Opcodes.IALOAD: case Opcodes.BALOAD: case Opcodes.CALOAD: case Opcodes.SALOAD: case Opcodes.IADD: case Opcodes.ISUB: case Opcodes.IMUL: case Opcodes.IDIV: case Opcodes.IREM: case Opcodes.IAND: case Opcodes.IOR: case Opcodes.IXOR: case Opcodes.ISHL: case Opcodes.ISHR: case Opcodes.IUSHR: case Opcodes.L2I: case Opcodes.D2I: case Opcodes.FCMPL: case Opcodes.FCMPG: pop(2); push(Opcodes.INTEGER); break; case Opcodes.LADD: case Opcodes.LSUB: case Opcodes.LMUL: case Opcodes.LDIV: case Opcodes.LREM: case Opcodes.LAND: case Opcodes.LOR: case Opcodes.LXOR: pop(4); push(Opcodes.LONG); push(Opcodes.TOP); break; case Opcodes.FALOAD: case Opcodes.FADD: case Opcodes.FSUB: case Opcodes.FMUL: case Opcodes.FDIV: case Opcodes.FREM: case Opcodes.L2F: case Opcodes.D2F: pop(2); push(Opcodes.FLOAT); break; case Opcodes.DADD: case Opcodes.DSUB: case Opcodes.DMUL: case Opcodes.DDIV: case Opcodes.DREM: pop(4); push(Opcodes.DOUBLE); push(Opcodes.TOP); break; case Opcodes.LSHL: case Opcodes.LSHR: case Opcodes.LUSHR: pop(3); push(Opcodes.LONG); push(Opcodes.TOP); break; case Opcodes.IINC: set(intArg, Opcodes.INTEGER); break; case Opcodes.I2L: case Opcodes.F2L: pop(1); push(Opcodes.LONG); push(Opcodes.TOP); break; case Opcodes.I2F: pop(1); push(Opcodes.FLOAT); break; case Opcodes.I2D: case Opcodes.F2D: pop(1); push(Opcodes.DOUBLE); push(Opcodes.TOP); break; case Opcodes.F2I: case Opcodes.ARRAYLENGTH: case Opcodes.INSTANCEOF: pop(1); push(Opcodes.INTEGER); break; case Opcodes.LCMP: case Opcodes.DCMPL: case Opcodes.DCMPG: pop(4); push(Opcodes.INTEGER); break; case Opcodes.GETSTATIC: pushDescriptor(stringArg); break; case Opcodes.PUTSTATIC: pop(stringArg); break; case Opcodes.GETFIELD: pop(1); pushDescriptor(stringArg); break; case Opcodes.PUTFIELD: pop(stringArg); pop(); break; case Opcodes.NEW: push(labels.get(0)); break; case Opcodes.NEWARRAY: pop(); switch (intArg) { case Opcodes.T_BOOLEAN: pushDescriptor("[Z"); break; case Opcodes.T_CHAR: pushDescriptor("[C"); break; case Opcodes.T_BYTE: pushDescriptor("[B"); break; case Opcodes.T_SHORT: pushDescriptor("[S"); break; case Opcodes.T_INT: pushDescriptor("[I"); break; case Opcodes.T_FLOAT: pushDescriptor("[F"); break; case Opcodes.T_DOUBLE: pushDescriptor("[D"); break; case Opcodes.T_LONG: pushDescriptor("[J"); break; default: throw new IllegalArgumentException("Invalid array type " + intArg); } break; case Opcodes.ANEWARRAY: pop(); pushDescriptor("[" + Type.getObjectType(stringArg)); break; case Opcodes.CHECKCAST: pop(); pushDescriptor(Type.getObjectType(stringArg).getDescriptor()); break; case Opcodes.MULTIANEWARRAY: pop(intArg); pushDescriptor(stringArg); break; default: throw new IllegalArgumentException("Invalid opcode " + opcode); } labels = null; } }