From f504348712be2b4699bf979885367a74f1d12a02 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 22 Jul 1999 11:51:03 +0000 Subject: [PATCH] Interpreter reworked and simplified git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1112 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/expr/InvokeOperator.java.in | 47 ++-- jode/jode/jvm/Interpreter.java.in | 227 +++++++++++++++--- jode/jode/jvm/RuntimeEnvironment.java | 47 ++++ jode/jode/jvm/SimpleRuntimeEnvironment.java | 18 -- .../ConstantRuntimeEnvironment.java | 25 +- 5 files changed, 273 insertions(+), 91 deletions(-) diff --git a/jode/jode/expr/InvokeOperator.java.in b/jode/jode/expr/InvokeOperator.java.in index ec316c4..b5c1e9d 100644 --- a/jode/jode/expr/InvokeOperator.java.in +++ b/jode/jode/expr/InvokeOperator.java.in @@ -342,26 +342,25 @@ public final class InvokeOperator extends Operator class Environment extends SimpleRuntimeEnvironment { + Interpreter interpreter; + String classSig; + + public Environment(String interpretedClassSig) { + classSig = interpretedClassSig.intern(); + } + public Object invokeMethod(Reference ref, boolean isVirtual, Object cls, Object[] params) throws InterpreterException, InvocationTargetException { - if (ref.getClazz().equals - ("L"+methodAnalyzer.getClazz().getName().replace('.','/')+";")) { - MethodType mt = (MethodType) Type.tType(ref.getType()); - BytecodeInfo info = methodAnalyzer.getClassAnalyzer() - .getMethod(ref.getName(), mt).getBytecodeInfo(); - Value[] locals = new Value[info.getMaxLocals()]; - for (int i=0; i< locals.length; i++) - locals[i] = new Value(); - int param = params.length; - int slot = 0; - if (cls != null) - locals[slot++].setObject(cls); - for (int i = 0; i < param; i++) { - locals[slot].setObject(params[i]); - slot += mt.getParameterTypes()[i].stackSize(); - } - return Interpreter.interpretMethod(this, info, locals); + if (cls == null && ref.getClazz().equals(classSig)) { + BytecodeInfo info = + ClassInfo.forName(ref.getClazz()) + .findMethod(ref.getName(), ref.getType()) + .getBytecode(); + if (info != null) + return interpreter.interpretMethod(info, null, params); + throw new InterpreterException + ("Can't interpret static native method: "+ref); } else return super.invokeMethod(ref, isVirtual, cls, params); } @@ -372,15 +371,15 @@ public final class InvokeOperator extends Operator MethodAnalyzer ma = clazz.getMethod(methodName, methodType); if (ma == null) return null; - Environment env = new Environment(); - BytecodeInfo info = ma.getBytecodeInfo(); - Value[] locals = new Value[info.getMaxLocals()]; - for (int i=0; i< locals.length; i++) - locals[i] = new Value(); - locals[0].setObject(op.getValue()); + Environment env = new Environment("L"+methodAnalyzer.getClazz() + .getName().replace('.','/')+";"); + Interpreter interpreter = new Interpreter(env); + env.interpreter = interpreter; + String result; try { - result = (String) Interpreter.interpretMethod(env, info, locals); + result = (String) interpreter.interpretMethod + (ma.getBytecodeInfo(), null, new String[] { op.getValue() }); } catch (InterpreterException ex) { GlobalOptions.err.println("Warning: Can't interpret method " +methodName); diff --git a/jode/jode/jvm/Interpreter.java.in b/jode/jode/jvm/Interpreter.java.in index 825c050..011b5f3 100644 --- a/jode/jode/jvm/Interpreter.java.in +++ b/jode/jode/jvm/Interpreter.java.in @@ -25,11 +25,11 @@ import jode.bytecode.Handler; import jode.bytecode.Instruction; import jode.bytecode.Opcodes; import jode.bytecode.Reference; -import jode.type.MethodType; -import jode.type.Type; +import jode.bytecode.TypeSignature; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; +import @COLLECTIONS@.Arrays; /** * This class is a java virtual machine written in java :-). Well not @@ -53,14 +53,55 @@ public class Interpreter implements Opcodes { private final static int CMP_EQUAL_MASK = (1 << CMP_GE)|(1 << CMP_LE)|(1 << CMP_EQ); - public static Object interpretMethod - (RuntimeEnvironment env, BytecodeInfo code, Value[] locals) + private RuntimeEnvironment env; + + public Interpreter(RuntimeEnvironment env) { + this.env = env; + } + + private Value[] fillParameters(BytecodeInfo code, + Object cls, Object[] params) { + Value[] locals = new Value[code.getMaxLocals()]; + for (int i=0; i< locals.length; i++) + locals[i] = new Value(); + + String myType = code.getMethodInfo().getType(); + String[] myParamTypes = TypeSignature.getParameterTypes(myType); + int slot = 0; + if (!code.getMethodInfo().isStatic()) + locals[slot++].setObject(cls); + for (int i=0; i< myParamTypes.length; i++) { + char type = myParamTypes[i].charAt(0); + switch ("ZBSC".indexOf(type)) { + case 0: + locals[slot].setInt(((Boolean) params[i]) + .booleanValue() ? 1 : 0); + break; + case 1: case 2: + locals[slot].setInt(((Number) params[i]).intValue()); + break; + case 3: + locals[slot].setInt(((Character) params[i]).charValue()); + break; + default: + locals[slot].setObject(params[i]); + break; + } + slot += TypeSignature.getTypeSize(myParamTypes[i]); + } + return locals; + } + + public Object interpretMethod(BytecodeInfo code, + Object instance, Object[] myParams) throws InterpreterException, InvocationTargetException { if ((GlobalOptions.debuggingFlags & GlobalOptions.DEBUG_INTERPRT) != 0) GlobalOptions.err.println("Interpreting "+code); + + Value[] locals = fillParameters(code, instance, myParams); Value[] stack = new Value[code.getMaxStack()]; - for (int i=0; i< stack.length; i++) + for (int i=0; i < stack.length; i++) stack[i] = new Value(); Instruction pc = (Instruction) code.getInstructions().get(0); @@ -78,7 +119,8 @@ public class Interpreter implements Opcodes { 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.print + (new String((char[])stack[i].objectValue())); } } GlobalOptions.err.println("]"); @@ -563,16 +605,31 @@ public class Interpreter implements Opcodes { case opc_lookupswitch: { int value = stack[--stacktop].intValue(); int[] values = instr.getValues(); - pc = instr.getSuccs()[values.length]; - for (int i=0; i< values.length; i++) { - if (values[i] == value) { - pc = instr.getSuccs()[i]; - break; - } - } + int pos = Arrays.binarySearch(values, value); + pc = pos < 0 + ? instr.getSuccs()[values.length] + : instr.getSuccs()[pos]; break; } - case opc_ireturn: case opc_freturn: case opc_areturn: + case opc_ireturn: { + Object result = stack[--stacktop].objectValue(); + String retType = TypeSignature + .getReturnType(code.getMethodInfo().getType()); + switch ("ZBSC".indexOf(retType.charAt(0))) { + case 0: // boolean + return new Boolean(((Integer)result).intValue() != 0); + case 1: // byte + return new Byte(((Integer)result).byteValue()); + case 2: // short + return new Short(((Integer)result).shortValue()); + case 3: // char + return new Character((char) + ((Integer)result).intValue()); + default: // integer + return result; + } + } + case opc_freturn: case opc_areturn: return stack[--stacktop].objectValue(); case opc_lreturn: case opc_dreturn: return stack[stacktop -= 2].objectValue(); @@ -580,34 +637,100 @@ public class Interpreter implements Opcodes { return Void.TYPE; case opc_getstatic: { Reference ref = instr.getReference(); - stack[stacktop].setObject - (env.getField(instr.getReference(), null)); - stacktop += Type.tType(ref.getType()).stackSize(); + Object result = env.getField(instr.getReference(), null); + char type = ref.getType().charAt(0); + switch ("ZBSC".indexOf(type)) { + case 0: + stack[stacktop].setInt(((Boolean) result) + .booleanValue() ? 1 : 0); + break; + case 1: case 2: + stack[stacktop].setInt(((Number) result) + .intValue()); + break; + case 3: + stack[stacktop].setInt(((Character) result) + .charValue()); + break; + default: + stack[stacktop].setObject(result); + break; + } + stacktop += TypeSignature.getTypeSize(ref.getType()); break; } case opc_getfield: { Reference ref = instr.getReference(); - Object cls = stack[--stacktop]; + Object cls = stack[--stacktop].objectValue(); if (cls == null) throw new InvocationTargetException (new NullPointerException()); - stack[stacktop].setObject - (env.getField(instr.getReference(), cls)); - stacktop += Type.tType(ref.getType()).stackSize(); + Object result = env.getField(instr.getReference(), cls); + char type = ref.getType().charAt(0); + switch ("ZBSC".indexOf(type)) { + case 0: + stack[stacktop].setInt(((Boolean) result) + .booleanValue() ? 1 : 0); + break; + case 1: case 2: + stack[stacktop].setInt(((Number) result) + .intValue()); + break; + case 3: + stack[stacktop].setInt(((Character) result) + .charValue()); + break; + default: + stack[stacktop].setObject(result); + break; + } + stacktop += TypeSignature.getTypeSize(ref.getType()); break; } case opc_putstatic: { Reference ref = instr.getReference(); - stacktop -= Type.tType(ref.getType()).stackSize(); - Object value = stack[stacktop]; + stacktop -= TypeSignature.getTypeSize(ref.getType()); + Object value = stack[stacktop].objectValue(); + int type = "ZBSC".indexOf(ref.getType().charAt(0)); + switch(type) { + case 0: // boolean + value = new Boolean(((Integer)value).intValue() != 0); + break; + case 1: // byte + value = new Byte(((Integer)value).byteValue()); + break; + case 2: // short + value = new Short(((Integer)value).shortValue()); + break; + case 3: // char + value = new Character((char) + ((Integer)value).intValue()); + break; + } env.putField(instr.getReference(), null, value); break; } case opc_putfield: { Reference ref = instr.getReference(); - stacktop -= Type.tType(ref.getType()).stackSize(); - Object value = stack[stacktop]; - Object cls = stack[--stacktop]; + stacktop -= TypeSignature.getTypeSize(ref.getType()); + Object value = stack[stacktop].objectValue(); + int type = "ZBSC".indexOf(ref.getType().charAt(0)); + switch(type) { + case 0: // boolean + value = new Boolean(((Integer)value).intValue() != 0); + break; + case 1: // byte + value = new Byte(((Integer)value).byteValue()); + break; + case 2: // short + value = new Short(((Integer)value).shortValue()); + break; + case 3: // char + value = new Character((char) + ((Integer)value).intValue()); + break; + } + Object cls = stack[--stacktop].objectValue(); if (cls == null) throw new InvocationTargetException (new NullPointerException()); @@ -619,11 +742,30 @@ public class Interpreter implements Opcodes { case opc_invokestatic : case opc_invokeinterface: { Reference ref = instr.getReference(); - MethodType mt = (MethodType) Type.tType(ref.getType()); - Object[] args = new Object[mt.getParameterTypes().length]; - for (int i=args.length - 1; i >= 0; i--) { - stacktop -= mt.getParameterTypes()[i].stackSize(); - args[i] = stack[stacktop].objectValue(); + 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]); + Object value = stack[stacktop].objectValue(); + int type = "ZBSC".indexOf(paramTypes[i].charAt(0)); + switch(type) { + case 0: // boolean + value = new Boolean(((Integer)value) + .intValue() != 0); + break; + case 1: // byte + value = new Byte(((Integer)value).byteValue()); + break; + case 2: // short + value = new Short(((Integer)value).shortValue()); + break; + case 3: // char + value = new Character((char) + ((Integer)value).intValue()); + break; + } + args[i] = value; } Object result = null; @@ -643,10 +785,29 @@ public class Interpreter implements Opcodes { result = env.invokeMethod (ref, opcode != opc_invokespecial, cls, args); } - if (mt.getReturnType() != Type.tVoid) { + String retType + = TypeSignature.getReturnType(ref.getType()); + char type = retType.charAt(0); + switch ("ZBSCV".indexOf(type)) { + case 0: + stack[stacktop].setInt(((Boolean) result) + .booleanValue() ? 1 : 0); + break; + case 1: case 2: + stack[stacktop].setInt(((Number) result) + .intValue()); + break; + case 3: + stack[stacktop].setInt(((Character) result) + .charValue()); + break; + case 4: // Void + break; + default: stack[stacktop].setObject(result); - stacktop += mt.getReturnType().stackSize(); + break; } + stacktop += TypeSignature.getTypeSize(retType); break; } case opc_new: { diff --git a/jode/jode/jvm/RuntimeEnvironment.java b/jode/jode/jvm/RuntimeEnvironment.java index 97292af..d6d08c9 100644 --- a/jode/jode/jvm/RuntimeEnvironment.java +++ b/jode/jode/jvm/RuntimeEnvironment.java @@ -21,11 +21,56 @@ package jode.jvm; import jode.bytecode.Reference; import java.lang.reflect.InvocationTargetException; +/** + * This interface is used by the Interpreter to actually modify objects, + * invoke methods, etc.
+ * + * The objects used in this runtime environment need not to be of the + * real class, but could also be of some wrapper class. The only + * exception are arrays, which must be arrays (but not necessarily of + * the real element type).
+ * + * @author Jochen Hoenicke */ public interface RuntimeEnvironment { + /** + * Get the value of a field member. + * @param fieldref the Reference of the field. + * @param obj the object of which the field should be taken, null + * if the field is static. + * @return the field value. Primitive types are wrapped to + * Object. + * @exception InterpreterException if the field does not exists, the + * object is not supported etc. + */ public Object getField(Reference fieldref, Object obj) throws InterpreterException; + + /** + * Set the value of a field member. + * @param fieldref the Reference of the field. + * @param obj the object of which the field should be taken, null + * if the field is static. + * @param value the field value. Primitive types are wrapped to + * Object. + * @exception InterpreterException if the field does not exists, the + * object is not supported etc. + */ public void putField(Reference fieldref, Object obj, Object value) throws InterpreterException; + + + /** + * Invoke a method. + * @param methodRef the reference to the method. + * @param isVirtual true, iff the call is virtual + * @param cls the object on which the method should be called, null + * if the method is static. + * @param params the params of the method. Primitive types are + * wrapped to Object. + * @return the return value of the method. Primitive types are + * wrapped to Object, void type is ignored, may be null. + * @exception InterpreterException if the field does not exists, the + * object is not supported etc. */ public Object invokeMethod(Reference methodRef, boolean isVirtual, Object cls, Object[] params) throws InterpreterException, InvocationTargetException; @@ -43,3 +88,5 @@ public interface RuntimeEnvironment { public void exitMonitor(Object obj) throws InterpreterException; } + + diff --git a/jode/jode/jvm/SimpleRuntimeEnvironment.java b/jode/jode/jvm/SimpleRuntimeEnvironment.java index 201ca89..900ad26 100644 --- a/jode/jode/jvm/SimpleRuntimeEnvironment.java +++ b/jode/jode/jvm/SimpleRuntimeEnvironment.java @@ -154,24 +154,6 @@ public class SimpleRuntimeEnvironment implements RuntimeEnvironment { (ref+": Security exception"); } try { - Type[] paramTypes = mt.getParameterTypes(); - for (int i = 0; i< paramTypes.length; i++) { - if (paramTypes[i] instanceof IntegerType - && paramTypes[i] != Type.tInt) { - int value = ((Integer) params[i]).intValue(); - if (paramTypes[i] == Type.tBoolean) { - params[i] = value != 0 ? Boolean.TRUE : Boolean.FALSE; - } else if (paramTypes[i] == Type.tChar) { - params[i] = new Character((char) value); - } else if (paramTypes[i] == Type.tByte) { - params[i] = new Byte((byte) value); - } else if (paramTypes[i] == Type.tShort) { - params[i] = new Short((short) value); - } else - throw new AssertError("Unknown integer type: " - +paramTypes[i]); - } - } return m.invoke(cls, params); } catch (IllegalAccessException ex) { throw new InterpreterException diff --git a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java index c2a99f5..d882507 100644 --- a/jode/jode/obfuscator/ConstantRuntimeEnvironment.java +++ b/jode/jode/obfuscator/ConstantRuntimeEnvironment.java @@ -18,7 +18,9 @@ */ package jode.obfuscator; -import jode.jvm.*; +import jode.jvm.Interpreter; +import jode.jvm.SimpleRuntimeEnvironment; +import jode.jvm.InterpreterException; import jode.bytecode.Reference; import jode.bytecode.BytecodeInfo; import jode.type.*; @@ -213,8 +215,11 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment { addWhite(Reference.getReference ("Ljava/lang/Math;", "PI", "D")); } + + private Interpreter interpreter; public ConstantRuntimeEnvironment() { + interpreter = new Interpreter(this); } public Object getField(Reference ref, Object obj) @@ -235,7 +240,7 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment { public void putField(Reference ref, Object obj, Object value) throws InterpreterException { - throw new InterpreterException("Modifiing Field " + ref + "."); + throw new InterpreterException("Modifying Field " + ref + "."); } public Object invokeConstructor(Reference ref, Object[] params) @@ -255,20 +260,8 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment { = (MethodIdentifier) Main.getClassBundle().getIdentifier(ref); if (mi != null) { BytecodeInfo code = mi.info.getBytecode(); - if (code != null) { - MethodType mt = (MethodType) Type.tType(ref.getType()); - Value[] locals = new Value[code.getMaxLocals()]; - for (int i=0; i< locals.length; i++) - locals[i] = new Value(); - int slot = 0; - if (cls != null) - locals[slot++].setObject(cls); - for (int i = 0; i < params.length; i++) { - locals[slot].setObject(params[i]); - slot += mt.getParameterTypes()[i].stackSize(); - } - return Interpreter.interpretMethod(this, code, locals); - } + if (code != null) + return interpreter.interpretMethod(code, cls, params); } throw new InterpreterException("Invoking library method " + ref + "."); }