Interpreter reworked and simplified

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1112 379699f6-c40d-0410-875b-85095c16579e
branch_1_1
jochen 26 years ago
parent b15a674928
commit f504348712
  1. 47
      jode/jode/expr/InvokeOperator.java.in
  2. 229
      jode/jode/jvm/Interpreter.java.in
  3. 47
      jode/jode/jvm/RuntimeEnvironment.java
  4. 18
      jode/jode/jvm/SimpleRuntimeEnvironment.java
  5. 25
      jode/jode/obfuscator/ConstantRuntimeEnvironment.java

@ -342,26 +342,25 @@ public final class InvokeOperator extends Operator
class Environment extends SimpleRuntimeEnvironment { class Environment extends SimpleRuntimeEnvironment {
Interpreter interpreter;
String classSig;
public Environment(String interpretedClassSig) {
classSig = interpretedClassSig.intern();
}
public Object invokeMethod(Reference ref, boolean isVirtual, public Object invokeMethod(Reference ref, boolean isVirtual,
Object cls, Object[] params) Object cls, Object[] params)
throws InterpreterException, InvocationTargetException { throws InterpreterException, InvocationTargetException {
if (ref.getClazz().equals if (cls == null && ref.getClazz().equals(classSig)) {
("L"+methodAnalyzer.getClazz().getName().replace('.','/')+";")) { BytecodeInfo info =
MethodType mt = (MethodType) Type.tType(ref.getType()); ClassInfo.forName(ref.getClazz())
BytecodeInfo info = methodAnalyzer.getClassAnalyzer() .findMethod(ref.getName(), ref.getType())
.getMethod(ref.getName(), mt).getBytecodeInfo(); .getBytecode();
Value[] locals = new Value[info.getMaxLocals()]; if (info != null)
for (int i=0; i< locals.length; i++) return interpreter.interpretMethod(info, null, params);
locals[i] = new Value(); throw new InterpreterException
int param = params.length; ("Can't interpret static native method: "+ref);
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);
} else } else
return super.invokeMethod(ref, isVirtual, cls, params); return super.invokeMethod(ref, isVirtual, cls, params);
} }
@ -372,15 +371,15 @@ public final class InvokeOperator extends Operator
MethodAnalyzer ma = clazz.getMethod(methodName, methodType); MethodAnalyzer ma = clazz.getMethod(methodName, methodType);
if (ma == null) if (ma == null)
return null; return null;
Environment env = new Environment(); Environment env = new Environment("L"+methodAnalyzer.getClazz()
BytecodeInfo info = ma.getBytecodeInfo(); .getName().replace('.','/')+";");
Value[] locals = new Value[info.getMaxLocals()]; Interpreter interpreter = new Interpreter(env);
for (int i=0; i< locals.length; i++) env.interpreter = interpreter;
locals[i] = new Value();
locals[0].setObject(op.getValue());
String result; String result;
try { try {
result = (String) Interpreter.interpretMethod(env, info, locals); result = (String) interpreter.interpretMethod
(ma.getBytecodeInfo(), null, new String[] { op.getValue() });
} catch (InterpreterException ex) { } catch (InterpreterException ex) {
GlobalOptions.err.println("Warning: Can't interpret method " GlobalOptions.err.println("Warning: Can't interpret method "
+methodName); +methodName);

@ -25,11 +25,11 @@ import jode.bytecode.Handler;
import jode.bytecode.Instruction; import jode.bytecode.Instruction;
import jode.bytecode.Opcodes; import jode.bytecode.Opcodes;
import jode.bytecode.Reference; import jode.bytecode.Reference;
import jode.type.MethodType; import jode.bytecode.TypeSignature;
import jode.type.Type;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import @COLLECTIONS@.Arrays;
/** /**
* This class is a java virtual machine written in java :-). Well not * This class is a java virtual machine written in java :-). Well not
@ -53,12 +53,53 @@ public class Interpreter implements Opcodes {
private final static int CMP_EQUAL_MASK private final static int CMP_EQUAL_MASK
= (1 << CMP_GE)|(1 << CMP_LE)|(1 << CMP_EQ); = (1 << CMP_GE)|(1 << CMP_LE)|(1 << CMP_EQ);
public static Object interpretMethod private RuntimeEnvironment env;
(RuntimeEnvironment env, BytecodeInfo code, Value[] locals)
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 { throws InterpreterException, InvocationTargetException {
if ((GlobalOptions.debuggingFlags if ((GlobalOptions.debuggingFlags
& GlobalOptions.DEBUG_INTERPRT) != 0) & GlobalOptions.DEBUG_INTERPRT) != 0)
GlobalOptions.err.println("Interpreting "+code); GlobalOptions.err.println("Interpreting "+code);
Value[] locals = fillParameters(code, instance, myParams);
Value[] stack = new Value[code.getMaxStack()]; 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(); stack[i] = new Value();
@ -78,7 +119,8 @@ public class Interpreter implements Opcodes {
GlobalOptions.err.print(","); GlobalOptions.err.print(",");
GlobalOptions.err.print(stack[i]); GlobalOptions.err.print(stack[i]);
if (stack[i].objectValue() instanceof char[]) { 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("]"); GlobalOptions.err.println("]");
@ -563,16 +605,31 @@ public class Interpreter implements Opcodes {
case opc_lookupswitch: { case opc_lookupswitch: {
int value = stack[--stacktop].intValue(); int value = stack[--stacktop].intValue();
int[] values = instr.getValues(); int[] values = instr.getValues();
pc = instr.getSuccs()[values.length]; int pos = Arrays.binarySearch(values, value);
for (int i=0; i< values.length; i++) { pc = pos < 0
if (values[i] == value) { ? instr.getSuccs()[values.length]
pc = instr.getSuccs()[i]; : instr.getSuccs()[pos];
break; break;
} }
} case opc_ireturn: {
break; Object result = stack[--stacktop].objectValue();
} String retType = TypeSignature
case opc_ireturn: case opc_freturn: case opc_areturn: .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(); return stack[--stacktop].objectValue();
case opc_lreturn: case opc_dreturn: case opc_lreturn: case opc_dreturn:
return stack[stacktop -= 2].objectValue(); return stack[stacktop -= 2].objectValue();
@ -580,34 +637,100 @@ public class Interpreter implements Opcodes {
return Void.TYPE; return Void.TYPE;
case opc_getstatic: { case opc_getstatic: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
stack[stacktop].setObject Object result = env.getField(instr.getReference(), null);
(env.getField(instr.getReference(), null)); char type = ref.getType().charAt(0);
stacktop += Type.tType(ref.getType()).stackSize(); 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; break;
} }
case opc_getfield: { case opc_getfield: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
Object cls = stack[--stacktop]; Object cls = stack[--stacktop].objectValue();
if (cls == null) if (cls == null)
throw new InvocationTargetException throw new InvocationTargetException
(new NullPointerException()); (new NullPointerException());
stack[stacktop].setObject Object result = env.getField(instr.getReference(), cls);
(env.getField(instr.getReference(), cls)); char type = ref.getType().charAt(0);
stacktop += Type.tType(ref.getType()).stackSize(); 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; break;
} }
case opc_putstatic: { case opc_putstatic: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
stacktop -= Type.tType(ref.getType()).stackSize(); stacktop -= TypeSignature.getTypeSize(ref.getType());
Object value = stack[stacktop]; 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); env.putField(instr.getReference(), null, value);
break; break;
} }
case opc_putfield: { case opc_putfield: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
stacktop -= Type.tType(ref.getType()).stackSize(); stacktop -= TypeSignature.getTypeSize(ref.getType());
Object value = stack[stacktop]; Object value = stack[stacktop].objectValue();
Object cls = stack[--stacktop]; 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) if (cls == null)
throw new InvocationTargetException throw new InvocationTargetException
(new NullPointerException()); (new NullPointerException());
@ -619,11 +742,30 @@ public class Interpreter implements Opcodes {
case opc_invokestatic : case opc_invokestatic :
case opc_invokeinterface: { case opc_invokeinterface: {
Reference ref = instr.getReference(); Reference ref = instr.getReference();
MethodType mt = (MethodType) Type.tType(ref.getType()); String[] paramTypes
Object[] args = new Object[mt.getParameterTypes().length]; = TypeSignature.getParameterTypes(ref.getType());
for (int i=args.length - 1; i >= 0; i--) { Object[] args = new Object[paramTypes.length];
stacktop -= mt.getParameterTypes()[i].stackSize(); for (int i = paramTypes.length - 1; i >= 0; i--) {
args[i] = stack[stacktop].objectValue(); 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; Object result = null;
@ -643,10 +785,29 @@ public class Interpreter implements Opcodes {
result = env.invokeMethod result = env.invokeMethod
(ref, opcode != opc_invokespecial, cls, args); (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); stack[stacktop].setObject(result);
stacktop += mt.getReturnType().stackSize(); break;
} }
stacktop += TypeSignature.getTypeSize(retType);
break; break;
} }
case opc_new: { case opc_new: {

@ -21,11 +21,56 @@ package jode.jvm;
import jode.bytecode.Reference; import jode.bytecode.Reference;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
/**
* This interface is used by the Interpreter to actually modify objects,
* invoke methods, etc. <br>
*
* 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). <br>
*
* @author Jochen Hoenicke */
public interface RuntimeEnvironment { 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) public Object getField(Reference fieldref, Object obj)
throws InterpreterException; 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) public void putField(Reference fieldref, Object obj, Object value)
throws InterpreterException; 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, public Object invokeMethod(Reference methodRef, boolean isVirtual,
Object cls, Object[] params) Object cls, Object[] params)
throws InterpreterException, InvocationTargetException; throws InterpreterException, InvocationTargetException;
@ -43,3 +88,5 @@ public interface RuntimeEnvironment {
public void exitMonitor(Object obj) public void exitMonitor(Object obj)
throws InterpreterException; throws InterpreterException;
} }

@ -154,24 +154,6 @@ public class SimpleRuntimeEnvironment implements RuntimeEnvironment {
(ref+": Security exception"); (ref+": Security exception");
} }
try { 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); return m.invoke(cls, params);
} catch (IllegalAccessException ex) { } catch (IllegalAccessException ex) {
throw new InterpreterException throw new InterpreterException

@ -18,7 +18,9 @@
*/ */
package jode.obfuscator; 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.Reference;
import jode.bytecode.BytecodeInfo; import jode.bytecode.BytecodeInfo;
import jode.type.*; import jode.type.*;
@ -214,7 +216,10 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment {
("Ljava/lang/Math;", "PI", "D")); ("Ljava/lang/Math;", "PI", "D"));
} }
private Interpreter interpreter;
public ConstantRuntimeEnvironment() { public ConstantRuntimeEnvironment() {
interpreter = new Interpreter(this);
} }
public Object getField(Reference ref, Object obj) 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) public void putField(Reference ref, Object obj, Object value)
throws InterpreterException { throws InterpreterException {
throw new InterpreterException("Modifiing Field " + ref + "."); throw new InterpreterException("Modifying Field " + ref + ".");
} }
public Object invokeConstructor(Reference ref, Object[] params) public Object invokeConstructor(Reference ref, Object[] params)
@ -255,20 +260,8 @@ public class ConstantRuntimeEnvironment extends SimpleRuntimeEnvironment {
= (MethodIdentifier) Main.getClassBundle().getIdentifier(ref); = (MethodIdentifier) Main.getClassBundle().getIdentifier(ref);
if (mi != null) { if (mi != null) {
BytecodeInfo code = mi.info.getBytecode(); BytecodeInfo code = mi.info.getBytecode();
if (code != null) { if (code != null)
MethodType mt = (MethodType) Type.tType(ref.getType()); return interpreter.interpretMethod(code, cls, params);
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);
}
} }
throw new InterpreterException("Invoking library method " + ref + "."); throw new InterpreterException("Invoking library method " + ref + ".");
} }

Loading…
Cancel
Save