/** * Creates a new local variable of the given type. * * @param type the type of the local variable to be created. * @return the identifier of the newly created local variable. */ public int newLocal(final Type type) { Object t; switch (type.getSort()) { case Type.BOOLEAN: case Type.CHAR: case Type.BYTE: case Type.SHORT: case Type.INT: t = Opcodes.INTEGER; break; case Type.FLOAT: t = Opcodes.FLOAT; break; case Type.LONG: t = Opcodes.LONG; break; case Type.DOUBLE: t = Opcodes.DOUBLE; break; case Type.ARRAY: t = type.getDescriptor(); break; // case Type.OBJECT: default: t = type.getInternalName(); break; } int local = nextLocal; setLocalType(local, type); setFrameLocal(local, t); nextLocal += type.getSize(); return local; }
/** * Creates a new {@link AnalyzerAdapter}. * * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM4} * or {@link Opcodes#ASM5}. * @param owner the owner's class name. * @param access the method's access flags (see {@link Opcodes}). * @param name the method's name. * @param desc the method's descriptor (see {@link Type Type}). * @param mv the method visitor to which this adapter delegates calls. May be <tt>null</tt>. */ protected AnalyzerAdapter( final int api, final String owner, final int access, final String name, final String desc, final MethodVisitor mv) { super(api, mv); this.owner = owner; locals = new ArrayList<Object>(); stack = new ArrayList<Object>(); uninitializedTypes = new HashMap<Object, Object>(); if ((access & Opcodes.ACC_STATIC) == 0) { if ("<init>".equals(name)) { locals.add(Opcodes.UNINITIALIZED_THIS); } else { locals.add(owner); } } Type[] types = Type.getArgumentTypes(desc); for (int i = 0; i < types.length; ++i) { Type type = types[i]; switch (type.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(types[i].getDescriptor()); break; // case Type.OBJECT: default: locals.add(types[i].getInternalName()); } } maxLocals = locals.size(); }
/** * Creates a new {@link LocalVariablesSorter}. * * @param access access flags of the adapted method. * @param desc the method's descriptor (see {@link Type Type}). * @param mv the method visitor to which this adapter delegates calls. */ public LocalVariablesSorter(final int access, final String desc, final MethodVisitor mv) { super(mv); Type[] args = Type.getArgumentTypes(desc); nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1; for (int i = 0; i < args.length; i++) { nextLocal += args[i].getSize(); } firstLocal = nextLocal; }
private int remap(final int var, final Type type) { if (var < firstLocal) { return var; } int key = 2 * var + type.getSize() - 1; int size = mapping.length; if (key >= size) { int[] newMapping = new int[Math.max(2 * size, key + 1)]; System.arraycopy(mapping, 0, newMapping, 0, size); mapping = newMapping; } int value = mapping[key]; if (value == 0) { value = nextLocal + 1; mapping[key] = value; setLocalType(nextLocal, type); nextLocal += type.getSize(); } if (value - 1 != var) { changed = true; } return value - 1; }
private void pop(final String desc) { char c = desc.charAt(0); if (c == '(') { int n = 0; Type[] types = Type.getArgumentTypes(desc); for (int i = 0; i < types.length; ++i) { n += types[i].getSize(); } pop(n); } else if (c == 'J' || c == 'D') { pop(2); } else { pop(1); } }
/** * A {@link MethodAdapter} that renumbers local variables in their order of appearance. This adapter * allows one to easily add new local variables to a method. It may be used by inheriting from this * class, but the preferred way of using it is via delegation: the next visitor in the chain can * indeed add new locals when needed by calling {@link #newLocal} on this adapter (this requires a * reference back to this {@link LocalVariablesSorter}). * * @author Chris Nokleberg * @author Eugene Kuleshov * @author Eric Bruneton */ public class LocalVariablesSorter extends MethodAdapter { private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); /** * Mapping from old to new local variable indexes. A local variable at index i of size 1 is * remapped to 'mapping[2*i]', while a local variable at index i of size 2 is remapped to * 'mapping[2*i+1]'. */ private int[] mapping = new int[40]; /** Array used to store stack map local variable types after remapping. */ private Object[] newLocals = new Object[20]; /** Index of the first local variable, after formal parameters. */ protected final int firstLocal; /** Index of the next local variable to be created by {@link #newLocal}. */ protected int nextLocal; /** Indicates if at least one local variable has moved due to remapping. */ private boolean changed; /** * Creates a new {@link LocalVariablesSorter}. * * @param access access flags of the adapted method. * @param desc the method's descriptor (see {@link Type Type}). * @param mv the method visitor to which this adapter delegates calls. */ public LocalVariablesSorter(final int access, final String desc, final MethodVisitor mv) { super(mv); Type[] args = Type.getArgumentTypes(desc); nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1; for (int i = 0; i < args.length; i++) { nextLocal += args[i].getSize(); } firstLocal = nextLocal; } public void visitVarInsn(final int opcode, final int var) { Type type; switch (opcode) { case Opcodes.LLOAD: case Opcodes.LSTORE: type = Type.LONG_TYPE; break; case Opcodes.DLOAD: case Opcodes.DSTORE: type = Type.DOUBLE_TYPE; break; case Opcodes.FLOAD: case Opcodes.FSTORE: type = Type.FLOAT_TYPE; break; case Opcodes.ILOAD: case Opcodes.ISTORE: type = Type.INT_TYPE; break; case Opcodes.ALOAD: case Opcodes.ASTORE: type = OBJECT_TYPE; break; // case RET: default: type = Type.VOID_TYPE; } mv.visitVarInsn(opcode, remap(var, type)); } public void visitIincInsn(final int var, final int increment) { mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); } public void visitMaxs(final int maxStack, final int maxLocals) { mv.visitMaxs(maxStack, nextLocal); } public void visitLocalVariable( final String name, final String desc, final String signature, final Label start, final Label end, final int index) { int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1; int newIndex = remap(index, size); mv.visitLocalVariable(name, desc, signature, start, end, newIndex); } public void visitFrame( final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) { if (type != Opcodes.F_NEW) { // uncompressed frame throw new IllegalStateException( "ClassReader.accept() should be called with EXPAND_FRAMES flag"); } if (!changed) { // optimization for the case where mapping = identity mv.visitFrame(type, nLocal, local, nStack, stack); return; } // creates a copy of newLocals Object[] oldLocals = new Object[newLocals.length]; System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); // copies types from 'local' to 'newLocals' // 'newLocals' already contains the variables added with 'newLocal' int index = 0; // old local variable index int number = 0; // old local variable number for (; number < nLocal; ++number) { Object t = local[number]; int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; if (t != Opcodes.TOP) { setFrameLocal(remap(index, size), t); } index += size; } // removes TOP after long and double types as well as trailing TOPs index = 0; number = 0; for (int i = 0; index < newLocals.length; ++i) { Object t = newLocals[index++]; if (t != null && t != Opcodes.TOP) { newLocals[i] = t; number = i + 1; if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { index += 1; } } else { newLocals[i] = Opcodes.TOP; } } // visits remapped frame mv.visitFrame(type, number, newLocals, nStack, stack); // restores original value of 'newLocals' newLocals = oldLocals; } // ------------- /** * Creates a new local variable of the given type. * * @param type the type of the local variable to be created. * @return the identifier of the newly created local variable. */ public int newLocal(final Type type) { Object t; switch (type.getSort()) { case Type.BOOLEAN: case Type.CHAR: case Type.BYTE: case Type.SHORT: case Type.INT: t = Opcodes.INTEGER; break; case Type.FLOAT: t = Opcodes.FLOAT; break; case Type.LONG: t = Opcodes.LONG; break; case Type.DOUBLE: t = Opcodes.DOUBLE; break; case Type.ARRAY: t = type.getDescriptor(); break; // case Type.OBJECT: default: t = type.getInternalName(); break; } int local = nextLocal; setLocalType(local, type); setFrameLocal(local, t); nextLocal += type.getSize(); return local; } /** * Sets the current type of the given local variable. The default implementation of this method * does nothing. * * @param local a local variable identifier, as returned by {@link #newLocal newLocal()}. * @param type the type of the value being stored in the local variable */ protected void setLocalType(final int local, final Type type) {} private void setFrameLocal(final int local, final Object type) { int l = newLocals.length; if (local >= l) { Object[] a = new Object[Math.max(2 * l, local + 1)]; System.arraycopy(newLocals, 0, a, 0, l); newLocals = a; } newLocals[local] = type; } private int remap(final int var, final Type type) { if (var < firstLocal) { return var; } int key = 2 * var + type.getSize() - 1; int size = mapping.length; if (key >= size) { int[] newMapping = new int[Math.max(2 * size, key + 1)]; System.arraycopy(mapping, 0, newMapping, 0, size); mapping = newMapping; } int value = mapping[key]; if (value == 0) { value = nextLocal + 1; mapping[key] = value; setLocalType(nextLocal, type); nextLocal += type.getSize(); } if (value - 1 != var) { changed = true; } return value - 1; } private int remap(final int var, final int size) { if (var < firstLocal || !changed) { return var; } int key = 2 * var + size - 1; int value = key < mapping.length ? mapping[key] : 0; if (value == 0) { throw new IllegalStateException("Unknown local variable " + var); } return value - 1; } }
private void execute(final int opcode, final int iarg, final String sarg) { if (this.locals == null) { labels = null; return; } Object t1, t2, t3, 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(iarg)); break; case Opcodes.LLOAD: case Opcodes.DLOAD: push(get(iarg)); push(Opcodes.TOP); break; case Opcodes.IALOAD: case Opcodes.BALOAD: case Opcodes.CALOAD: case Opcodes.SALOAD: pop(2); push(Opcodes.INTEGER); break; case Opcodes.LALOAD: case Opcodes.D2L: pop(2); push(Opcodes.LONG); push(Opcodes.TOP); break; case Opcodes.FALOAD: pop(2); push(Opcodes.FLOAT); break; case Opcodes.DALOAD: case Opcodes.L2D: pop(2); push(Opcodes.DOUBLE); push(Opcodes.TOP); break; case Opcodes.AALOAD: pop(1); t1 = pop(); if (t1 instanceof String) { pushDesc(((String) t1).substring(1)); } else { push("java/lang/Object"); } break; case Opcodes.ISTORE: case Opcodes.FSTORE: case Opcodes.ASTORE: t1 = pop(); set(iarg, t1); if (iarg > 0) { t2 = get(iarg - 1); if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) { set(iarg - 1, Opcodes.TOP); } } break; case Opcodes.LSTORE: case Opcodes.DSTORE: pop(1); t1 = pop(); set(iarg, t1); set(iarg + 1, Opcodes.TOP); if (iarg > 0) { t2 = get(iarg - 1); if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) { set(iarg - 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: t1 = pop(); push(t1); push(t1); break; case Opcodes.DUP_X1: t1 = pop(); t2 = pop(); push(t1); push(t2); push(t1); break; case Opcodes.DUP_X2: t1 = pop(); t2 = pop(); t3 = pop(); push(t1); push(t3); push(t2); push(t1); break; case Opcodes.DUP2: t1 = pop(); t2 = pop(); push(t2); push(t1); push(t2); push(t1); break; case Opcodes.DUP2_X1: t1 = pop(); t2 = pop(); t3 = pop(); push(t2); push(t1); push(t3); push(t2); push(t1); break; case Opcodes.DUP2_X2: t1 = pop(); t2 = pop(); t3 = pop(); t4 = pop(); push(t2); push(t1); push(t4); push(t3); push(t2); push(t1); break; case Opcodes.SWAP: t1 = pop(); t2 = pop(); push(t1); push(t2); break; 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.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(iarg, 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.JSR: case Opcodes.RET: throw new RuntimeException("JSR/RET are not supported"); case Opcodes.GETSTATIC: pushDesc(sarg); break; case Opcodes.PUTSTATIC: pop(sarg); break; case Opcodes.GETFIELD: pop(1); pushDesc(sarg); break; case Opcodes.PUTFIELD: pop(sarg); pop(); break; case Opcodes.NEW: push(labels.get(0)); break; case Opcodes.NEWARRAY: pop(); switch (iarg) { case Opcodes.T_BOOLEAN: pushDesc("[Z"); break; case Opcodes.T_CHAR: pushDesc("[C"); break; case Opcodes.T_BYTE: pushDesc("[B"); break; case Opcodes.T_SHORT: pushDesc("[S"); break; case Opcodes.T_INT: pushDesc("[I"); break; case Opcodes.T_FLOAT: pushDesc("[F"); break; case Opcodes.T_DOUBLE: pushDesc("[D"); break; // case Opcodes.T_LONG: default: pushDesc("[J"); break; } break; case Opcodes.ANEWARRAY: pop(); pushDesc("[" + Type.getObjectType(sarg)); break; case Opcodes.CHECKCAST: pop(); pushDesc(Type.getObjectType(sarg).getDescriptor()); break; // case Opcodes.MULTIANEWARRAY: default: pop(iarg); pushDesc(sarg); break; } labels = null; }