Support try..catch and instanceof/casts
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user