Support try..catch and instanceof/casts

This commit is contained in:
Andrey Breslav
2013-10-07 00:16:09 +04:00
parent 3199a8f690
commit 58ad84d4e9
5 changed files with 103 additions and 13 deletions
@@ -386,6 +386,9 @@
<item name='org.objectweb.asm.tree.MethodNode org.objectweb.asm.tree.LabelNode getLabelNode(org.objectweb.asm.Label) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.objectweb.asm.tree.MethodNode tryCatchBlocks'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.objectweb.asm.tree.MethodNode void accept(org.objectweb.asm.ClassVisitor) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
@@ -551,6 +554,12 @@
<item name='org.objectweb.asm.tree.TableSwitchInsnNode void accept(org.objectweb.asm.MethodVisitor) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.objectweb.asm.tree.TryCatchBlockNode end'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.objectweb.asm.tree.TryCatchBlockNode start'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.objectweb.asm.tree.TryCatchBlockNode void accept(org.objectweb.asm.MethodVisitor) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
+11 -4
View File
@@ -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<Int>): Value
@@ -176,12 +175,20 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
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<ClassCastException>())
))
}
}
INSTANCEOF -> {
val targetType = Type.getObjectType((insn as TypeInsnNode).desc)
boolean(eval.isInsetanceOf(value, targetType))
boolean(eval.isInstanceOf(value, targetType))
}
// TODO: maybe just do nothing?
+40 -4
View File
@@ -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 <V : org.objectweb.asm.tree.analysis.Value> initFrame(
}
return current
}
fun computeHandlers(m: MethodNode): Array<out List<TryCatchBlockNode>?> {
val insns = m.instructions
val handlers = Array<MutableList<TryCatchBlockNode>?>(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<TryCatchBlockNode>()
handlers[i] = insnHandlers
insnHandlers.add(tcb)
}
}
return handlers
}
@@ -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;
}
}
+3 -5
View File
@@ -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 {