From 58ad84d4e92aabafbf3e0a57b55f1a45eb42b0e7 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Mon, 7 Oct 2013 00:16:09 +0400 Subject: [PATCH] Support try..catch and instanceof/casts --- .../org/objectweb/asm/tree/annotations.xml | 9 ++++ src/org/jetbrains/eval4j/interpreter.kt | 15 +++++-- src/org/jetbrains/eval4j/interpreterLoop.kt | 44 +++++++++++++++++-- test/org/jetbrains/eval4j/test/TestData.java | 40 +++++++++++++++++ test/org/jetbrains/eval4j/test/main.kt | 8 ++-- 5 files changed, 103 insertions(+), 13 deletions(-) diff --git a/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml b/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml index 7866c5dc168..d2f989a9d19 100644 --- a/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml +++ b/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml @@ -386,6 +386,9 @@ + + + @@ -551,6 +554,12 @@ + + + + + + diff --git a/src/org/jetbrains/eval4j/interpreter.kt b/src/org/jetbrains/eval4j/interpreter.kt index 6395d1ff5a5..0d249e2edb8 100644 --- a/src/org/jetbrains/eval4j/interpreter.kt +++ b/src/org/jetbrains/eval4j/interpreter.kt @@ -20,8 +20,7 @@ trait Eval { fun loadClass(classType: Type): Value fun loadString(str: String): Value fun newInstance(classType: Type): Value - fun checkCast(value: Value, targetType: Type): Value - fun isInsetanceOf(value: Value, targetType: Type): Boolean + fun isInstanceOf(value: Value, targetType: Type): Boolean fun newArray(arrayType: Type, size: Int): Value fun newMultiDimensionalArray(arrayType: Type, dimensionSizes: List): Value @@ -176,12 +175,20 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter( CHECKCAST -> { val targetType = Type.getObjectType((insn as TypeInsnNode).desc) - eval.checkCast(value, targetType) + if (eval.isInstanceOf(value, targetType)) { + ObjectValue(value.obj, targetType) + } + else { + throw ThrownFromEvalException(ObjectValue( + ClassCastException("Value '$value' cannot be cast to $targetType"), + Type.getType(javaClass()) + )) + } } INSTANCEOF -> { val targetType = Type.getObjectType((insn as TypeInsnNode).desc) - boolean(eval.isInsetanceOf(value, targetType)) + boolean(eval.isInstanceOf(value, targetType)) } // TODO: maybe just do nothing? diff --git a/src/org/jetbrains/eval4j/interpreterLoop.kt b/src/org/jetbrains/eval4j/interpreterLoop.kt index 6762f9c4501..e0656f436ef 100644 --- a/src/org/jetbrains/eval4j/interpreterLoop.kt +++ b/src/org/jetbrains/eval4j/interpreterLoop.kt @@ -9,6 +9,8 @@ import org.objectweb.asm.tree.analysis.Interpreter import org.objectweb.asm.tree.JumpInsnNode import org.objectweb.asm.tree.VarInsnNode import org.objectweb.asm.util.Printer +import org.objectweb.asm.tree.TryCatchBlockNode +import java.util.ArrayList trait InterpreterResult { fun toString(): String @@ -62,11 +64,27 @@ fun interpreterLoop( } val interpreter = SingleInstructionInterpreter(eval) - var frame = initFrame(ownerClassInternalName, m, interpreter) + val frame = initFrame(ownerClassInternalName, m, interpreter) + val handlers = computeHandlers(m) + + fun exceptionCaught(exceptionValue: Value): Boolean { + val catchBlocks = handlers[m.instructions.indexOf(currentInsn)] ?: listOf() + for (catch in catchBlocks) { + val exceptionTypeDesc = catch.`type` + if (exceptionTypeDesc != null) { + val exceptionType = Type.getType(exceptionTypeDesc) + if (eval.isInstanceOf(exceptionValue, exceptionType)) { + frame.clearStack() + frame.push(exceptionValue) + goto(catch.handler) + return true + } + } + } + return false + } while (true) { - // TODO try-catch-finally support - val insnOpcode = currentInsn.getOpcode() val insnType = currentInsn.getType() @@ -134,11 +152,11 @@ fun interpreterLoop( } } - // TODO: try/catch/finally ATHROW -> { val exceptionValue = frame.getStack(0)!! val handled = handler.exceptionThrown(frame, currentInsn, exceptionValue) if (handled != null) return handled + if (exceptionCaught(exceptionValue)) continue return ExceptionThrown(exceptionValue) } @@ -153,6 +171,7 @@ fun interpreterLoop( // TODO: try/catch.finaly val handled = handler.exceptionThrown(frame, currentInsn, e.exception) if (handled != null) return handled + if (exceptionCaught(e.exception)) continue return ExceptionThrown(e.exception) } } @@ -193,4 +212,21 @@ fun initFrame( } return current +} + +fun computeHandlers(m: MethodNode): Array?> { + val insns = m.instructions + val handlers = Array?>(insns.size()) {null} + for (tcb in m.tryCatchBlocks) { + val begin = insns.indexOf(tcb.start) + val end = insns.indexOf(tcb.end) + for (i in begin..end - 1) { + val insnHandlers = handlers[i] ?: ArrayList() + handlers[i] = insnHandlers + + insnHandlers.add(tcb) + } + } + + return handlers } \ 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 88d890325ce..16afc02f18e 100644 --- a/test/org/jetbrains/eval4j/test/TestData.java +++ b/test/org/jetbrains/eval4j/test/TestData.java @@ -208,4 +208,44 @@ class TestData { static int constructorCallWithArgs() { return new C(10).y; } + + static class MyEx extends RuntimeException { + final int x; + + MyEx(int x) { + this.x = x; + } + } + + static int tryCatch() { + try { + throw new MyEx(10); + } + catch (MyEx e) { + return e.x; + } + } + + static int tryWiderCatch() { + int a = 10; + try { + if (a > 0) { + throw new MyEx(10); + } + } catch (Exception e) { + return ((MyEx) e).x; + } + return 2; + } + + static int classCastException() { + Object a = ""; + try { + Integer s = (Integer) a; + } + catch (ClassCastException e) { + return 1; + } + return 2; + } } diff --git a/test/org/jetbrains/eval4j/test/main.kt b/test/org/jetbrains/eval4j/test/main.kt index 5fce03afe39..5e60db79c9e 100644 --- a/test/org/jetbrains/eval4j/test/main.kt +++ b/test/org/jetbrains/eval4j/test/main.kt @@ -111,11 +111,9 @@ object REFLECTION_EVAL : Eval { return NewObjectValue(_class, classType) } - override fun checkCast(value: Value, targetType: Type): Value { - throw UnsupportedOperationException() - } - override fun isInsetanceOf(value: Value, targetType: Type): Boolean { - throw UnsupportedOperationException() + override fun isInstanceOf(value: Value, targetType: Type): Boolean { + val _class = findClass(targetType.getInternalName()) + return _class.isInstance(value.obj) } override fun newArray(arrayType: Type, size: Int): Value {