diff --git a/src/org/jetbrains/eval4j/interpreter.kt b/src/org/jetbrains/eval4j/interpreter.kt index 38734a2e413..6395d1ff5a5 100644 --- a/src/org/jetbrains/eval4j/interpreter.kt +++ b/src/org/jetbrains/eval4j/interpreter.kt @@ -98,7 +98,7 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter( } JSR -> LabelValue((insn as JumpInsnNode).label) GETSTATIC -> eval.getStaticField(FieldDescription(insn as FieldInsnNode)) - NEW -> eval.newInstance(Type.getType((insn as TypeInsnNode).desc)) + NEW -> eval.newInstance(Type.getObjectType((insn as TypeInsnNode).desc)) else -> throw UnsupportedByteCodeException("$insn") } } diff --git a/src/org/jetbrains/eval4j/values.kt b/src/org/jetbrains/eval4j/values.kt index c0f93054736..c2e9e348a9c 100644 --- a/src/org/jetbrains/eval4j/values.kt +++ b/src/org/jetbrains/eval4j/values.kt @@ -30,11 +30,11 @@ class NotInitialized(override val asmType: Type): Value { override fun toString() = "NotInitialized: $asmType" } -abstract class AbstractValue( - public val value: V, +abstract class AbstractValueBase( override val asmType: Type ) : Value { override val valid = true + abstract val value: V override fun toString() = "$value: $asmType" @@ -49,11 +49,19 @@ abstract class AbstractValue( } } +abstract class AbstractValue( + override val value: V, + asmType: Type +) : AbstractValueBase(asmType) + class IntValue(value: Int, asmType: Type): AbstractValue(value, asmType) class LongValue(value: Long): AbstractValue(value, Type.LONG_TYPE) class FloatValue(value: Float): AbstractValue(value, Type.FLOAT_TYPE) class DoubleValue(value: Double): AbstractValue(value, Type.DOUBLE_TYPE) class ObjectValue(value: Any?, asmType: Type): AbstractValue(value, asmType) +class NewObjectValue(val _class: Class, asmType: Type): AbstractValueBase(asmType) { + override var value: Any? = null +} class LabelValue(value: LabelNode): AbstractValue(value, Type.VOID_TYPE) @@ -74,4 +82,12 @@ 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() = (this as AbstractValue<*>).value \ No newline at end of file +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 + } \ No newline at end of file diff --git a/test/org/jetbrains/eval4j/test/TestData.java b/test/org/jetbrains/eval4j/test/TestData.java index 03d662a08c8..88d890325ce 100644 --- a/test/org/jetbrains/eval4j/test/TestData.java +++ b/test/org/jetbrains/eval4j/test/TestData.java @@ -169,6 +169,12 @@ class TestData { static class C { int y = 15; + C(int y) { + this.y = y; + } + + C() {} + int getY() { return y; } @@ -195,4 +201,11 @@ class TestData { return C.newC().getY(); } + static int constructorCallNoArgs() { + return new C().y; + } + + static int constructorCallWithArgs() { + return new C(10).y; + } } diff --git a/test/org/jetbrains/eval4j/test/main.kt b/test/org/jetbrains/eval4j/test/main.kt index 2d4fde5a073..5fce03afe39 100644 --- a/test/org/jetbrains/eval4j/test/main.kt +++ b/test/org/jetbrains/eval4j/test/main.kt @@ -10,6 +10,7 @@ import junit.framework.TestSuite import junit.framework.TestCase import java.lang.reflect.Method import java.lang.reflect.Field +import java.lang.reflect.Constructor fun suite(): TestSuite { val suite = TestSuite() @@ -102,11 +103,14 @@ object REFLECTION_EVAL : Eval { override fun loadClass(classType: Type): Value { throw UnsupportedOperationException() } + override fun loadString(str: String): Value = ObjectValue(str, Type.getType(javaClass())) override fun newInstance(classType: Type): Value { - throw UnsupportedOperationException() + val _class = findClass(classType.getInternalName()) + return NewObjectValue(_class, classType) } + override fun checkCast(value: Value, targetType: Type): Value { throw UnsupportedOperationException() } @@ -155,9 +159,11 @@ object REFLECTION_EVAL : Eval { return objectToValue(result, methodDesc.returnType) } - fun findClass(memberDesc: MemberDescription): Class { - val owner = lookup.findClass(memberDesc.ownerInternalName) - assertNotNull("Class not found: ${memberDesc.ownerInternalName}", owner) + fun findClass(memberDesc: MemberDescription): Class = findClass(memberDesc.ownerInternalName) + + fun findClass(internalName: String): Class { + val owner = lookup.findClass(internalName) + assertNotNull("Class not found: ${internalName}", owner) return owner!! } @@ -185,7 +191,22 @@ object REFLECTION_EVAL : Eval { } override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List, invokespecial: Boolean): Value { - // TODO: INVOKESPECIAL + if (invokespecial) { + if (methodDesc.name == "") { + // Constructor call + val _class = (instance as NewObjectValue)._class + val ctor = _class.findConstructor(methodDesc) + assertNotNull("Constructor not found: $methodDesc", ctor) + val args = arguments.map { v -> v.obj }.copyToArray() + val result = ctor!!.newInstance(*args) + instance.value = result + return objectToValue(result, instance.asmType) + } + else { + // TODO + throw UnsupportedOperationException("invokespecial is not suported yet") + } + } val obj = instance.obj val method = obj.javaClass.findMethod(methodDesc) assertNotNull("Method not found: $methodDesc", method) @@ -216,6 +237,24 @@ fun Class.findMethod(methodDesc: MethodDescription): Method? { return null } +[suppress("UNCHECKED_CAST")] +fun Class.findConstructor(methodDesc: MethodDescription): Constructor? { + for (declared in getDeclaredConstructors()) { + if (methodDesc.matches(declared)) return declared as Constructor + } + return null +} + +fun MethodDescription.matches(ctor: Constructor<*>): Boolean { + val methodParams = ctor.getParameterTypes()!! + if (parameterTypes.size() != methodParams.size) return false + for ((i, p) in parameterTypes.withIndices()) { + if (!p.matches(methodParams[i])) return false + } + + return true; +} + fun MethodDescription.matches(method: Method): Boolean { if (name != method.getName()) return false @@ -234,8 +273,11 @@ fun Class.findField(fieldDesc: FieldDescription): Field? { if (fieldDesc.matches(declared)) return declared } - val fromSuperClass = (getSuperclass() as Class).findField(fieldDesc) - if (fromSuperClass != null) return fromSuperClass + val superclass = getSuperclass() + if (superclass != null) { + val fromSuperClass = (superclass as Class).findField(fieldDesc) + if (fromSuperClass != null) return fromSuperClass + } for (supertype in getInterfaces()) { val fromSuper = (supertype as Class).findField(fieldDesc)