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'>
|
<item name='org.objectweb.asm.tree.MethodNode org.objectweb.asm.tree.LabelNode getLabelNode(org.objectweb.asm.Label) 0'>
|
||||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||||
</item>
|
</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'>
|
<item name='org.objectweb.asm.tree.MethodNode void accept(org.objectweb.asm.ClassVisitor) 0'>
|
||||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||||
</item>
|
</item>
|
||||||
@@ -551,6 +554,12 @@
|
|||||||
<item name='org.objectweb.asm.tree.TableSwitchInsnNode void accept(org.objectweb.asm.MethodVisitor) 0'>
|
<item name='org.objectweb.asm.tree.TableSwitchInsnNode void accept(org.objectweb.asm.MethodVisitor) 0'>
|
||||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||||
</item>
|
</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'>
|
<item name='org.objectweb.asm.tree.TryCatchBlockNode void accept(org.objectweb.asm.MethodVisitor) 0'>
|
||||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ trait Eval {
|
|||||||
fun loadClass(classType: Type): Value
|
fun loadClass(classType: Type): Value
|
||||||
fun loadString(str: String): Value
|
fun loadString(str: String): Value
|
||||||
fun newInstance(classType: Type): Value
|
fun newInstance(classType: Type): Value
|
||||||
fun checkCast(value: Value, targetType: Type): Value
|
fun isInstanceOf(value: Value, targetType: Type): Boolean
|
||||||
fun isInsetanceOf(value: Value, targetType: Type): Boolean
|
|
||||||
|
|
||||||
fun newArray(arrayType: Type, size: Int): Value
|
fun newArray(arrayType: Type, size: Int): Value
|
||||||
fun newMultiDimensionalArray(arrayType: Type, dimensionSizes: List<Int>): Value
|
fun newMultiDimensionalArray(arrayType: Type, dimensionSizes: List<Int>): Value
|
||||||
@@ -176,12 +175,20 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
|
|
||||||
CHECKCAST -> {
|
CHECKCAST -> {
|
||||||
val targetType = Type.getObjectType((insn as TypeInsnNode).desc)
|
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 -> {
|
INSTANCEOF -> {
|
||||||
val targetType = Type.getObjectType((insn as TypeInsnNode).desc)
|
val targetType = Type.getObjectType((insn as TypeInsnNode).desc)
|
||||||
boolean(eval.isInsetanceOf(value, targetType))
|
boolean(eval.isInstanceOf(value, targetType))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: maybe just do nothing?
|
// 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.JumpInsnNode
|
||||||
import org.objectweb.asm.tree.VarInsnNode
|
import org.objectweb.asm.tree.VarInsnNode
|
||||||
import org.objectweb.asm.util.Printer
|
import org.objectweb.asm.util.Printer
|
||||||
|
import org.objectweb.asm.tree.TryCatchBlockNode
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
trait InterpreterResult {
|
trait InterpreterResult {
|
||||||
fun toString(): String
|
fun toString(): String
|
||||||
@@ -62,11 +64,27 @@ fun interpreterLoop(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val interpreter = SingleInstructionInterpreter(eval)
|
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) {
|
while (true) {
|
||||||
// TODO try-catch-finally support
|
|
||||||
|
|
||||||
val insnOpcode = currentInsn.getOpcode()
|
val insnOpcode = currentInsn.getOpcode()
|
||||||
val insnType = currentInsn.getType()
|
val insnType = currentInsn.getType()
|
||||||
|
|
||||||
@@ -134,11 +152,11 @@ fun interpreterLoop(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: try/catch/finally
|
|
||||||
ATHROW -> {
|
ATHROW -> {
|
||||||
val exceptionValue = frame.getStack(0)!!
|
val exceptionValue = frame.getStack(0)!!
|
||||||
val handled = handler.exceptionThrown(frame, currentInsn, exceptionValue)
|
val handled = handler.exceptionThrown(frame, currentInsn, exceptionValue)
|
||||||
if (handled != null) return handled
|
if (handled != null) return handled
|
||||||
|
if (exceptionCaught(exceptionValue)) continue
|
||||||
return ExceptionThrown(exceptionValue)
|
return ExceptionThrown(exceptionValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +171,7 @@ fun interpreterLoop(
|
|||||||
// TODO: try/catch.finaly
|
// TODO: try/catch.finaly
|
||||||
val handled = handler.exceptionThrown(frame, currentInsn, e.exception)
|
val handled = handler.exceptionThrown(frame, currentInsn, e.exception)
|
||||||
if (handled != null) return handled
|
if (handled != null) return handled
|
||||||
|
if (exceptionCaught(e.exception)) continue
|
||||||
return ExceptionThrown(e.exception)
|
return ExceptionThrown(e.exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,4 +212,21 @@ fun <V : org.objectweb.asm.tree.analysis.Value> initFrame(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return current
|
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() {
|
static int constructorCallWithArgs() {
|
||||||
return new C(10).y;
|
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)
|
return NewObjectValue(_class, classType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun checkCast(value: Value, targetType: Type): Value {
|
override fun isInstanceOf(value: Value, targetType: Type): Boolean {
|
||||||
throw UnsupportedOperationException()
|
val _class = findClass(targetType.getInternalName())
|
||||||
}
|
return _class.isInstance(value.obj)
|
||||||
override fun isInsetanceOf(value: Value, targetType: Type): Boolean {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun newArray(arrayType: Type, size: Int): Value {
|
override fun newArray(arrayType: Type, size: Int): Value {
|
||||||
|
|||||||
Reference in New Issue
Block a user