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 c86461e2e6b..1775fc061c3 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 @@ -36,7 +36,6 @@ class IrInterpreter(irModule: IrModuleFragment) { private val illegalArgumentException = irExceptions.first { it.name.asString() == IllegalArgumentException::class.java.simpleName } private val stack = StackImpl() - private val stackTrace = mutableListOf() private var commandCount = 0 private val mapOfEnums = mutableMapOf, Complex>() @@ -129,34 +128,21 @@ class IrInterpreter(irModule: IrModuleFragment) { // catch exception from JVM such as: ArithmeticException, StackOverflowError and others val exceptionName = e::class.java.simpleName val irExceptionClass = irExceptions.firstOrNull { it.name.asString() == exceptionName } ?: irBuiltIns.throwableClass.owner - stack.pushReturnValue(ExceptionState(e, irExceptionClass, stackTrace)) + stack.pushReturnValue(ExceptionState(e, irExceptionClass, stack.getStackTrace())) return Exception } } // this method is used to get stack trace after exception private suspend fun interpretFunction(irFunction: IrFunctionImpl): ExecutionResult { - return try { - yield() + yield() - if (irFunction.fileOrNull != null) { - val fileName = irFunction.file.name - val lineNum = irFunction.fileEntry.getLineNumber(irFunction.startOffset) + 1 - stackTrace += "at ${fileName.replace(".kt", "Kt").capitalize()}.${irFunction.fqNameForIrSerialization}($fileName:$lineNum)" - } + if (irFunction.fileOrNull != null) stack.setCurrentFrameName(irFunction) - if (stackTrace.size == MAX_STACK_SIZE) { - throw StackOverflowError("") - } + if (stack.getStackTrace().size == MAX_STACK_SIZE) throw StackOverflowError("") - when (val kind = (irFunction.body as? IrSyntheticBody)?.kind) { - IrSyntheticBodyKind.ENUM_VALUES, IrSyntheticBodyKind.ENUM_VALUEOF -> handleIntrinsicMethods(irFunction) - null -> irFunction.body?.interpret() ?: throw InterpreterException("Ir function must be with body") - else -> throw InterpreterException("Unsupported IrSyntheticBodyKind $kind") - } - } finally { - if (irFunction.fileOrNull != null) stackTrace.removeAt(stackTrace.lastIndex) - } + if (irFunction.body is IrSyntheticBody) return handleIntrinsicMethods(irFunction) + return irFunction.body?.interpret() ?: throw InterpreterException("Ir function must be with body") } private suspend fun MethodHandle?.invokeMethod(irFunction: IrFunction): ExecutionResult { @@ -202,7 +188,7 @@ class IrInterpreter(irModule: IrModuleFragment) { .singleOrNull { it.name.asString() == enumEntryName } if (enumEntry == null) { val message = "No enum constant ${enumClass.fqNameForIrSerialization}.$enumEntryName" - stack.pushReturnValue(ExceptionState(IllegalArgumentException(message), illegalArgumentException, stackTrace)) + stack.pushReturnValue(ExceptionState(IllegalArgumentException(message), illegalArgumentException, stack.getStackTrace())) return Exception } else { enumEntry.interpret().check { return it } @@ -682,7 +668,7 @@ class IrInterpreter(irModule: IrModuleFragment) { val convertibleClassName = stack.popReturnValue().irClass.fqNameForIrSerialization val castClassName = expression.type.classOrNull?.owner?.fqNameForIrSerialization val message = "$convertibleClassName cannot be cast to $castClassName" - stack.pushReturnValue(ExceptionState(ClassCastException(message), classCastException, stackTrace)) + stack.pushReturnValue(ExceptionState(ClassCastException(message), classCastException, stack.getStackTrace())) return Exception } } @@ -759,8 +745,8 @@ class IrInterpreter(irModule: IrModuleFragment) { private suspend fun interpretThrow(expression: IrThrow): ExecutionResult { expression.value.interpret().check { return it } when (val exception = stack.popReturnValue()) { - is Common -> stack.pushReturnValue(ExceptionState(exception, stackTrace)) - is Wrapper -> stack.pushReturnValue(ExceptionState(exception, stackTrace)) + is Common -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace())) + is Wrapper -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace())) is ExceptionState -> stack.pushReturnValue(exception) else -> throw InterpreterException("${exception::class} cannot be used as exception state") } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Stack.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Stack.kt index f960dc753be..2a8789b0ae3 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Stack.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Stack.kt @@ -9,6 +9,11 @@ import org.jetbrains.kotlin.backend.common.interpreter.ExecutionResult import org.jetbrains.kotlin.backend.common.interpreter.exceptions.InterpreterException import org.jetbrains.kotlin.backend.common.interpreter.state.State import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.name +import org.jetbrains.kotlin.ir.util.file +import org.jetbrains.kotlin.ir.util.fileEntry +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult interface Stack { @@ -16,6 +21,9 @@ interface Stack { asSubFrame: Boolean = false, initPool: List = listOf(), block: suspend () -> ExecutionResult ): ExecutionResult + fun setCurrentFrameName(irFunction: IrFunction) + fun getStackTrace(): List + fun clean() fun addVar(variable: Variable) fun addAll(variables: List) @@ -49,6 +57,18 @@ class StackImpl : Stack { frameList.removeAt(frameList.lastIndex) } + override fun setCurrentFrameName(irFunction: IrFunction) { + val fileName = irFunction.file.name + val fileNameCapitalized = fileName.replace(".kt", "Kt").capitalize() + val lineNum = irFunction.fileEntry.getLineNumber(irFunction.startOffset) + 1 + getCurrentFrame().frameEntryPoint = "at $fileNameCapitalized.${irFunction.fqNameWhenAvailable}($fileName:$lineNum)" + } + + override fun getStackTrace(): List { + // TODO implement some sort of cache + return frameList.mapNotNull { it.frameEntryPoint } + } + override fun clean() { frameList.clear() frameList.add(FrameContainer()) @@ -92,7 +112,8 @@ class StackImpl : Stack { } private class FrameContainer(current: Frame = InterpreterFrame()) { - private var innerStack = mutableListOf(current) + var frameEntryPoint: String? = null + private val innerStack = mutableListOf(current) private fun getTopFrame() = innerStack.first() fun addSubFrame(frame: Frame) {