diff --git a/eval4j/src/org/jetbrains/eval4j/interpreter.kt b/eval4j/src/org/jetbrains/eval4j/interpreter.kt index f2954dd50de..01d65b765f4 100644 --- a/eval4j/src/org/jetbrains/eval4j/interpreter.kt +++ b/eval4j/src/org/jetbrains/eval4j/interpreter.kt @@ -173,7 +173,7 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter( 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( 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( 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") } } diff --git a/eval4j/src/org/jetbrains/eval4j/interpreterLoop.kt b/eval4j/src/org/jetbrains/eval4j/interpreterLoop.kt index 2abcb795094..8dc1eb52925 100644 --- a/eval4j/src/org/jetbrains/eval4j/interpreterLoop.kt +++ b/eval4j/src/org/jetbrains/eval4j/interpreterLoop.kt @@ -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) } diff --git a/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt b/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt index 4a81803a099..93a473ac7fe 100644 --- a/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt +++ b/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt @@ -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, expecetedTypes: List): List { + return arguments.zip(expecetedTypes).map { + val (arg, expectedType) = it + arg.asJdiValue(vm, expectedType.asType()) + } + } } fun mayThrow(f: () -> T): T { diff --git a/eval4j/src/org/jetbrains/eval4j/jdi/jdiValues.kt b/eval4j/src/org/jetbrains/eval4j/jdi/jdiValues.kt index 76e1c272495..b409366ee87 100644 --- a/eval4j/src/org/jetbrains/eval4j/jdi/jdiValues.kt +++ b/eval4j/src/org/jetbrains/eval4j/jdi/jdiValues.kt @@ -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") } } \ No newline at end of file diff --git a/eval4j/src/org/jetbrains/eval4j/values.kt b/eval4j/src/org/jetbrains/eval4j/values.kt index 49438ca315f..d81d313cfab 100644 --- a/eval4j/src/org/jetbrains/eval4j/values.kt +++ b/eval4j/src/org/jetbrains/eval4j/values.kt @@ -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?.checkNull(): T { if (this == null) { diff --git a/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt b/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt index 7b473e1a7d0..f2252a8d8fe 100644 --- a/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt +++ b/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt @@ -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) diff --git a/eval4j/test/org/jetbrains/eval4j/test/TestData.java b/eval4j/test/org/jetbrains/eval4j/test/TestData.java index 83889be679b..5a22a6d7523 100644 --- a/eval4j/test/org/jetbrains/eval4j/test/TestData.java +++ b/eval4j/test/org/jetbrains/eval4j/test/TestData.java @@ -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; diff --git a/eval4j/test/org/jetbrains/eval4j/test/main.kt b/eval4j/test/org/jetbrains/eval4j/test/main.kt index ef23b6c6445..aed54cb0516 100644 --- a/eval4j/test/org/jetbrains/eval4j/test/main.kt +++ b/eval4j/test/org/jetbrains/eval4j/test/main.kt @@ -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, expecetedTypes: List): List { + return arguments.zip(expecetedTypes).map { + val (arg, expectedType) = it + arg.obj(expectedType) + } + } } class ReflectionLookup(val classLoader: ClassLoader) {