From 1dfbbb9f084dbb7c1b1fa07c5157d41159f2383e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Mon, 7 Oct 2013 00:32:34 +0400 Subject: [PATCH] Support NPE and exceptions thrown from methods being called --- src/org/jetbrains/eval4j/values.kt | 2 +- test/org/jetbrains/eval4j/test/TestData.java | 25 +++++++ test/org/jetbrains/eval4j/test/main.kt | 78 +++++++++++++------- 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/src/org/jetbrains/eval4j/values.kt b/src/org/jetbrains/eval4j/values.kt index c2e9e348a9c..0974356d0d3 100644 --- a/src/org/jetbrains/eval4j/values.kt +++ b/src/org/jetbrains/eval4j/values.kt @@ -59,7 +59,7 @@ 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) { +class NewObjectValue(val _class: Class<*>, asmType: Type): AbstractValueBase(asmType) { override var value: Any? = null } diff --git a/test/org/jetbrains/eval4j/test/TestData.java b/test/org/jetbrains/eval4j/test/TestData.java index db22febb1bc..239df0dc5e4 100644 --- a/test/org/jetbrains/eval4j/test/TestData.java +++ b/test/org/jetbrains/eval4j/test/TestData.java @@ -182,6 +182,10 @@ class TestData { static C newC() { return new C(); } + + static void throwException() { + throw new RuntimeException(); + } } static int getInstanceField() { @@ -252,4 +256,25 @@ class TestData { static String classLiteral() { return String.class.toString(); } + + static int callThrowingMethod() { + try { + C.throwException(); + } + catch (RuntimeException e) { + return 1; + } + return 0; + } + + static int NPE() { + try { + Object x = null; + x.toString(); + } + catch (NullPointerException e) { + return 1; + } + return 0; + } } diff --git a/test/org/jetbrains/eval4j/test/main.kt b/test/org/jetbrains/eval4j/test/main.kt index 32010f02372..9485f0bd30a 100644 --- a/test/org/jetbrains/eval4j/test/main.kt +++ b/test/org/jetbrains/eval4j/test/main.kt @@ -11,6 +11,7 @@ import junit.framework.TestCase import java.lang.reflect.Method import java.lang.reflect.Field import java.lang.reflect.Constructor +import java.lang.reflect.InvocationTargetException fun suite(): TestSuite { val suite = TestSuite() @@ -132,13 +133,38 @@ object REFLECTION_EVAL : Eval { throw UnsupportedOperationException() } + fun mayThrow(f: () -> T): T { + try { + try { + return f() + } + catch (ite: InvocationTargetException) { + throw ite.getCause() ?: ite + } + } + catch (e: Throwable) { + throw ThrownFromEvalException(ObjectValue(e, Type.getType(e.javaClass))) + } + } + + fun Any?.checkNull(): Any { + if (this == null) { + throw ThrownFromEvalException(ObjectValue(NullPointerException(), Type.getType(javaClass()))) + } + return this + } + override fun getStaticField(fieldDesc: FieldDescription): Value { - val result = findStaticField(fieldDesc).get(null) + val field = findStaticField(fieldDesc) + + val result = mayThrow {field.get(null)} return objectToValue(result, fieldDesc.fieldType) } override fun setStaticField(fieldDesc: FieldDescription, newValue: Value) { - findStaticField(fieldDesc).set(null, newValue.obj) + val field = findStaticField(fieldDesc) + val obj = newValue.obj + mayThrow {field.set(null, obj)} } fun findStaticField(fieldDesc: FieldDescription): Field { @@ -153,36 +179,36 @@ object REFLECTION_EVAL : Eval { assertTrue(methodDesc.isStatic) val method = findClass(methodDesc).findMethod(methodDesc) assertNotNull("Method not found: $methodDesc", method) - val result = method!!.invoke(null, *arguments.map { v -> v.obj }.copyToArray()) + val args = arguments.map { v -> v.obj }.copyToArray() + val result = mayThrow {method!!.invoke(null, *args)} return objectToValue(result, methodDesc.returnType) } - fun findClass(memberDesc: MemberDescription): Class = findClass(memberDesc.ownerInternalName) + fun findClass(memberDesc: MemberDescription): Class = findClass(memberDesc.ownerInternalName) - fun findClass(internalName: String): Class { + fun findClass(internalName: String): Class { val owner = lookup.findClass(internalName) assertNotNull("Class not found: ${internalName}", owner) return owner!! } override fun getField(instance: Value, fieldDesc: FieldDescription): Value { - val obj = instance.obj + val obj = instance.obj.checkNull() val field = findInstanceField(obj, fieldDesc) - return objectToValue(field.get(obj), fieldDesc.fieldType) + return objectToValue(mayThrow {field.get(obj)}, fieldDesc.fieldType) } override fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value) { - val obj = instance.obj + val obj = instance.obj.checkNull() val field = findInstanceField(obj, fieldDesc) - field.set(obj, newValue.obj) + val newObj = newValue.obj + mayThrow {field.set(obj, newObj)} } - fun findInstanceField(obj: Any?, fieldDesc: FieldDescription): Field { - if (obj == null) throw NullPointerException() - - val _class: Class = obj.javaClass + fun findInstanceField(obj: Any, fieldDesc: FieldDescription): Field { + val _class = obj.javaClass val field = _class.findField(fieldDesc) assertNotNull("Field not found: $fieldDesc", field) return field!! @@ -192,11 +218,12 @@ object REFLECTION_EVAL : Eval { if (invokespecial) { if (methodDesc.name == "") { // Constructor call - val _class = (instance as NewObjectValue)._class + [suppress("UNCHECKED_CAST")] + val _class = (instance as NewObjectValue)._class as 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) + val result = mayThrow {ctor!!.newInstance(*args)} instance.value = result return objectToValue(result, instance.asmType) } @@ -205,30 +232,31 @@ object REFLECTION_EVAL : Eval { throw UnsupportedOperationException("invokespecial is not suported yet") } } - val obj = instance.obj + val obj = instance.obj.checkNull() val method = obj.javaClass.findMethod(methodDesc) assertNotNull("Method not found: $methodDesc", method) - val result = method!!.invoke(obj, *arguments.map { v -> v.obj }.copyToArray()) + val args = arguments.map { v -> v.obj }.copyToArray() + val result = mayThrow {method!!.invoke(obj, *args)} return objectToValue(result, methodDesc.returnType) } } class ReflectionLookup(val classLoader: ClassLoader) { [suppress("UNCHECKED_CAST")] - fun findClass(internalName: String): Class? = classLoader.loadClass(internalName.replace('/', '.')) as Class + fun findClass(internalName: String): Class? = classLoader.loadClass(internalName.replace('/', '.')) as Class } [suppress("UNCHECKED_CAST")] -fun Class.findMethod(methodDesc: MethodDescription): Method? { +fun Class.findMethod(methodDesc: MethodDescription): Method? { for (declared in getDeclaredMethods()) { if (methodDesc.matches(declared)) return declared } - val fromSuperClass = (getSuperclass() as Class).findMethod(methodDesc) + val fromSuperClass = (getSuperclass() as Class).findMethod(methodDesc) if (fromSuperClass != null) return fromSuperClass for (supertype in getInterfaces()) { - val fromSuper = (supertype as Class).findMethod(methodDesc) + val fromSuper = (supertype as Class).findMethod(methodDesc) if (fromSuper != null) return fromSuper } @@ -236,7 +264,7 @@ fun Class.findMethod(methodDesc: MethodDescription): Method? { } [suppress("UNCHECKED_CAST")] -fun Class.findConstructor(methodDesc: MethodDescription): Constructor? { +fun Class.findConstructor(methodDesc: MethodDescription): Constructor? { for (declared in getDeclaredConstructors()) { if (methodDesc.matches(declared)) return declared as Constructor } @@ -266,19 +294,19 @@ fun MethodDescription.matches(method: Method): Boolean { } [suppress("UNCHECKED_CAST")] -fun Class.findField(fieldDesc: FieldDescription): Field? { +fun Class.findField(fieldDesc: FieldDescription): Field? { for (declared in getDeclaredFields()) { if (fieldDesc.matches(declared)) return declared } val superclass = getSuperclass() if (superclass != null) { - val fromSuperClass = (superclass as Class).findField(fieldDesc) + val fromSuperClass = (superclass as Class).findField(fieldDesc) if (fromSuperClass != null) return fromSuperClass } for (supertype in getInterfaces()) { - val fromSuper = (supertype as Class).findField(fieldDesc) + val fromSuper = (supertype as Class).findField(fieldDesc) if (fromSuper != null) return fromSuper }