|
|
@ -1,26 +1,26 @@ |
|
|
|
/* |
|
|
|
/* Interpreter Copyright (C) 1999 Jochen Hoenicke. |
|
|
|
* Opcodes (c) 1998 Jochen Hoenicke |
|
|
|
|
|
|
|
* |
|
|
|
* |
|
|
|
* You may distribute under the terms of the GNU General Public License. |
|
|
|
* This program is free software; you can redistribute it and/or modify |
|
|
|
|
|
|
|
* it under the terms of the GNU General Public License as published by |
|
|
|
|
|
|
|
* the Free Software Foundation; either version 2, or (at your option) |
|
|
|
|
|
|
|
* any later version. |
|
|
|
* |
|
|
|
* |
|
|
|
* IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, |
|
|
|
* This program is distributed in the hope that it will be useful, |
|
|
|
* INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF |
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
|
* THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE |
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
* HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
* GNU General Public License for more details. |
|
|
|
* |
|
|
|
* |
|
|
|
* JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT |
|
|
|
* You should have received a copy of the GNU General Public License |
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
|
|
|
* along with this program; see the file COPYING. If not, write to |
|
|
|
* PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" |
|
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
|
|
|
* BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, |
|
|
|
|
|
|
|
* SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
|
|
|
|
|
|
|
* |
|
|
|
* |
|
|
|
* $Id$ |
|
|
|
* $Id$ |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
package jode.jvm; |
|
|
|
package jode.jvm; |
|
|
|
|
|
|
|
import jode.*; |
|
|
|
import jode.bytecode.*; |
|
|
|
import jode.bytecode.*; |
|
|
|
import jode.decompiler.ClassAnalyzer; |
|
|
|
import jode.decompiler.ClassAnalyzer; |
|
|
|
import jode.MethodType; |
|
|
|
|
|
|
|
import jode.Type; |
|
|
|
|
|
|
|
import java.lang.reflect.*; |
|
|
|
import java.lang.reflect.*; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -32,84 +32,46 @@ import java.lang.reflect.*; |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class Interpreter implements Opcodes { |
|
|
|
public class Interpreter implements Opcodes { |
|
|
|
|
|
|
|
|
|
|
|
static int checkType(String typesig, int pos, Class type) { |
|
|
|
static boolean checkType(Type type1, Class type2) { |
|
|
|
switch (typesig.charAt(pos++)) { |
|
|
|
if (type1 == Type.tBoolean) { |
|
|
|
case 'Z': |
|
|
|
return type2 == Boolean.TYPE; |
|
|
|
if (type != Boolean.TYPE) |
|
|
|
} else if (type1 == Type.tByte) { |
|
|
|
return -1; |
|
|
|
return type2 == Byte.TYPE; |
|
|
|
break; |
|
|
|
} else if (type1 == Type.tChar) { |
|
|
|
case 'B': |
|
|
|
return type2 == Character.TYPE; |
|
|
|
if (type != Byte.TYPE) |
|
|
|
} else if (type1 == Type.tShort) { |
|
|
|
return -1; |
|
|
|
return type2 == Short.TYPE; |
|
|
|
break; |
|
|
|
} else if (type1 == Type.tInt) { |
|
|
|
case 'C': |
|
|
|
return type2 == Integer.TYPE; |
|
|
|
if (type != Character.TYPE) |
|
|
|
} else if (type1 == Type.tLong) { |
|
|
|
return -1; |
|
|
|
return type2 == Long.TYPE; |
|
|
|
break; |
|
|
|
} else if (type1 == Type.tFloat) { |
|
|
|
case 'S': |
|
|
|
return type2 == Float.TYPE; |
|
|
|
if (type != Short.TYPE) |
|
|
|
} else if (type1 == Type.tDouble) { |
|
|
|
return -1; |
|
|
|
return type2 == Double.TYPE; |
|
|
|
break; |
|
|
|
} else if (type1 == Type.tVoid) { |
|
|
|
case 'I': |
|
|
|
return type2 == Void.TYPE; |
|
|
|
if (type != Integer.TYPE) |
|
|
|
} else if (type1 instanceof ArrayType) { |
|
|
|
return -1; |
|
|
|
if (!type2.isArray()) |
|
|
|
break; |
|
|
|
return false; |
|
|
|
case 'J': |
|
|
|
return checkType(((ArrayType)type1).getElementType(), |
|
|
|
if (type != Long.TYPE) |
|
|
|
type2.getComponentType()); |
|
|
|
return -1; |
|
|
|
} else if (type1 instanceof ClassInterfacesType) { |
|
|
|
break; |
|
|
|
return type1.equals(Type.tClass(type2.getName())); |
|
|
|
case 'F': |
|
|
|
} else |
|
|
|
if (type != Float.TYPE) |
|
|
|
return false; |
|
|
|
return -1; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 'D': |
|
|
|
|
|
|
|
if (type != Double.TYPE) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 'V': |
|
|
|
|
|
|
|
if (type != Void.TYPE) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case '[': |
|
|
|
|
|
|
|
if (!type.isArray()) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
pos = checkType(typesig, pos, type.getComponentType()); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 'L': { |
|
|
|
|
|
|
|
int index = typesig.indexOf(';', pos); |
|
|
|
|
|
|
|
if (index == -1) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
if (!type.getName().replace('.','/') |
|
|
|
|
|
|
|
.equals(typesig.substring(pos, index))) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
pos = index+1; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return pos; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static boolean checkMethod(String typesig, |
|
|
|
static boolean checkMethod(MethodType methodType, |
|
|
|
Class[] paramTypes, Class retType) { |
|
|
|
Class[] paramTypes, Class retType) { |
|
|
|
if (typesig.charAt(0) != '(') |
|
|
|
Type[] params = methodType.getParameterTypes(); |
|
|
|
return false; |
|
|
|
if (params.length != paramTypes.length) |
|
|
|
int pos = 1; |
|
|
|
|
|
|
|
int i = 0; |
|
|
|
|
|
|
|
while (typesig.charAt(pos) != ')') { |
|
|
|
|
|
|
|
if (i >= paramTypes.length) |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
pos = checkType(typesig, pos, paramTypes[i++]); |
|
|
|
for (int i=0; i < params.length; i++) { |
|
|
|
if (pos == -1) { |
|
|
|
if (!checkType(params[i], paramTypes[i])) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return checkType(methodType.getReturnType(), retType); |
|
|
|
if (i != paramTypes.length) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
pos++; |
|
|
|
|
|
|
|
pos = checkType(typesig, pos, retType); |
|
|
|
|
|
|
|
return pos == typesig.length(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static Object interpretMethod |
|
|
|
public static Object interpretMethod |
|
|
@ -139,17 +101,7 @@ public class Interpreter implements Opcodes { |
|
|
|
switch (opcode) { |
|
|
|
switch (opcode) { |
|
|
|
case opc_nop: |
|
|
|
case opc_nop: |
|
|
|
break; |
|
|
|
break; |
|
|
|
case opc_aconst_null: |
|
|
|
|
|
|
|
case opc_iconst_m1: |
|
|
|
|
|
|
|
case opc_iconst_0: case opc_iconst_1: case opc_iconst_2: |
|
|
|
|
|
|
|
case opc_iconst_3: case opc_iconst_4: case opc_iconst_5: |
|
|
|
|
|
|
|
case opc_lconst_0: case opc_lconst_1: |
|
|
|
|
|
|
|
case opc_fconst_0: case opc_fconst_1: case opc_fconst_2: |
|
|
|
|
|
|
|
case opc_dconst_0: case opc_dconst_1: |
|
|
|
|
|
|
|
case opc_bipush: |
|
|
|
|
|
|
|
case opc_sipush: |
|
|
|
|
|
|
|
case opc_ldc: |
|
|
|
case opc_ldc: |
|
|
|
case opc_ldc_w: |
|
|
|
|
|
|
|
case opc_ldc2_w: { |
|
|
|
case opc_ldc2_w: { |
|
|
|
stack[stacktop++].setObject(instr.objData); |
|
|
|
stack[stacktop++].setObject(instr.objData); |
|
|
|
break; |
|
|
|
break; |
|
|
@ -600,12 +552,11 @@ public class Interpreter implements Opcodes { |
|
|
|
case opc_invokespecial: |
|
|
|
case opc_invokespecial: |
|
|
|
case opc_invokestatic : |
|
|
|
case opc_invokestatic : |
|
|
|
case opc_invokeinterface: { |
|
|
|
case opc_invokeinterface: { |
|
|
|
String[] ref = (String[]) instr.objData; |
|
|
|
Reference ref = (Reference) instr.objData; |
|
|
|
|
|
|
|
|
|
|
|
if (ref[0].equals(ca.getClazz().getName().replace('.','/'))) { |
|
|
|
if (ref.getClazz().equals(ca.getClazz().getName())) { |
|
|
|
boolean isStatic = opcode == opc_invokestatic; |
|
|
|
MethodType mt = (MethodType) Type.tType(ref.getType()); |
|
|
|
MethodType mt = new MethodType(isStatic, ref[2]); |
|
|
|
BytecodeInfo info = ca.getMethod(ref.getName(), mt) |
|
|
|
BytecodeInfo info = ca.getMethod(ref[1], mt) |
|
|
|
|
|
|
|
.getCode().getBytecodeInfo(); |
|
|
|
.getCode().getBytecodeInfo(); |
|
|
|
Value[] newLocals = new Value[info.getMaxLocals()]; |
|
|
|
Value[] newLocals = new Value[info.getMaxLocals()]; |
|
|
|
for (int i=0; i< newLocals.length; i++) |
|
|
|
for (int i=0; i< newLocals.length; i++) |
|
|
@ -613,8 +564,14 @@ public class Interpreter implements Opcodes { |
|
|
|
Value[] newStack = new Value[info.getMaxStack()]; |
|
|
|
Value[] newStack = new Value[info.getMaxStack()]; |
|
|
|
for (int i=0; i< newStack.length; i++) |
|
|
|
for (int i=0; i< newStack.length; i++) |
|
|
|
newStack[i] = new Value(); |
|
|
|
newStack[i] = new Value(); |
|
|
|
for (int i=mt.getParameterTypes().length - 1; i >= 0; i--) |
|
|
|
int param = mt.getParameterTypes().length; |
|
|
|
newLocals[i].setValue(stack[--stacktop]); |
|
|
|
int slot = 0; |
|
|
|
|
|
|
|
if (opcode != opc_invokestatic) |
|
|
|
|
|
|
|
newLocals[slot++].setValue(stack[stacktop-param-1]); |
|
|
|
|
|
|
|
for (int i = 0; i < param; i++) { |
|
|
|
|
|
|
|
newLocals[slot].setValue(stack[stacktop-param+i]); |
|
|
|
|
|
|
|
slot += mt.getParameterTypes()[i].stackSize(); |
|
|
|
|
|
|
|
} |
|
|
|
Object result = interpretMethod(ca, info, |
|
|
|
Object result = interpretMethod(ca, info, |
|
|
|
newLocals, newStack); |
|
|
|
newLocals, newStack); |
|
|
|
if (mt.getReturnType() != Type.tVoid) |
|
|
|
if (mt.getReturnType() != Type.tVoid) |
|
|
@ -622,41 +579,43 @@ public class Interpreter implements Opcodes { |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Class clazz; |
|
|
|
Class clazz; |
|
|
|
try { |
|
|
|
try { |
|
|
|
clazz = Class.forName(ref[0].replace('/','.')); |
|
|
|
clazz = Class.forName(ref.getClazz()); |
|
|
|
} catch (ClassNotFoundException ex) { |
|
|
|
} catch (ClassNotFoundException ex) { |
|
|
|
throw new InterpreterException |
|
|
|
throw new InterpreterException |
|
|
|
("Class "+ref[0]+" not found"); |
|
|
|
("Class "+ref.getClazz()+" not found"); |
|
|
|
} |
|
|
|
} |
|
|
|
try { |
|
|
|
try { |
|
|
|
if (ref[1].equals("<init>")) { |
|
|
|
if (ref.getName().equals("<init>")) { |
|
|
|
Constructor[] cs = clazz.getConstructors(); |
|
|
|
Constructor[] cs = clazz.getConstructors(); |
|
|
|
Constructor c = null; |
|
|
|
Constructor c = null; |
|
|
|
for (int i=0; i< cs.length; i++) { |
|
|
|
for (int i=0; i< cs.length; i++) { |
|
|
|
if (checkMethod(ref[2], cs[i].getParameterTypes(), |
|
|
|
if (checkMethod((MethodType) |
|
|
|
|
|
|
|
Type.tType(ref.getType()), |
|
|
|
|
|
|
|
cs[i].getParameterTypes(), |
|
|
|
Void.TYPE)) { |
|
|
|
Void.TYPE)) { |
|
|
|
c = cs[i]; |
|
|
|
c = cs[i]; |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (c == null) |
|
|
|
if (c == null) |
|
|
|
throw new InterpreterException("Constructor " |
|
|
|
throw new InterpreterException |
|
|
|
+ref[0]+"." |
|
|
|
("Constructor " + ref + " not found."); |
|
|
|
+ref[1]+" not found."); |
|
|
|
|
|
|
|
Object[] args |
|
|
|
Object[] args |
|
|
|
= new Object[c.getParameterTypes().length]; |
|
|
|
= new Object[c.getParameterTypes().length]; |
|
|
|
for (int i=args.length - 1; i >= 0; i--) |
|
|
|
for (int i=args.length - 1; i >= 0; i--) |
|
|
|
args[i] = stack[--stacktop].objectValue(); |
|
|
|
args[i] = stack[--stacktop].objectValue(); |
|
|
|
NewObject newObj = stack[--stacktop].getNewObject(); |
|
|
|
NewObject newObj = stack[--stacktop].getNewObject(); |
|
|
|
if (!newObj.getType().equals(ref[0])) |
|
|
|
if (!newObj.getType().equals(ref.getClazz())) |
|
|
|
throw new InterpreterException("constructor not called" |
|
|
|
throw new InterpreterException |
|
|
|
+" on new instance"); |
|
|
|
("constructor called on wrong type"); |
|
|
|
newObj.setObject(c.newInstance(args)); |
|
|
|
newObj.setObject(c.newInstance(args)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Method[] ms = clazz.getMethods(); |
|
|
|
Method[] ms = clazz.getMethods(); |
|
|
|
Method m = null; |
|
|
|
Method m = null; |
|
|
|
for (int i=0; i< ms.length; i++) { |
|
|
|
for (int i=0; i< ms.length; i++) { |
|
|
|
if (ms[i].getName().equals(ref[1])) { |
|
|
|
if (ms[i].getName().equals(ref.getName())) { |
|
|
|
if (checkMethod(ref[2], |
|
|
|
if (checkMethod((MethodType) |
|
|
|
|
|
|
|
Type.tType(ref.getType()), |
|
|
|
ms[i].getParameterTypes(), |
|
|
|
ms[i].getParameterTypes(), |
|
|
|
ms[i].getReturnType())) { |
|
|
|
ms[i].getReturnType())) { |
|
|
|
m = ms[i]; |
|
|
|
m = ms[i]; |
|
|
@ -665,8 +624,8 @@ public class Interpreter implements Opcodes { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (m == null) |
|
|
|
if (m == null) |
|
|
|
throw new InterpreterException("Method "+ref[0]+"." |
|
|
|
throw new InterpreterException |
|
|
|
+ref[1]+" not found."); |
|
|
|
("Method " + ref + " not found."); |
|
|
|
Object obj = null; |
|
|
|
Object obj = null; |
|
|
|
Object[] args |
|
|
|
Object[] args |
|
|
|
= new Object[m.getParameterTypes().length]; |
|
|
|
= new Object[m.getParameterTypes().length]; |
|
|
@ -681,13 +640,13 @@ public class Interpreter implements Opcodes { |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (IllegalAccessException ex) { |
|
|
|
} catch (IllegalAccessException ex) { |
|
|
|
throw new InterpreterException |
|
|
|
throw new InterpreterException |
|
|
|
("Method "+ref[0]+"."+ref[1]+" not accessible"); |
|
|
|
("Method " + ref + " not accessible"); |
|
|
|
} catch (InstantiationException ex) { |
|
|
|
} catch (InstantiationException ex) { |
|
|
|
throw new InterpreterException |
|
|
|
throw new InterpreterException |
|
|
|
("InstantiationException in "+ref[0]+"."+ref[1]+"."); |
|
|
|
("InstantiationException in " + ref + "."); |
|
|
|
} catch (InvocationTargetException ex) { |
|
|
|
} catch (InvocationTargetException ex) { |
|
|
|
throw new InterpreterException |
|
|
|
throw new InterpreterException |
|
|
|
("Method "+ref[0]+"."+ref[1]+" throwed an exception"); |
|
|
|
("Method " + ref + " throwed an exception"); |
|
|
|
/*XXX exception handler?*/ |
|
|
|
/*XXX exception handler?*/ |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -758,7 +717,7 @@ public class Interpreter implements Opcodes { |
|
|
|
throw new InterpreterException |
|
|
|
throw new InterpreterException |
|
|
|
("Class "+ex.getMessage()+" not found"); |
|
|
|
("Class "+ex.getMessage()+" not found"); |
|
|
|
} |
|
|
|
} |
|
|
|
Object obj = stack[--stacktop].objectValue(); |
|
|
|
Object obj = stack[stacktop-1].objectValue(); |
|
|
|
if (obj != null && !clazz.isInstance(obj)) { |
|
|
|
if (obj != null && !clazz.isInstance(obj)) { |
|
|
|
/*XXX*/ |
|
|
|
/*XXX*/ |
|
|
|
throw new InterpreterException |
|
|
|
throw new InterpreterException |
|
|
@ -796,7 +755,7 @@ public class Interpreter implements Opcodes { |
|
|
|
} |
|
|
|
} |
|
|
|
int dimension = instr.intData; |
|
|
|
int dimension = instr.intData; |
|
|
|
int[] dims = new int[dimension]; |
|
|
|
int[] dims = new int[dimension]; |
|
|
|
for (int i=dimension-1; i >= 0; i--) |
|
|
|
for (int i=dimension - 1; i >= 0; i--) |
|
|
|
dims[i-1] = stack[--stacktop].intValue(); |
|
|
|
dims[i-1] = stack[--stacktop].intValue(); |
|
|
|
stack[stacktop++].setObject(Array.newInstance(clazz, dims)); |
|
|
|
stack[stacktop++].setObject(Array.newInstance(clazz, dims)); |
|
|
|
break; |
|
|
|
break; |
|
|
|