From bb119280be2b7031e00f5607e29d32620ca0bfbb Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Tue, 21 Jan 2020 00:51:53 +0300 Subject: [PATCH] Interpret basic interpretation for try catch block --- .../common/interpreter/IrInterpreter.kt | 57 +++++++++++++++++-- .../backend/common/interpreter/stack/State.kt | 9 +-- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt index 0abd5786ac7..45bef7d0cfa 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -14,9 +14,13 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.types.isSubtypeOf import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.isSubclassOf import org.jetbrains.kotlin.ir.util.statements import java.lang.invoke.MethodHandle @@ -26,6 +30,8 @@ enum class Code(var info: String = "") { class IrInterpreter(irModule: IrModuleFragment) { private val irBuiltIns = irModule.irBuiltins + private val irExceptions = irModule.files.flatMap { it.declarations }.filterIsInstance() + .filter { it.isSubclassOf(irBuiltIns.throwableClass.owner) } private fun Any?.getType(defaultType: IrType): IrType { return when (this) { @@ -44,7 +50,19 @@ class IrInterpreter(irModule: IrModuleFragment) { } fun interpret(expression: IrExpression): IrExpression { - return InterpreterFrame().apply { expression.interpret(this) }.popReturnValue().toIrExpression(expression) + val data = InterpreterFrame() + return when (val code = expression.interpret(data)) { + Code.NEXT -> data.popReturnValue().toIrExpression(expression) + Code.EXCEPTION -> { + val message = when (val exception = data.popReturnValue()) { + is Wrapper -> (exception.value as Throwable).message.toString() + is Complex -> (exception.fields.first { it.descriptor.name.asString() == "message" }.state as Primitive<*>).value.toString() + else -> TODO("not supported") + } + IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, message) + } + else -> TODO("$code not supported as result of interpretation") + } } private fun IrElement.interpret(data: Frame): Code { @@ -71,6 +89,9 @@ class IrInterpreter(irModule: IrModuleFragment) { is IrBreak -> interpretBreak(this, data) is IrContinue -> interpretContinue(this, data) is IrVararg -> interpretVararg(this, data) + is IrTry -> interpretTry(this, data) + is IrCatch -> interpretCatch(this, data) + is IrThrow -> interpretThrow(this, data) else -> TODO("${this.javaClass} not supported") } @@ -92,12 +113,13 @@ class IrInterpreter(irModule: IrModuleFragment) { is IrWhileLoop -> if ((this.label ?: "") == code.info) this.interpret(data) else code else -> code } - Code.EXCEPTION -> TODO("Code.EXCEPTION not implemented") + Code.EXCEPTION -> Code.EXCEPTION Code.NEXT -> Code.NEXT } - } catch (e: Exception) { - e.printStackTrace() - assert(false) + } catch (e: Exception) { // TODO catch Throwable + // catch exception from JVM such as: ArithmeticException, StackOverflowError and others + val irExceptionClass = irExceptions.firstOrNull { it.name.asString() == e::class.java.simpleName } + irExceptionClass?.let { data.pushReturnValue(Wrapper(e, it)) } ?: throw AssertionError("Cannot handle exception $e") return Code.EXCEPTION } } @@ -444,4 +466,29 @@ class IrInterpreter(irModule: IrModuleFragment) { return Code.NEXT } + + private fun interpretTry(expression: IrTry, data: Frame): Code { + var code = expression.tryResult.interpret(data) + if (code == Code.EXCEPTION) { + val exception = data.peekReturnValue() + for (catchBlock in expression.catches) { + if (exception.irClass.defaultType.isSubtypeOf(catchBlock.catchParameter.type, irBuiltIns)) { + code = catchBlock.interpret(data) + break + } + } + } + return expression.finallyExpression?.interpret(data) ?: code + } + + private fun interpretCatch(expression: IrCatch, data: Frame): Code { + val newFrame = InterpreterFrame(data.getAll().toMutableList()) + newFrame.addVar(Variable(expression.parameter, data.popReturnValue())) + return expression.result.interpret(newFrame).apply { data.pushReturnValue(newFrame) } + } + + private fun interpretThrow(expression: IrThrow, data: Frame): Code { + expression.value.interpret(data) + return Code.EXCEPTION + } } \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt index 41cfd979d6d..53951dd418c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt @@ -139,12 +139,13 @@ class Wrapper(val value: Any, override var irClass: IrClass) : Complex(irClass, private val receiverClass = irClass.defaultType.getClass(true) fun getMethod(irFunction: IrFunction): MethodHandle { - // intrinsicName is used to get correct java method - // for example, method 'get' in kotlin StringBuilder is actually 'charAt' in java StringBuilder - val intrinsicName = irFunction.getEvaluateIntrinsicValue() - // if function is actually a getter, then use property name as method name val property = (irFunction as? IrFunctionImpl)?.correspondingPropertySymbol?.owner + + // intrinsicName is used to get correct java method + // for example: - method 'get' in kotlin StringBuilder is actually 'charAt' in java StringBuilder + // - use getter for private fields such as detailMessage in java.lang.Throwable + val intrinsicName = property?.getEvaluateIntrinsicValue() ?: irFunction.getEvaluateIntrinsicValue() val methodName = intrinsicName ?: (property ?: irFunction).name.toString() val methodType = irFunction.getMethodType()