aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/javassist/bytecode/analysis/Executor.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/javassist/bytecode/analysis/Executor.java')
-rw-r--r--src/main/javassist/bytecode/analysis/Executor.java1012
1 files changed, 1012 insertions, 0 deletions
diff --git a/src/main/javassist/bytecode/analysis/Executor.java b/src/main/javassist/bytecode/analysis/Executor.java
new file mode 100644
index 00000000..7cf9a679
--- /dev/null
+++ b/src/main/javassist/bytecode/analysis/Executor.java
@@ -0,0 +1,1012 @@
+/*
+ * Javassist, a Java-bytecode translator toolkit.
+ * Copyright (C) 1999-2007 Shigeru Chiba, and others. 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.
+ *
+ * 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 javassist.bytecode.analysis;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+import javassist.bytecode.MethodInfo;
+import 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 186:
+ throw new RuntimeException("Bad opcode 186");
+ case NEW:
+ frame.push(typeFromDesc(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.getClassInfo(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 = typeFromDesc(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 classDesc = constPool.getInterfaceMethodrefClassName(index);
+ Type objectType = typeFromDesc(classDesc);
+ 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 = typeFromDesc(constPool.getMethodrefClassName(index));
+ verifyAssignable(objectType, simplePop(frame));
+ }
+
+ 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 = typeFromDesc(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 = typeFromDesc(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 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 + "]");
+ }
+}