Support exceptionCaught() handler
This commit is contained in:
@@ -67,6 +67,8 @@ fun interpreterLoop(
|
|||||||
val frame = initFrame(ownerClassInternalName, m, interpreter)
|
val frame = initFrame(ownerClassInternalName, m, interpreter)
|
||||||
val handlers = computeHandlers(m)
|
val handlers = computeHandlers(m)
|
||||||
|
|
||||||
|
class ResultException(val result: InterpreterResult): RuntimeException()
|
||||||
|
|
||||||
fun exceptionCaught(exceptionValue: Value): Boolean {
|
fun exceptionCaught(exceptionValue: Value): Boolean {
|
||||||
val catchBlocks = handlers[m.instructions.indexOf(currentInsn)] ?: listOf()
|
val catchBlocks = handlers[m.instructions.indexOf(currentInsn)] ?: listOf()
|
||||||
for (catch in catchBlocks) {
|
for (catch in catchBlocks) {
|
||||||
@@ -74,6 +76,8 @@ fun interpreterLoop(
|
|||||||
if (exceptionTypeInternalName != null) {
|
if (exceptionTypeInternalName != null) {
|
||||||
val exceptionType = Type.getObjectType(exceptionTypeInternalName)
|
val exceptionType = Type.getObjectType(exceptionTypeInternalName)
|
||||||
if (eval.isInstanceOf(exceptionValue, exceptionType)) {
|
if (eval.isInstanceOf(exceptionValue, exceptionType)) {
|
||||||
|
val handled = handler.exceptionCaught(frame, currentInsn, exceptionValue)
|
||||||
|
if (handled != null) throw ResultException(handled)
|
||||||
frame.clearStack()
|
frame.clearStack()
|
||||||
frame.push(exceptionValue)
|
frame.push(exceptionValue)
|
||||||
goto(catch.handler)
|
goto(catch.handler)
|
||||||
@@ -84,102 +88,107 @@ fun interpreterLoop(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
try {
|
||||||
val insnOpcode = currentInsn.getOpcode()
|
while (true) {
|
||||||
val insnType = currentInsn.getType()
|
val insnOpcode = currentInsn.getOpcode()
|
||||||
|
val insnType = currentInsn.getType()
|
||||||
|
|
||||||
when (insnType) {
|
when (insnType) {
|
||||||
AbstractInsnNode.LABEL,
|
AbstractInsnNode.LABEL,
|
||||||
AbstractInsnNode.FRAME,
|
AbstractInsnNode.FRAME,
|
||||||
AbstractInsnNode.LINE -> {
|
AbstractInsnNode.LINE -> {
|
||||||
// skip to the next instruction
|
// skip to the next instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
when (insnOpcode) {
|
when (insnOpcode) {
|
||||||
GOTO -> {
|
GOTO -> {
|
||||||
goto((currentInsn as JumpInsnNode).label)
|
goto((currentInsn as JumpInsnNode).label)
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
RET -> {
|
|
||||||
val varNode = currentInsn as VarInsnNode
|
|
||||||
val address = frame.getLocal(varNode.`var`)
|
|
||||||
goto((address as LabelValue).value)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: switch
|
|
||||||
LOOKUPSWITCH -> UnsupportedByteCodeException("LOOKUPSWITCH is not supported yet")
|
|
||||||
TABLESWITCH -> UnsupportedByteCodeException("TABLESWITCH is not supported yet")
|
|
||||||
|
|
||||||
IRETURN, LRETURN, FRETURN, DRETURN, ARETURN -> {
|
|
||||||
val value = frame.getStack(0)!!
|
|
||||||
val expectedType = Type.getReturnType(m.desc)
|
|
||||||
if (expectedType.getSort() == Type.OBJECT) {
|
|
||||||
val coerced = if (value != NULL_VALUE && value.asmType != expectedType)
|
|
||||||
ObjectValue(value.obj, expectedType)
|
|
||||||
else value
|
|
||||||
return ValueReturned(coerced)
|
|
||||||
}
|
}
|
||||||
if (value.asmType != expectedType) {
|
|
||||||
assert(insnOpcode == IRETURN, "Only ints should be coerced: " + Printer.OPCODES[insnOpcode])
|
|
||||||
|
|
||||||
val coerced = when (expectedType.getSort()) {
|
RET -> {
|
||||||
Type.BOOLEAN -> boolean(value.boolean)
|
val varNode = currentInsn as VarInsnNode
|
||||||
Type.BYTE -> byte(value.int.toByte())
|
val address = frame.getLocal(varNode.`var`)
|
||||||
Type.SHORT -> short(value.int.toShort())
|
goto((address as LabelValue).value)
|
||||||
Type.CHAR -> char(value.int.toChar())
|
continue
|
||||||
else -> throw UnsupportedByteCodeException("Should not be coerced: $expectedType")
|
}
|
||||||
|
|
||||||
|
// TODO: switch
|
||||||
|
LOOKUPSWITCH -> UnsupportedByteCodeException("LOOKUPSWITCH is not supported yet")
|
||||||
|
TABLESWITCH -> UnsupportedByteCodeException("TABLESWITCH is not supported yet")
|
||||||
|
|
||||||
|
IRETURN, LRETURN, FRETURN, DRETURN, ARETURN -> {
|
||||||
|
val value = frame.getStack(0)!!
|
||||||
|
val expectedType = Type.getReturnType(m.desc)
|
||||||
|
if (expectedType.getSort() == Type.OBJECT) {
|
||||||
|
val coerced = if (value != NULL_VALUE && value.asmType != expectedType)
|
||||||
|
ObjectValue(value.obj, expectedType)
|
||||||
|
else value
|
||||||
|
return ValueReturned(coerced)
|
||||||
}
|
}
|
||||||
return ValueReturned(coerced)
|
if (value.asmType != expectedType) {
|
||||||
|
assert(insnOpcode == IRETURN, "Only ints should be coerced: " + Printer.OPCODES[insnOpcode])
|
||||||
|
|
||||||
|
val coerced = when (expectedType.getSort()) {
|
||||||
|
Type.BOOLEAN -> boolean(value.boolean)
|
||||||
|
Type.BYTE -> byte(value.int.toByte())
|
||||||
|
Type.SHORT -> short(value.int.toShort())
|
||||||
|
Type.CHAR -> char(value.int.toChar())
|
||||||
|
else -> throw UnsupportedByteCodeException("Should not be coerced: $expectedType")
|
||||||
|
}
|
||||||
|
return ValueReturned(coerced)
|
||||||
|
}
|
||||||
|
return ValueReturned(value)
|
||||||
}
|
}
|
||||||
return ValueReturned(value)
|
RETURN -> return ValueReturned(VOID_VALUE)
|
||||||
}
|
IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL -> {
|
||||||
RETURN -> return ValueReturned(VOID_VALUE)
|
if (interpreter.checkUnaryCondition(frame.getStack(0)!!, insnOpcode)) {
|
||||||
IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL -> {
|
frame.execute(currentInsn, interpreter)
|
||||||
if (interpreter.checkUnaryCondition(frame.getStack(0)!!, insnOpcode)) {
|
goto((currentInsn as JumpInsnNode).label)
|
||||||
frame.execute(currentInsn, interpreter)
|
continue
|
||||||
goto((currentInsn as JumpInsnNode).label)
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> {
|
||||||
IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> {
|
if (interpreter.checkBinaryCondition(frame.getStack(0)!!, frame.getStack(1)!!, insnOpcode)) {
|
||||||
if (interpreter.checkBinaryCondition(frame.getStack(0)!!, frame.getStack(1)!!, insnOpcode)) {
|
frame.execute(currentInsn, interpreter)
|
||||||
frame.execute(currentInsn, interpreter)
|
goto((currentInsn as JumpInsnNode).label)
|
||||||
goto((currentInsn as JumpInsnNode).label)
|
continue
|
||||||
continue
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for a bug in Kotlin: NoPatterMatched exception is thrown otherwise!
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
ATHROW -> {
|
try {
|
||||||
val exceptionValue = frame.getStack(0)!!
|
frame.execute(currentInsn, interpreter)
|
||||||
val handled = handler.exceptionThrown(frame, currentInsn, exceptionValue)
|
}
|
||||||
|
catch (e: ThrownFromEvalException) {
|
||||||
|
val handled = handler.exceptionThrown(frame, currentInsn, e.exception)
|
||||||
if (handled != null) return handled
|
if (handled != null) return handled
|
||||||
if (exceptionCaught(exceptionValue)) continue
|
if (exceptionCaught(e.exception)) continue
|
||||||
return ExceptionThrown(exceptionValue)
|
return ExceptionThrown(e.exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for a bug in Kotlin: NoPatterMatched exception is thrown otherwise!
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
frame.execute(currentInsn, interpreter)
|
|
||||||
}
|
|
||||||
catch (e: ThrownFromEvalException) {
|
|
||||||
val handled = handler.exceptionThrown(frame, currentInsn, e.exception)
|
|
||||||
if (handled != null) return handled
|
|
||||||
if (exceptionCaught(e.exception)) continue
|
|
||||||
return ExceptionThrown(e.exception)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val handled = handler.instructionProcessed(currentInsn)
|
||||||
|
if (handled != null) return handled
|
||||||
|
|
||||||
|
goto(currentInsn.getNext())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val handled = handler.instructionProcessed(currentInsn)
|
catch(e: ResultException) {
|
||||||
if (handled != null) return handled
|
return e.result
|
||||||
|
|
||||||
goto(currentInsn.getNext())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user