/*
* 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 + "]");
}
}