/* Interpreter Copyright (C) 1999-2002 Jochen Hoenicke. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; see the file COPYING.LESSER. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package net.sf.jode.jvm; import net.sf.jode.GlobalOptions; import net.sf.jode.bytecode.BasicBlocks; import net.sf.jode.bytecode.Block; import net.sf.jode.bytecode.Handler; import net.sf.jode.bytecode.Instruction; import net.sf.jode.bytecode.Opcodes; import net.sf.jode.bytecode.Reference; import net.sf.jode.bytecode.TypeSignature; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; ///#def COLLECTIONS java.util import java.util.Arrays; import java.util.Iterator; ///#enddef /** * This class is a java virtual machine written in java :-). Well not * exactly. It is only a bytecode interpreter, you have to supply the * rest of the VM (the runtime environment). * * @author Jochen Hoenicke */ public class Interpreter implements Opcodes { private final static int CMP_EQ = 0; private final static int CMP_NE = 1; private final static int CMP_LT = 2; private final static int CMP_GE = 3; private final static int CMP_GT = 4; private final static int CMP_LE = 5; private final static int CMP_GREATER_MASK = (1 << CMP_GT)|(1 << CMP_GE)|(1 << CMP_NE); private final static int CMP_LESS_MASK = (1 << CMP_LT)|(1 << CMP_LE)|(1 << CMP_NE); private final static int CMP_EQUAL_MASK = (1 << CMP_GE)|(1 << CMP_LE)|(1 << CMP_EQ); private RuntimeEnvironment env; public Interpreter(RuntimeEnvironment env) { this.env = env; } private Value[] fillParameters(BasicBlocks bb, Object cls, Object[] params) { Value[] locals = new Value[bb.getMaxLocals()]; for (int i=0; i< locals.length; i++) locals[i] = new Value(); String myType = bb.getMethodInfo().getType(); String[] myParamTypes = TypeSignature.getParameterTypes(myType); int slot = 0; if (!bb.getMethodInfo().isStatic()) locals[slot++].setObject(cls); for (int i=0; i< myParamTypes.length; i++) { locals[slot].setObject(params[i]); slot += TypeSignature.getTypeSize(myParamTypes[i]); } return locals; } public Object interpretMethod(BasicBlocks bb, Object instance, Object[] myParams) throws InterpreterException, InvocationTargetException { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INTERPRT) != 0) GlobalOptions.err.println("Interpreting "+bb); Value[] locals = fillParameters(bb, instance, myParams); Value[] stack = new Value[bb.getMaxStack()]; for (int i=0; i < stack.length; i++) stack[i] = new Value(); Block nextBlock = bb.getStartBlock(); int stacktop = 0; Block[] succs = null; Handler[] handlers = null; Iterator iter = null; big_loop: for(;;) { if (iter == null || !iter.hasNext()) { /* If block is over continue with the next block */ if (nextBlock == null) return Void.TYPE; iter = Arrays.asList(nextBlock.getInstructions()).iterator(); succs = nextBlock.getSuccs(); handlers = nextBlock.getHandlers(); nextBlock = succs.length > 0 ? succs[succs.length - 1] : null; } try { Instruction instr = (Instruction) iter.next(); if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INTERPRT) != 0) { GlobalOptions.err.println(instr.getDescription()); GlobalOptions.err.print("stack: ["); for (int i=0; i0) GlobalOptions.err.print(","); GlobalOptions.err.print(stack[i]); if (stack[i].objectValue() instanceof char[]) { GlobalOptions.err.print (new String((char[])stack[i].objectValue())); } } GlobalOptions.err.println("]"); GlobalOptions.err.print("local: ["); for (int i=0; i> stack[stacktop-1].intValue()); stacktop--; break; case opc_iushr: stack[stacktop-2].setInt(stack[stacktop-2].intValue() >>> stack[stacktop-1].intValue()); stacktop--; break; case opc_iand: stack[stacktop-2].setInt(stack[stacktop-2].intValue() & stack[stacktop-1].intValue()); stacktop--; break; case opc_ior : stack[stacktop-2].setInt(stack[stacktop-2].intValue() | stack[stacktop-1].intValue()); stacktop--; break; case opc_ixor: stack[stacktop-2].setInt(stack[stacktop-2].intValue() ^ stack[stacktop-1].intValue()); stacktop--; break; case opc_lshl: stack[stacktop-3].setLong(stack[stacktop-3].longValue() << stack[stacktop-1].intValue()); stacktop--; break; case opc_lshr: stack[stacktop-3].setLong(stack[stacktop-3].longValue() >> stack[stacktop-1].intValue()); stacktop--; break; case opc_lushr: stack[stacktop-3].setLong(stack[stacktop-3].longValue() >>> stack[stacktop-1].intValue()); stacktop--; break; case opc_land: stacktop-=2; stack[stacktop-2].setLong(stack[stacktop-2].longValue() & stack[stacktop].longValue()); break; case opc_lor : stacktop-=2; stack[stacktop-2].setLong(stack[stacktop-2].longValue() | stack[stacktop].longValue()); break; case opc_lxor: stacktop-=2; stack[stacktop-2].setLong(stack[stacktop-2].longValue() ^ stack[stacktop].longValue()); break; case opc_iinc: locals[instr.getLocalSlot()].setInt (locals[instr.getLocalSlot()].intValue() + instr.getIncrement()); break; case opc_i2l: stack[stacktop-1] .setLong((long)stack[stacktop-1].intValue()); stacktop++; break; case opc_i2f: stack[stacktop-1] .setFloat((float)stack[stacktop-1].intValue()); break; case opc_i2d: stack[stacktop-1] .setDouble((double)stack[stacktop-1].intValue()); stacktop++; break; case opc_l2i: stacktop--; stack[stacktop-1] .setInt((int)stack[stacktop-1].longValue()); break; case opc_l2f: stacktop--; stack[stacktop-1] .setFloat((float)stack[stacktop-1].longValue()); break; case opc_l2d: stack[stacktop-2] .setDouble((double)stack[stacktop-2].longValue()); break; case opc_f2i: stack[stacktop-1] .setInt((int)stack[stacktop-1].floatValue()); break; case opc_f2l: stack[stacktop-1] .setLong((long)stack[stacktop-1].floatValue()); stacktop++; break; case opc_f2d: stack[stacktop-1] .setDouble((double)stack[stacktop-1].floatValue()); stacktop++; break; case opc_d2i: stacktop--; stack[stacktop-1] .setInt((int)stack[stacktop-1].doubleValue()); break; case opc_d2l: stack[stacktop-2] .setLong((long)stack[stacktop-2].doubleValue()); break; case opc_d2f: stacktop--; stack[stacktop-1] .setFloat((float)stack[stacktop-1].doubleValue()); break; case opc_i2b: stack[stacktop-1] .setInt((byte)stack[stacktop-1].intValue()); break; case opc_i2c: stack[stacktop-1] .setInt((char)stack[stacktop-1].intValue()); break; case opc_i2s: stack[stacktop-1] .setInt((short)stack[stacktop-1].intValue()); break; case opc_lcmp: { stacktop -= 3; long val1 = stack[stacktop-1].longValue(); long val2 = stack[stacktop+1].longValue(); stack[stacktop-1].setInt (val1 == val2 ? 0 : val1 < val2 ? -1 : 1); break; } case opc_fcmpl: case opc_fcmpg: { float val1 = stack[stacktop-2].floatValue(); float val2 = stack[--stacktop].floatValue(); stack[stacktop-1].setInt (val1 == val2 ? 0 : ( opcode == opc_fcmpg ? (val1 < val2 ? -1 : 1) : (val1 > val2 ? 1 : -1))); break; } case opc_dcmpl: case opc_dcmpg: { stacktop -= 3; double val1 = stack[stacktop-1].doubleValue(); double val2 = stack[stacktop+1].doubleValue(); stack[stacktop-1].setInt (val1 == val2 ? 0 : ( opcode == opc_dcmpg ? (val1 < val2 ? -1 : 1) : (val1 > val2 ? 1 : -1))); break; } case opc_ifeq: case opc_ifne: case opc_iflt: case opc_ifge: case opc_ifgt: case opc_ifle: case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmplt: case opc_if_icmpge: case opc_if_icmpgt: case opc_if_icmple: case opc_if_acmpeq: case opc_if_acmpne: case opc_ifnull: case opc_ifnonnull: { int value; if (opcode >= opc_if_acmpeq) { Object objValue = stack[--stacktop].objectValue(); if (opcode >= opc_ifnull) { value = objValue == null ? 0 : 1; opcode -= opc_ifnull; } else { value = objValue == stack[--stacktop].objectValue() ? 0 : 1; opcode -= opc_if_acmpeq; } } else { value = stack[--stacktop].intValue(); if (opcode >= opc_if_icmpeq) { int val1 = stack[--stacktop].intValue(); value = (val1 == value ? 0 : val1 < value ? -1 : 1); opcode -= opc_if_icmpeq; } else opcode -= opc_ifeq; } int opc_mask = 1 << opcode; if (value > 0 && (opc_mask & CMP_GREATER_MASK) != 0 || value < 0 && (opc_mask & CMP_LESS_MASK) != 0 || value == 0 && (opc_mask & CMP_EQUAL_MASK) != 0) { nextBlock = succs[0]; } break; } case opc_jsr: case opc_jsr_w: stack[stacktop++].setObject(nextBlock); nextBlock = succs[0]; break; case opc_ret: nextBlock = (Block) locals[instr.getLocalSlot()].objectValue(); break; case opc_lookupswitch: { int value = stack[--stacktop].intValue(); int[] values = instr.getValues(); int pos = Arrays.binarySearch(values, value); if (pos >= 0) nextBlock = succs[pos]; break; } case opc_ireturn: case opc_freturn: case opc_areturn: return stack[--stacktop].objectValue(); case opc_lreturn: case opc_dreturn: return stack[stacktop -= 2].objectValue(); case opc_return: return Void.TYPE; case opc_getstatic: { Reference ref = instr.getReference(); Object result = env.getField(instr.getReference(), null); stack[stacktop].setObject(result); stacktop += TypeSignature.getTypeSize(ref.getType()); break; } case opc_getfield: { Reference ref = instr.getReference(); Object cls = stack[--stacktop].objectValue(); if (cls == null) throw new InvocationTargetException (new NullPointerException()); Object result = env.getField(instr.getReference(), cls); stack[stacktop].setObject(result); stacktop += TypeSignature.getTypeSize(ref.getType()); break; } case opc_putstatic: { Reference ref = instr.getReference(); stacktop -= TypeSignature.getTypeSize(ref.getType()); Object value = stack[stacktop].objectValue(); env.putField(instr.getReference(), null, value); break; } case opc_putfield: { Reference ref = instr.getReference(); stacktop -= TypeSignature.getTypeSize(ref.getType()); Object value = stack[stacktop].objectValue(); Object cls = stack[--stacktop].objectValue(); if (cls == null) throw new InvocationTargetException (new NullPointerException()); env.putField(instr.getReference(), cls, value); break; } case opc_invokevirtual: case opc_invokespecial: case opc_invokestatic : case opc_invokeinterface: { Reference ref = instr.getReference(); String[] paramTypes = TypeSignature.getParameterTypes(ref.getType()); Object[] args = new Object[paramTypes.length]; for (int i = paramTypes.length - 1; i >= 0; i--) { stacktop -= TypeSignature.getTypeSize(paramTypes[i]); args[i] = stack[stacktop].objectValue(); } Object result = null; if (opcode == opc_invokespecial && ref.getName().equals("") && stack[--stacktop].getNewObject() != null) { NewObject newObj = stack[stacktop].getNewObject(); if (!newObj.getType().equals(ref.getClazz())) throw new InterpreterException ("constructor doesn't match new"); newObj.setObject(env.invokeConstructor(ref, args)); } else if (opcode == opc_invokestatic) { result = env.invokeMethod(ref, false, null, args); } else { Object cls = stack[--stacktop].objectValue(); if (cls == null) throw new InvocationTargetException (new NullPointerException()); result = env.invokeMethod (ref, opcode != opc_invokespecial, cls, args); } String retType = TypeSignature.getReturnType(ref.getType()); if (!retType.equals("V")) { stack[stacktop].setObject(result); stacktop += TypeSignature.getTypeSize(retType); } break; } case opc_new: { String clazz = instr.getClazzType(); stack[stacktop++].setNewObject(new NewObject(clazz)); break; } case opc_arraylength: { Object array = stack[--stacktop].objectValue(); if (array == null) throw new InvocationTargetException (new NullPointerException()); stack[stacktop++].setInt(Array.getLength(array)); break; } case opc_athrow: { Throwable exc = (Throwable) stack[--stacktop].objectValue(); throw new InvocationTargetException (exc == null ? new NullPointerException() : exc); } case opc_checkcast: { Object obj = stack[stacktop-1].objectValue(); if (obj != null && !env.instanceOf(obj, instr.getClazzType())) throw new InvocationTargetException (new ClassCastException(obj.getClass().getName())); break; } case opc_instanceof: { Object obj = stack[--stacktop].objectValue(); stack[stacktop++].setInt (env.instanceOf(obj, instr.getClazzType()) ? 1 : 0); break; } case opc_monitorenter: env.enterMonitor(stack[--stacktop].objectValue()); break; case opc_monitorexit: env.exitMonitor(stack[--stacktop].objectValue()); break; case opc_multianewarray: { int dimension = instr.getDimensions(); int[] dims = new int[dimension]; for (int i=dimension - 1; i >= 0; i--) dims[i] = stack[--stacktop].intValue(); try { stack[stacktop++].setObject (env.newArray(instr.getClazzType(), dims)); } catch (NegativeArraySizeException ex) { throw new InvocationTargetException(ex); } break; } default: throw new InternalError("Invalid opcode "+opcode); } } catch (InvocationTargetException ex) { iter = null; Throwable obj = ex.getTargetException(); for (int i=0; i < handlers.length; i++) { if (handlers[i].getType() == null || env.instanceOf(obj, handlers[i].getType())) { stacktop = 0; stack[stacktop++].setObject(obj); nextBlock = handlers[i].getCatcher(); continue big_loop; } } throw ex; } } } }