Cast method arguments to method parameter types
This commit is contained in:
@@ -173,7 +173,7 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
||||
CHECKCAST -> {
|
||||
val targetType = Type.getObjectType((insn as TypeInsnNode).desc)
|
||||
if (eval.isInstanceOf(value, targetType)) {
|
||||
ObjectValue(value.obj, targetType)
|
||||
ObjectValue(value.obj(), targetType)
|
||||
}
|
||||
else {
|
||||
throwEvalException(ClassCastException("Value '$value' cannot be cast to $targetType"))
|
||||
@@ -200,8 +200,8 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
||||
IFGT -> value.int > 0
|
||||
IFLE -> value.int <= 0
|
||||
IFGE -> value.int >= 0
|
||||
IFNULL -> value.obj == null
|
||||
IFNONNULL -> value.obj != null
|
||||
IFNULL -> value.obj() == null
|
||||
IFNONNULL -> value.obj() != null
|
||||
else -> throw UnsupportedByteCodeException("Unknown opcode: $opcode")
|
||||
}
|
||||
}
|
||||
@@ -310,8 +310,8 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
||||
IF_ICMPLE -> value1.int <= value2.int
|
||||
IF_ICMPGE -> value1.int >= value2.int
|
||||
|
||||
IF_ACMPEQ -> value1.obj == value2.obj
|
||||
IF_ACMPNE -> value1.obj != value2.obj
|
||||
IF_ACMPEQ -> value1.obj() == value2.obj()
|
||||
IF_ACMPNE -> value1.obj() != value2.obj()
|
||||
else -> throw UnsupportedByteCodeException("Unknown opcode: $opcode")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ fun interpreterLoop(
|
||||
val expectedType = Type.getReturnType(m.desc)
|
||||
if (expectedType.getSort() == Type.OBJECT) {
|
||||
val coerced = if (value != NULL_VALUE && value.asmType != expectedType)
|
||||
ObjectValue(value.obj, expectedType)
|
||||
ObjectValue(value.obj(), expectedType)
|
||||
else value
|
||||
return ValueReturned(coerced)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class JDIEval(
|
||||
}
|
||||
|
||||
override fun setArrayElement(array: Value, index: Value, newValue: Value) {
|
||||
array.array().setValue(index.int, newValue.asJdiValue(vm))
|
||||
array.array().setValue(index.int, newValue.asJdiValue(vm, array.asmType.arrayElementType))
|
||||
}
|
||||
|
||||
private fun findField(fieldDesc: FieldDescription): jdi.Field {
|
||||
@@ -131,7 +131,7 @@ class JDIEval(
|
||||
throwEvalException(NoSuchFieldError("Can't a field in a non-class: $field"))
|
||||
}
|
||||
|
||||
val jdiValue = newValue.asJdiValue(vm)
|
||||
val jdiValue = newValue.asJdiValue(vm, field.`type`().asType())
|
||||
mayThrow { _class.setValue(field, jdiValue) }
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ class JDIEval(
|
||||
val _class = method.declaringType()
|
||||
if (_class !is jdi.ClassType) throwEvalException(NoSuchMethodError("Static method is a non-class type: $method"))
|
||||
|
||||
val args = arguments.map { v -> v.asJdiValue(vm) }
|
||||
val args = mapArguments(arguments, method.argumentTypes())
|
||||
val result = mayThrow { _class.invokeMethod(thread, method, args, 0) }
|
||||
return result.asValue()
|
||||
}
|
||||
@@ -174,7 +174,7 @@ class JDIEval(
|
||||
val field = findField(fieldDesc)
|
||||
val obj = instance.jdiObj.checkNull()
|
||||
|
||||
val jdiValue = newValue.asJdiValue(vm)
|
||||
val jdiValue = newValue.asJdiValue(vm, field.`type`().asType())
|
||||
mayThrow { obj.setValue(field, jdiValue) }
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ class JDIEval(
|
||||
// Constructor call
|
||||
val ctor = findMethod(methodDesc)
|
||||
val _class = (instance as NewObjectValue).asmType.asReferenceType() as jdi.ClassType
|
||||
val args = arguments.map { v -> v.asJdiValue(vm) }
|
||||
val args = mapArguments(arguments, ctor.argumentTypes())
|
||||
val result = mayThrow { _class.newInstance(thread, ctor, args, 0) }
|
||||
instance.value = result
|
||||
return result.asValue()
|
||||
@@ -197,10 +197,17 @@ class JDIEval(
|
||||
val method = findMethod(methodDesc)
|
||||
|
||||
val obj = instance.jdiObj.checkNull()
|
||||
val args = arguments.map { v -> v.asJdiValue(vm) }
|
||||
val args = mapArguments(arguments, method.argumentTypes())
|
||||
val result = mayThrow { obj.invokeMethod(thread, method, args, 0) }
|
||||
return result.asValue()
|
||||
}
|
||||
|
||||
private fun mapArguments(arguments: List<Value>, expecetedTypes: List<jdi.Type>): List<jdi.Value?> {
|
||||
return arguments.zip(expecetedTypes).map {
|
||||
val (arg, expectedType) = it
|
||||
arg.asJdiValue(vm, expectedType.asType())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> mayThrow(f: () -> T): T {
|
||||
|
||||
@@ -52,16 +52,16 @@ fun jdi.Value?.asValue(): Value {
|
||||
fun jdi.Type.asType(): Type = Type.getType(this.signature())
|
||||
|
||||
val Value.jdiObj: jdi.ObjectReference?
|
||||
get() = this.obj as jdi.ObjectReference?
|
||||
get() = this.obj() as jdi.ObjectReference?
|
||||
|
||||
val Value.jdiClass: jdi.ClassObjectReference?
|
||||
get() = this.jdiObj as jdi.ClassObjectReference?
|
||||
|
||||
fun Value.asJdiValue(vm: jdi.VirtualMachine): jdi.Value? {
|
||||
fun Value.asJdiValue(vm: jdi.VirtualMachine, expectedType: Type): jdi.Value? {
|
||||
return when (this) {
|
||||
NULL_VALUE -> null
|
||||
VOID_VALUE -> vm.mirrorOfVoid()
|
||||
is IntValue -> when (asmType) {
|
||||
is IntValue -> when (expectedType) {
|
||||
Type.BOOLEAN_TYPE -> vm.mirrorOf(boolean)
|
||||
Type.BYTE_TYPE -> vm.mirrorOf(int.toByte())
|
||||
Type.SHORT_TYPE -> vm.mirrorOf(int.toShort())
|
||||
@@ -73,7 +73,7 @@ fun Value.asJdiValue(vm: jdi.VirtualMachine): jdi.Value? {
|
||||
is FloatValue -> vm.mirrorOf(value)
|
||||
is DoubleValue -> vm.mirrorOf(value)
|
||||
is ObjectValue -> value as jdi.ObjectReference
|
||||
is NewObjectValue -> this.obj as jdi.ObjectReference
|
||||
is NewObjectValue -> this.obj() as jdi.ObjectReference
|
||||
else -> throw JDIFailureException("Unknown value: $this")
|
||||
}
|
||||
}
|
||||
@@ -89,15 +89,21 @@ val Value.int: Int get() = (this as IntValue).value
|
||||
val Value.long: Long get() = (this as LongValue).value
|
||||
val Value.float: Float get() = (this as FloatValue).value
|
||||
val Value.double: Double get() = (this as DoubleValue).value
|
||||
val Value.obj: Any?
|
||||
get(): Any? {
|
||||
if (this is NewObjectValue) {
|
||||
val v = value
|
||||
if (v == null) throw IllegalStateException("Trying to access an unitialized object: $this")
|
||||
return v
|
||||
}
|
||||
return (this as AbstractValue<*>).value
|
||||
fun Value.obj(expectedType: Type = asmType): Any? {
|
||||
if (this is NewObjectValue) {
|
||||
val v = value
|
||||
if (v == null) throw IllegalStateException("Trying to access an unitialized object: $this")
|
||||
return v
|
||||
}
|
||||
return when {
|
||||
expectedType == asmType -> (this as AbstractValue<*>).value
|
||||
expectedType == Type.BOOLEAN_TYPE -> this.boolean
|
||||
expectedType == Type.SHORT_TYPE -> (this as IntValue).int.toShort()
|
||||
expectedType == Type.BYTE_TYPE -> (this as IntValue).int.toByte()
|
||||
expectedType == Type.CHAR_TYPE -> (this as IntValue).int.toChar()
|
||||
else -> (this as AbstractValue<*>).value
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Any> T?.checkNull(): T {
|
||||
if (this == null) {
|
||||
|
||||
@@ -115,7 +115,7 @@ fun suite(): TestSuite {
|
||||
}
|
||||
|
||||
if (expected is ValueReturned && value is ValueReturned && value.result is ObjectValue) {
|
||||
assertEquals(expected.result.obj.toString(), value.result.jdiObj.callToString())
|
||||
assertEquals(expected.result.obj().toString(), value.result.jdiObj.callToString())
|
||||
}
|
||||
else {
|
||||
assertEquals(expected, value)
|
||||
|
||||
@@ -48,6 +48,91 @@ class TestData {
|
||||
return "str";
|
||||
}
|
||||
|
||||
static Integer integerValueOf() { return 1; }
|
||||
|
||||
static Byte byteValueOf() { return 1; }
|
||||
|
||||
static Short shortValueOf() { return 1; }
|
||||
|
||||
static Long longValueOf() { return 1L; }
|
||||
|
||||
static Float floatValueOf() { return 1.0f; }
|
||||
|
||||
static Double doubleValueOf() { return 1.0; }
|
||||
|
||||
static Character charValueOf() { return 1; }
|
||||
|
||||
static Boolean booleanValueOf() { return true; }
|
||||
|
||||
static void castFieldTypes() {
|
||||
CastFieldType.i = 1;
|
||||
CastFieldType.b = 1;
|
||||
CastFieldType.s = 1;
|
||||
CastFieldType.c = 1;
|
||||
CastFieldType.bool = true;
|
||||
CastFieldType.l = 1;
|
||||
CastFieldType.f = 1.0f;
|
||||
CastFieldType.d = 1.0;
|
||||
|
||||
CastFieldType klass = new CastFieldType();
|
||||
klass.im = 1;
|
||||
klass.bm = 1;
|
||||
klass.sm = 1;
|
||||
klass.cm = 1;
|
||||
klass.boolm = true;
|
||||
klass.lm = 1;
|
||||
klass.fm = 1;
|
||||
klass.dm = 1;
|
||||
|
||||
Integer i = klass.im;
|
||||
Byte b = klass.bm;
|
||||
Short s = klass.sm;
|
||||
Character c = klass.cm;
|
||||
Boolean bool = klass.boolm;
|
||||
Long l = klass.lm;
|
||||
Float f = klass.fm;
|
||||
Double d = klass.dm;
|
||||
}
|
||||
|
||||
static class CastFieldType {
|
||||
static int i = 1;
|
||||
static byte b = 1;
|
||||
static short s = 1;
|
||||
static char c = 1;
|
||||
static boolean bool = true;
|
||||
static long l = 1;
|
||||
static float f = 1;
|
||||
static double d = 1;
|
||||
|
||||
int im = 1;
|
||||
byte bm = 1;
|
||||
short sm = 1;
|
||||
char cm = 1;
|
||||
boolean boolm = true;
|
||||
long lm = 1;
|
||||
float fm = 1;
|
||||
double dm = 1;
|
||||
}
|
||||
|
||||
static void castArrayElementType() {
|
||||
int[] i = new int[1];
|
||||
i[0] = 1;
|
||||
short[] s = new short[1];
|
||||
s[0] = 1;
|
||||
byte[] b = new byte[1];
|
||||
b[0] = 1;
|
||||
char[] c = new char[1];
|
||||
c[0] = 1;
|
||||
boolean[] bool = new boolean[1];
|
||||
bool[0] = true;
|
||||
long[] l = new long[1];
|
||||
l[0] = 1;
|
||||
float[] f = new float[1];
|
||||
f[0] = 1;
|
||||
double[] d = new double[1];
|
||||
d[0] = 1;
|
||||
}
|
||||
|
||||
static int variable() {
|
||||
int i = 153;
|
||||
return i;
|
||||
|
||||
@@ -96,7 +96,7 @@ object REFLECTION_EVAL : Eval {
|
||||
|
||||
override fun isInstanceOf(value: Value, targetType: Type): Boolean {
|
||||
val _class = findClass(targetType)
|
||||
return _class.isInstance(value.obj)
|
||||
return _class.isInstance(value.obj())
|
||||
}
|
||||
|
||||
[suppress("UNCHECKED_CAST")]
|
||||
@@ -109,13 +109,13 @@ object REFLECTION_EVAL : Eval {
|
||||
}
|
||||
|
||||
override fun getArrayLength(array: Value): Value {
|
||||
return int(JArray.getLength(array.obj.checkNull()))
|
||||
return int(JArray.getLength(array.obj().checkNull()))
|
||||
}
|
||||
|
||||
override fun getArrayElement(array: Value, index: Value): Value {
|
||||
val asmType = array.asmType
|
||||
val elementType = if (asmType.getDimensions() == 1) asmType.getElementType() else Type.getType(asmType.getDescriptor().substring(1))
|
||||
val arr = array.obj.checkNull()
|
||||
val arr = array.obj().checkNull()
|
||||
val ind = index.int
|
||||
return when (elementType.getSort()) {
|
||||
Type.BOOLEAN -> boolean(JArray.getBoolean(arr, ind))
|
||||
@@ -136,10 +136,10 @@ object REFLECTION_EVAL : Eval {
|
||||
}
|
||||
|
||||
override fun setArrayElement(array: Value, index: Value, newValue: Value) {
|
||||
val arr = array.obj.checkNull()
|
||||
val arr = array.obj().checkNull()
|
||||
val ind = index.int
|
||||
if (array.asmType.getDimensions() > 1) {
|
||||
JArray.set(arr, ind, newValue.obj)
|
||||
JArray.set(arr, ind, newValue.obj())
|
||||
return
|
||||
}
|
||||
val elementType = array.asmType.getElementType()
|
||||
@@ -154,7 +154,7 @@ object REFLECTION_EVAL : Eval {
|
||||
Type.DOUBLE -> JArray.setDouble(arr, ind, newValue.double)
|
||||
Type.OBJECT,
|
||||
Type.ARRAY -> {
|
||||
JArray.set(arr, ind, newValue.obj)
|
||||
JArray.set(arr, ind, newValue.obj())
|
||||
}
|
||||
else -> throw UnsupportedOperationException("Unsupported array element type: $elementType")
|
||||
}
|
||||
@@ -183,7 +183,7 @@ object REFLECTION_EVAL : Eval {
|
||||
|
||||
override fun setStaticField(fieldDesc: FieldDescription, newValue: Value) {
|
||||
val field = findStaticField(fieldDesc)
|
||||
val obj = newValue.obj
|
||||
val obj = newValue.obj(fieldDesc.fieldType)
|
||||
mayThrow {field.set(null, obj)}
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ object REFLECTION_EVAL : Eval {
|
||||
assertTrue(methodDesc.isStatic)
|
||||
val method = findClass(methodDesc).findMethod(methodDesc)
|
||||
assertNotNull("Method not found: $methodDesc", method)
|
||||
val args = arguments.map { v -> v.obj }.copyToArray()
|
||||
val args = mapArguments(arguments, methodDesc.parameterTypes).copyToArray()
|
||||
val result = mayThrow {method!!.invoke(null, *args)}
|
||||
return objectToValue(result, methodDesc.returnType)
|
||||
}
|
||||
@@ -213,17 +213,17 @@ object REFLECTION_EVAL : Eval {
|
||||
}
|
||||
|
||||
override fun getField(instance: Value, fieldDesc: FieldDescription): Value {
|
||||
val obj = instance.obj.checkNull()
|
||||
val obj = instance.obj().checkNull()
|
||||
val field = findInstanceField(obj, fieldDesc)
|
||||
|
||||
return objectToValue(mayThrow {field.get(obj)}, fieldDesc.fieldType)
|
||||
}
|
||||
|
||||
override fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value) {
|
||||
val obj = instance.obj.checkNull()
|
||||
val obj = instance.obj().checkNull()
|
||||
val field = findInstanceField(obj, fieldDesc)
|
||||
|
||||
val newObj = newValue.obj
|
||||
val newObj = newValue.obj(fieldDesc.fieldType)
|
||||
mayThrow {field.set(obj, newObj)}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ object REFLECTION_EVAL : Eval {
|
||||
val _class = findClass((instance as NewObjectValue).asmType)
|
||||
val ctor = _class.findConstructor(methodDesc)
|
||||
assertNotNull("Constructor not found: $methodDesc", ctor)
|
||||
val args = arguments.map { v -> v.obj }.copyToArray()
|
||||
val args = mapArguments(arguments, methodDesc.parameterTypes).copyToArray()
|
||||
val result = mayThrow {ctor!!.newInstance(*args)}
|
||||
instance.value = result
|
||||
return objectToValue(result, instance.asmType)
|
||||
@@ -252,13 +252,20 @@ object REFLECTION_EVAL : Eval {
|
||||
throw UnsupportedOperationException("invokespecial is not suported yet")
|
||||
}
|
||||
}
|
||||
val obj = instance.obj.checkNull()
|
||||
val obj = instance.obj().checkNull()
|
||||
val method = obj.javaClass.findMethod(methodDesc)
|
||||
assertNotNull("Method not found: $methodDesc", method)
|
||||
val args = arguments.map { v -> v.obj }.copyToArray()
|
||||
val args = mapArguments(arguments, methodDesc.parameterTypes).copyToArray()
|
||||
val result = mayThrow {method!!.invoke(obj, *args)}
|
||||
return objectToValue(result, methodDesc.returnType)
|
||||
}
|
||||
|
||||
private fun mapArguments(arguments: List<Value>, expecetedTypes: List<Type>): List<Any?> {
|
||||
return arguments.zip(expecetedTypes).map {
|
||||
val (arg, expectedType) = it
|
||||
arg.obj(expectedType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReflectionLookup(val classLoader: ClassLoader) {
|
||||
|
||||
Reference in New Issue
Block a user