/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later, * or the Apache License Version 2.0. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package io.github.proxyhotswap.javassist.bytecode.analysis; import io.github.proxyhotswap.javassist.ClassPool; import io.github.proxyhotswap.javassist.CtClass; import io.github.proxyhotswap.javassist.NotFoundException; import io.github.proxyhotswap.javassist.bytecode.BadBytecode; import io.github.proxyhotswap.javassist.bytecode.CodeIterator; import io.github.proxyhotswap.javassist.bytecode.ConstPool; import io.github.proxyhotswap.javassist.bytecode.Descriptor; import io.github.proxyhotswap.javassist.bytecode.MethodInfo; import io.github.proxyhotswap.javassist.bytecode.Opcode; /** * Executor is responsible for modeling the effects of a JVM instruction on a frame. * * @author Jason T. Greene */ public class Executor implements Opcode { private final ConstPool constPool; private final ClassPool classPool; private final Type STRING_TYPE; private final Type CLASS_TYPE; private final Type THROWABLE_TYPE; private int lastPos; public Executor(ClassPool classPool, ConstPool constPool) { this.constPool = constPool; this.classPool = classPool; try { STRING_TYPE = getType("java.lang.String"); CLASS_TYPE = getType("java.lang.Class"); THROWABLE_TYPE = getType("java.lang.Throwable"); } catch (Exception e) { throw new RuntimeException(e); } } /** * Execute the instruction, modeling the effects on the specified frame and subroutine. * If a subroutine is passed, the access flags will be modified if this instruction accesses * the local variable table. * * @param method the method containing the instruction * @param pos the position of the instruction in the method * @param iter the code iterator used to find the instruction * @param frame the frame to modify to represent the result of the instruction * @param subroutine the optional subroutine this instruction belongs to. * @throws BadBytecode if the bytecode violates the jvm spec */ public void execute(MethodInfo method, int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode { this.lastPos = pos; int opcode = iter.byteAt(pos); // Declared opcode in order switch (opcode) { case NOP: break; case ACONST_NULL: frame.push(Type.UNINIT); break; case ICONST_M1: case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: frame.push(Type.INTEGER); break; case LCONST_0: case LCONST_1: frame.push(Type.LONG); frame.push(Type.TOP); break; case FCONST_0: case FCONST_1: case FCONST_2: frame.push(Type.FLOAT); break; case DCONST_0: case DCONST_1: frame.push(Type.DOUBLE); frame.push(Type.TOP); break; case BIPUSH: case SIPUSH: frame.push(Type.INTEGER); break; case LDC: evalLDC(iter.byteAt(pos + 1), frame); break; case LDC_W : case LDC2_W : evalLDC(iter.u16bitAt(pos + 1), frame); break; case ILOAD: evalLoad(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine); break; case LLOAD: evalLoad(Type.LONG, iter.byteAt(pos + 1), frame, subroutine); break; case FLOAD: evalLoad(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine); break; case DLOAD: evalLoad(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine); break; case ALOAD: evalLoad(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine); break; case ILOAD_0: case ILOAD_1: case ILOAD_2: case ILOAD_3: evalLoad(Type.INTEGER, opcode - ILOAD_0, frame, subroutine); break; case LLOAD_0: case LLOAD_1: case LLOAD_2: case LLOAD_3: evalLoad(Type.LONG, opcode - LLOAD_0, frame, subroutine); break; case FLOAD_0: case FLOAD_1: case FLOAD_2: case FLOAD_3: evalLoad(Type.FLOAT, opcode - FLOAD_0, frame, subroutine); break; case DLOAD_0: case DLOAD_1: case DLOAD_2: case DLOAD_3: evalLoad(Type.DOUBLE, opcode - DLOAD_0, frame, subroutine); break; case ALOAD_0: case ALOAD_1: case ALOAD_2: case ALOAD_3: evalLoad(Type.OBJECT, opcode - ALOAD_0, frame, subroutine); break; case IALOAD: evalArrayLoad(Type.INTEGER, frame); break; case LALOAD: evalArrayLoad(Type.LONG, frame); break; case FALOAD: evalArrayLoad(Type.FLOAT, frame); break; case DALOAD: evalArrayLoad(Type.DOUBLE, frame); break; case AALOAD: evalArrayLoad(Type.OBJECT, frame); break; case BALOAD: case CALOAD: case SALOAD: evalArrayLoad(Type.INTEGER, frame); break; case ISTORE: evalStore(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine); break; case LSTORE: evalStore(Type.LONG, iter.byteAt(pos + 1), frame, subroutine); break; case FSTORE: evalStore(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine); break; case DSTORE: evalStore(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine); break; case ASTORE: evalStore(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine); break; case ISTORE_0: case ISTORE_1: case ISTORE_2: case ISTORE_3: evalStore(Type.INTEGER, opcode - ISTORE_0, frame, subroutine); break; case LSTORE_0: case LSTORE_1: case LSTORE_2: case LSTORE_3: evalStore(Type.LONG, opcode - LSTORE_0, frame, subroutine); break; case FSTORE_0: case FSTORE_1: case FSTORE_2: case FSTORE_3: evalStore(Type.FLOAT, opcode - FSTORE_0, frame, subroutine); break; case DSTORE_0: case DSTORE_1: case DSTORE_2: case DSTORE_3: evalStore(Type.DOUBLE, opcode - DSTORE_0, frame, subroutine); break; case ASTORE_0: case ASTORE_1: case ASTORE_2: case ASTORE_3: evalStore(Type.OBJECT, opcode - ASTORE_0, frame, subroutine); break; case IASTORE: evalArrayStore(Type.INTEGER, frame); break; case LASTORE: evalArrayStore(Type.LONG, frame); break; case FASTORE: evalArrayStore(Type.FLOAT, frame); break; case DASTORE: evalArrayStore(Type.DOUBLE, frame); break; case AASTORE: evalArrayStore(Type.OBJECT, frame); break; case BASTORE: case CASTORE: case SASTORE: evalArrayStore(Type.INTEGER, frame); break; case POP: if (frame.pop() == Type.TOP) throw new BadBytecode("POP can not be used with a category 2 value, pos = " + pos); break; case POP2: frame.pop(); frame.pop(); break; case DUP: { Type type = frame.peek(); if (type == Type.TOP) throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos); frame.push(frame.peek()); break; } case DUP_X1: case DUP_X2: { Type type = frame.peek(); if (type == Type.TOP) throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos); int end = frame.getTopIndex(); int insert = end - (opcode - DUP_X1) - 1; frame.push(type); while (end > insert) { frame.setStack(end, frame.getStack(end - 1)); end--; } frame.setStack(insert, type); break; } case DUP2: frame.push(frame.getStack(frame.getTopIndex() - 1)); frame.push(frame.getStack(frame.getTopIndex() - 1)); break; case DUP2_X1: case DUP2_X2: { int end = frame.getTopIndex(); int insert = end - (opcode - DUP2_X1) - 1; Type type1 = frame.getStack(frame.getTopIndex() - 1); Type type2 = frame.peek(); frame.push(type1); frame.push(type2); while (end > insert) { frame.setStack(end, frame.getStack(end - 2)); end--; } frame.setStack(insert, type2); frame.setStack(insert - 1, type1); break; } case SWAP: { Type type1 = frame.pop(); Type type2 = frame.pop(); if (type1.getSize() == 2 || type2.getSize() == 2) throw new BadBytecode("Swap can not be used with category 2 values, pos = " + pos); frame.push(type1); frame.push(type2); break; } // Math case IADD: evalBinaryMath(Type.INTEGER, frame); break; case LADD: evalBinaryMath(Type.LONG, frame); break; case FADD: evalBinaryMath(Type.FLOAT, frame); break; case DADD: evalBinaryMath(Type.DOUBLE, frame); break; case ISUB: evalBinaryMath(Type.INTEGER, frame); break; case LSUB: evalBinaryMath(Type.LONG, frame); break; case FSUB: evalBinaryMath(Type.FLOAT, frame); break; case DSUB: evalBinaryMath(Type.DOUBLE, frame); break; case IMUL: evalBinaryMath(Type.INTEGER, frame); break; case LMUL: evalBinaryMath(Type.LONG, frame); break; case FMUL: evalBinaryMath(Type.FLOAT, frame); break; case DMUL: evalBinaryMath(Type.DOUBLE, frame); break; case IDIV: evalBinaryMath(Type.INTEGER, frame); break; case LDIV: evalBinaryMath(Type.LONG, frame); break; case FDIV: evalBinaryMath(Type.FLOAT, frame); break; case DDIV: evalBinaryMath(Type.DOUBLE, frame); break; case IREM: evalBinaryMath(Type.INTEGER, frame); break; case LREM: evalBinaryMath(Type.LONG, frame); break; case FREM: evalBinaryMath(Type.FLOAT, frame); break; case DREM: evalBinaryMath(Type.DOUBLE, frame); break; // Unary case INEG: verifyAssignable(Type.INTEGER, simplePeek(frame)); break; case LNEG: verifyAssignable(Type.LONG, simplePeek(frame)); break; case FNEG: verifyAssignable(Type.FLOAT, simplePeek(frame)); break; case DNEG: verifyAssignable(Type.DOUBLE, simplePeek(frame)); break; // Shifts case ISHL: evalShift(Type.INTEGER, frame); break; case LSHL: evalShift(Type.LONG, frame); break; case ISHR: evalShift(Type.INTEGER, frame); break; case LSHR: evalShift(Type.LONG, frame); break; case IUSHR: evalShift(Type.INTEGER,frame); break; case LUSHR: evalShift(Type.LONG, frame); break; // Bitwise Math case IAND: evalBinaryMath(Type.INTEGER, frame); break; case LAND: evalBinaryMath(Type.LONG, frame); break; case IOR: evalBinaryMath(Type.INTEGER, frame); break; case LOR: evalBinaryMath(Type.LONG, frame); break; case IXOR: evalBinaryMath(Type.INTEGER, frame); break; case LXOR: evalBinaryMath(Type.LONG, frame); break; case IINC: { int index = iter.byteAt(pos + 1); verifyAssignable(Type.INTEGER, frame.getLocal(index)); access(index, Type.INTEGER, subroutine); break; } // Conversion case I2L: verifyAssignable(Type.INTEGER, simplePop(frame)); simplePush(Type.LONG, frame); break; case I2F: verifyAssignable(Type.INTEGER, simplePop(frame)); simplePush(Type.FLOAT, frame); break; case I2D: verifyAssignable(Type.INTEGER, simplePop(frame)); simplePush(Type.DOUBLE, frame); break; case L2I: verifyAssignable(Type.LONG, simplePop(frame)); simplePush(Type.INTEGER, frame); break; case L2F: verifyAssignable(Type.LONG, simplePop(frame)); simplePush(Type.FLOAT, frame); break; case L2D: verifyAssignable(Type.LONG, simplePop(frame)); simplePush(Type.DOUBLE, frame); break; case F2I: verifyAssignable(Type.FLOAT, simplePop(frame)); simplePush(Type.INTEGER, frame); break; case F2L: verifyAssignable(Type.FLOAT, simplePop(frame)); simplePush(Type.LONG, frame); break; case F2D: verifyAssignable(Type.FLOAT, simplePop(frame)); simplePush(Type.DOUBLE, frame); break; case D2I: verifyAssignable(Type.DOUBLE, simplePop(frame)); simplePush(Type.INTEGER, frame); break; case D2L: verifyAssignable(Type.DOUBLE, simplePop(frame)); simplePush(Type.LONG, frame); break; case D2F: verifyAssignable(Type.DOUBLE, simplePop(frame)); simplePush(Type.FLOAT, frame); break; case I2B: case I2C: case I2S: verifyAssignable(Type.INTEGER, frame.peek()); break; case LCMP: verifyAssignable(Type.LONG, simplePop(frame)); verifyAssignable(Type.LONG, simplePop(frame)); frame.push(Type.INTEGER); break; case FCMPL: case FCMPG: verifyAssignable(Type.FLOAT, simplePop(frame)); verifyAssignable(Type.FLOAT, simplePop(frame)); frame.push(Type.INTEGER); break; case DCMPL: case DCMPG: verifyAssignable(Type.DOUBLE, simplePop(frame)); verifyAssignable(Type.DOUBLE, simplePop(frame)); frame.push(Type.INTEGER); break; // Control flow case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE: verifyAssignable(Type.INTEGER, simplePop(frame)); break; case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: verifyAssignable(Type.INTEGER, simplePop(frame)); verifyAssignable(Type.INTEGER, simplePop(frame)); break; case IF_ACMPEQ: case IF_ACMPNE: verifyAssignable(Type.OBJECT, simplePop(frame)); verifyAssignable(Type.OBJECT, simplePop(frame)); break; case GOTO: break; case JSR: frame.push(Type.RETURN_ADDRESS); break; case RET: verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(iter.byteAt(pos + 1))); break; case TABLESWITCH: case LOOKUPSWITCH: case IRETURN: verifyAssignable(Type.INTEGER, simplePop(frame)); break; case LRETURN: verifyAssignable(Type.LONG, simplePop(frame)); break; case FRETURN: verifyAssignable(Type.FLOAT, simplePop(frame)); break; case DRETURN: verifyAssignable(Type.DOUBLE, simplePop(frame)); break; case ARETURN: try { CtClass returnType = Descriptor.getReturnType(method.getDescriptor(), classPool); verifyAssignable(Type.get(returnType), simplePop(frame)); } catch (NotFoundException e) { throw new RuntimeException(e); } break; case RETURN: break; case GETSTATIC: evalGetField(opcode, iter.u16bitAt(pos + 1), frame); break; case PUTSTATIC: evalPutField(opcode, iter.u16bitAt(pos + 1), frame); break; case GETFIELD: evalGetField(opcode, iter.u16bitAt(pos + 1), frame); break; case PUTFIELD: evalPutField(opcode, iter.u16bitAt(pos + 1), frame); break; case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: evalInvokeMethod(opcode, iter.u16bitAt(pos + 1), frame); break; case INVOKEINTERFACE: evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame); break; case INVOKEDYNAMIC: evalInvokeDynamic(opcode, iter.u16bitAt(pos + 1), frame); break; case NEW: frame.push(resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1)))); break; case NEWARRAY: evalNewArray(pos, iter, frame); break; case ANEWARRAY: evalNewObjectArray(pos, iter, frame); break; case ARRAYLENGTH: { Type array = simplePop(frame); if (! array.isArray() && array != Type.UNINIT) throw new BadBytecode("Array length passed a non-array [pos = " + pos + "]: " + array); frame.push(Type.INTEGER); break; } case ATHROW: verifyAssignable(THROWABLE_TYPE, simplePop(frame)); break; case CHECKCAST: verifyAssignable(Type.OBJECT, simplePop(frame)); frame.push(typeFromDesc(constPool.getClassInfoByDescriptor(iter.u16bitAt(pos + 1)))); break; case INSTANCEOF: verifyAssignable(Type.OBJECT, simplePop(frame)); frame.push(Type.INTEGER); break; case MONITORENTER: case MONITOREXIT: verifyAssignable(Type.OBJECT, simplePop(frame)); break; case WIDE: evalWide(pos, iter, frame, subroutine); break; case MULTIANEWARRAY: evalNewObjectArray(pos, iter, frame); break; case IFNULL: case IFNONNULL: verifyAssignable(Type.OBJECT, simplePop(frame)); break; case GOTO_W: break; case JSR_W: frame.push(Type.RETURN_ADDRESS); break; } } private Type zeroExtend(Type type) { if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN) return Type.INTEGER; return type; } private void evalArrayLoad(Type expectedComponent, Frame frame) throws BadBytecode { Type index = frame.pop(); Type array = frame.pop(); // Special case, an array defined by aconst_null // TODO - we might need to be more inteligent about this if (array == Type.UNINIT) { verifyAssignable(Type.INTEGER, index); if (expectedComponent == Type.OBJECT) { simplePush(Type.UNINIT, frame); } else { simplePush(expectedComponent, frame); } return; } Type component = array.getComponent(); if (component == null) throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component); component = zeroExtend(component); verifyAssignable(expectedComponent, component); verifyAssignable(Type.INTEGER, index); simplePush(component, frame); } private void evalArrayStore(Type expectedComponent, Frame frame) throws BadBytecode { Type value = simplePop(frame); Type index = frame.pop(); Type array = frame.pop(); if (array == Type.UNINIT) { verifyAssignable(Type.INTEGER, index); return; } Type component = array.getComponent(); if (component == null) throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component); component = zeroExtend(component); verifyAssignable(expectedComponent, component); verifyAssignable(Type.INTEGER, index); // This intentionally only checks for Object on aastore // downconverting of an array (no casts) // e.g. Object[] blah = new String[]; // blah[2] = (Object) "test"; // blah[3] = new Integer(); // compiler doesnt catch it (has legal bytecode), // // but will throw arraystoreexception if (expectedComponent == Type.OBJECT) { verifyAssignable(expectedComponent, value); } else { verifyAssignable(component, value); } } private void evalBinaryMath(Type expected, Frame frame) throws BadBytecode { Type value2 = simplePop(frame); Type value1 = simplePop(frame); verifyAssignable(expected, value2); verifyAssignable(expected, value1); simplePush(value1, frame); } private void evalGetField(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getFieldrefType(index); Type type = zeroExtend(typeFromDesc(desc)); if (opcode == GETFIELD) { Type objectType = resolveClassInfo(constPool.getFieldrefClassName(index)); verifyAssignable(objectType, simplePop(frame)); } simplePush(type, frame); } private void evalInvokeIntfMethod(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getInterfaceMethodrefType(index); Type[] types = paramTypesFromDesc(desc); int i = types.length; while (i > 0) verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); String classInfo = constPool.getInterfaceMethodrefClassName(index); Type objectType = resolveClassInfo(classInfo); verifyAssignable(objectType, simplePop(frame)); Type returnType = returnTypeFromDesc(desc); if (returnType != Type.VOID) simplePush(zeroExtend(returnType), frame); } private void evalInvokeMethod(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getMethodrefType(index); Type[] types = paramTypesFromDesc(desc); int i = types.length; while (i > 0) verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); if (opcode != INVOKESTATIC) { Type objectType = resolveClassInfo(constPool.getMethodrefClassName(index)); verifyAssignable(objectType, simplePop(frame)); } Type returnType = returnTypeFromDesc(desc); if (returnType != Type.VOID) simplePush(zeroExtend(returnType), frame); } private void evalInvokeDynamic(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getInvokeDynamicType(index); Type[] types = paramTypesFromDesc(desc); int i = types.length; while (i > 0) verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); // simplePop(frame); // assume CosntPool#REF_invokeStatic Type returnType = returnTypeFromDesc(desc); if (returnType != Type.VOID) simplePush(zeroExtend(returnType), frame); } private void evalLDC(int index, Frame frame) throws BadBytecode { int tag = constPool.getTag(index); Type type; switch (tag) { case ConstPool.CONST_String: type = STRING_TYPE; break; case ConstPool.CONST_Integer: type = Type.INTEGER; break; case ConstPool.CONST_Float: type = Type.FLOAT; break; case ConstPool.CONST_Long: type = Type.LONG; break; case ConstPool.CONST_Double: type = Type.DOUBLE; break; case ConstPool.CONST_Class: type = CLASS_TYPE; break; default: throw new BadBytecode("bad LDC [pos = " + lastPos + "]: " + tag); } simplePush(type, frame); } private void evalLoad(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode { Type type = frame.getLocal(index); verifyAssignable(expected, type); simplePush(type, frame); access(index, type, subroutine); } private void evalNewArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode { verifyAssignable(Type.INTEGER, simplePop(frame)); Type type = null; int typeInfo = iter.byteAt(pos + 1); switch (typeInfo) { case T_BOOLEAN: type = getType("boolean[]"); break; case T_CHAR: type = getType("char[]"); break; case T_BYTE: type = getType("byte[]"); break; case T_SHORT: type = getType("short[]"); break; case T_INT: type = getType("int[]"); break; case T_LONG: type = getType("long[]"); break; case T_FLOAT: type = getType("float[]"); break; case T_DOUBLE: type = getType("double[]"); break; default: throw new BadBytecode("Invalid array type [pos = " + pos + "]: " + typeInfo); } frame.push(type); } private void evalNewObjectArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode { // Convert to x[] format Type type = resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1))); String name = type.getCtClass().getName(); int opcode = iter.byteAt(pos); int dimensions; if (opcode == MULTIANEWARRAY) { dimensions = iter.byteAt(pos + 3); } else { name = name + "[]"; dimensions = 1; } while (dimensions-- > 0) { verifyAssignable(Type.INTEGER, simplePop(frame)); } simplePush(getType(name), frame); } private void evalPutField(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getFieldrefType(index); Type type = zeroExtend(typeFromDesc(desc)); verifyAssignable(type, simplePop(frame)); if (opcode == PUTFIELD) { Type objectType = resolveClassInfo(constPool.getFieldrefClassName(index)); verifyAssignable(objectType, simplePop(frame)); } } private void evalShift(Type expected, Frame frame) throws BadBytecode { Type value2 = simplePop(frame); Type value1 = simplePop(frame); verifyAssignable(Type.INTEGER, value2); verifyAssignable(expected, value1); simplePush(value1, frame); } private void evalStore(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode { Type type = simplePop(frame); // RETURN_ADDRESS is allowed by ASTORE if (! (expected == Type.OBJECT && type == Type.RETURN_ADDRESS)) verifyAssignable(expected, type); simpleSetLocal(index, type, frame); access(index, type, subroutine); } private void evalWide(int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode { int opcode = iter.byteAt(pos + 1); int index = iter.u16bitAt(pos + 2); switch (opcode) { case ILOAD: evalLoad(Type.INTEGER, index, frame, subroutine); break; case LLOAD: evalLoad(Type.LONG, index, frame, subroutine); break; case FLOAD: evalLoad(Type.FLOAT, index, frame, subroutine); break; case DLOAD: evalLoad(Type.DOUBLE, index, frame, subroutine); break; case ALOAD: evalLoad(Type.OBJECT, index, frame, subroutine); break; case ISTORE: evalStore(Type.INTEGER, index, frame, subroutine); break; case LSTORE: evalStore(Type.LONG, index, frame, subroutine); break; case FSTORE: evalStore(Type.FLOAT, index, frame, subroutine); break; case DSTORE: evalStore(Type.DOUBLE, index, frame, subroutine); break; case ASTORE: evalStore(Type.OBJECT, index, frame, subroutine); break; case IINC: verifyAssignable(Type.INTEGER, frame.getLocal(index)); break; case RET: verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(index)); break; default: throw new BadBytecode("Invalid WIDE operand [pos = " + pos + "]: " + opcode); } } private Type getType(String name) throws BadBytecode { try { return Type.get(classPool.get(name)); } catch (NotFoundException e) { throw new BadBytecode("Could not find class [pos = " + lastPos + "]: " + name); } } private Type[] paramTypesFromDesc(String desc) throws BadBytecode { CtClass classes[] = null; try { classes = Descriptor.getParameterTypes(desc, classPool); } catch (NotFoundException e) { throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); } if (classes == null) throw new BadBytecode("Could not obtain parameters for descriptor [pos = " + lastPos + "]: " + desc); Type[] types = new Type[classes.length]; for (int i = 0; i < types.length; i++) types[i] = Type.get(classes[i]); return types; } private Type returnTypeFromDesc(String desc) throws BadBytecode { CtClass clazz = null; try { clazz = Descriptor.getReturnType(desc, classPool); } catch (NotFoundException e) { throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); } if (clazz == null) throw new BadBytecode("Could not obtain return type for descriptor [pos = " + lastPos + "]: " + desc); return Type.get(clazz); } private Type simplePeek(Frame frame) { Type type = frame.peek(); return (type == Type.TOP) ? frame.getStack(frame.getTopIndex() - 1) : type; } private Type simplePop(Frame frame) { Type type = frame.pop(); return (type == Type.TOP) ? frame.pop() : type; } private void simplePush(Type type, Frame frame) { frame.push(type); if (type.getSize() == 2) frame.push(Type.TOP); } private void access(int index, Type type, Subroutine subroutine) { if (subroutine == null) return; subroutine.access(index); if (type.getSize() == 2) subroutine.access(index + 1); } private void simpleSetLocal(int index, Type type, Frame frame) { frame.setLocal(index, type); if (type.getSize() == 2) frame.setLocal(index + 1, Type.TOP); } private Type resolveClassInfo(String info) throws BadBytecode { CtClass clazz = null; try { if (info.charAt(0) == '[') { clazz = Descriptor.toCtClass(info, classPool); } else { clazz = classPool.get(info); } } catch (NotFoundException e) { throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); } if (clazz == null) throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + info); return Type.get(clazz); } private Type typeFromDesc(String desc) throws BadBytecode { CtClass clazz = null; try { clazz = Descriptor.toCtClass(desc, classPool); } catch (NotFoundException e) { throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); } if (clazz == null) throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + desc); return Type.get(clazz); } private void verifyAssignable(Type expected, Type type) throws BadBytecode { if (! expected.isAssignableFrom(type)) throw new BadBytecode("Expected type: " + expected + " Got: " + type + " [pos = " + lastPos + "]"); } }