diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index 33a32e18969..396595ff499 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -43,7 +43,7 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map() private val mapOfObjects = mutableMapOf() - constructor(irModule: IrModuleFragment): this(irModule.irBuiltins) { + constructor(irModule: IrModuleFragment) : this(irModule.irBuiltins) { irExceptions.addAll( irModule.files .flatMap { it.declarations } @@ -73,25 +73,21 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map= MAX_COMMANDS) InterpreterTimeOutError().throwAsUserException() } - fun interpret(expression: IrExpression): IrExpression { + fun interpret(expression: IrExpression, parentFile: IrFile? = null): IrExpression { stack.clean() - return try { - when (val returnLabel = expression.interpret().returnLabel) { - ReturnLabel.REGULAR -> stack.popReturnValue().toIrExpression(expression) - ReturnLabel.EXCEPTION -> { - val message = (stack.popReturnValue() as ExceptionState).getFullDescription() - IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + message) - } - else -> TODO("$returnLabel not supported as result of interpretation") + val result = stack.withEntryPoint(irFile = parentFile) { expression.interpret() } + return when (val returnLabel = result.returnLabel) { + ReturnLabel.REGULAR -> stack.popReturnValue().toIrExpression(expression) + ReturnLabel.EXCEPTION -> { + val message = (stack.popReturnValue() as ExceptionState).getFullDescription() + IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + message) } - } catch (e: Throwable) { - // TODO don't handle, throw to lowering - IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + e.message) + else -> TODO("$returnLabel not supported as result of interpretation") } } internal fun IrFunction.interpret(valueArguments: List, expectedResultClass: Class<*> = Any::class.java): Any? { - val returnLabel = stack.newFrame(asSubFrame = this.isLocal, initPool = valueArguments) { + val returnLabel = stack.newFrame(this, initPool = valueArguments) { this@interpret.interpret() } return when (returnLabel.returnLabel) { @@ -158,8 +154,6 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map= MAX_STACK) StackOverflowError().throwAsUserException() - if (irFunction.fileOrNull != null) stack.setCurrentFrameName(irFunction) - if (irFunction.body is IrSyntheticBody) return handleIntrinsicMethods(irFunction) return irFunction.body?.interpret() ?: throw InterpreterError("Ir function must be with body") } @@ -316,7 +310,8 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace())) is Wrapper -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace())) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/Stack.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/Stack.kt index 190266dcb77..b30271d93e2 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/Stack.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/Stack.kt @@ -5,22 +5,25 @@ package org.jetbrains.kotlin.ir.interpreter.stack -import org.jetbrains.kotlin.ir.interpreter.ExecutionResult -import org.jetbrains.kotlin.ir.interpreter.getCapitalizedFileName -import org.jetbrains.kotlin.ir.interpreter.state.State +import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.name +import org.jetbrains.kotlin.ir.interpreter.ExecutionResult +import org.jetbrains.kotlin.ir.interpreter.state.State +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol -import org.jetbrains.kotlin.ir.util.file -import org.jetbrains.kotlin.ir.util.fileEntry +import org.jetbrains.kotlin.ir.util.fileOrNull import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.util.isLocal internal interface Stack { fun newFrame(asSubFrame: Boolean = false, initPool: List = listOf(), block: () -> ExecutionResult): ExecutionResult + fun newFrame(irFunction: IrFunction, initPool: List = listOf(), block: () -> ExecutionResult): ExecutionResult - fun setCurrentFrameName(irFunction: IrFunction) + fun fixCallEntryPoint(irExpression: IrExpression) + fun withEntryPoint(irFunction: IrFunction? = null, irFile: IrFile? = null, block: () -> ExecutionResult): ExecutionResult fun getStackTrace(): List fun getStackCount(): Int @@ -57,22 +60,36 @@ internal class StackImpl : Stack { } } + override fun newFrame(irFunction: IrFunction, initPool: List, block: () -> ExecutionResult): ExecutionResult { + val asSubFrame = irFunction.isInline || irFunction.isLocal + return newFrame(asSubFrame, initPool) { + withEntryPoint(irFunction, irFunction.fileOrNull) { + block() + } + } + } + private fun removeLastFrame() { if (frameList.size > 1 && getCurrentFrame().hasReturnValue()) frameList[frameList.lastIndex - 1].pushReturnValue(getCurrentFrame()) frameList.removeAt(frameList.lastIndex) } - override fun setCurrentFrameName(irFunction: IrFunction) { - val fileName = irFunction.file.name - val fileNameCapitalized = irFunction.getCapitalizedFileName() - val lineNum = irFunction.fileEntry.getLineNumber(irFunction.startOffset) + 1 - if (getCurrentFrame().frameEntryPoint == null) - getCurrentFrame().frameEntryPoint = "at $fileNameCapitalized.${irFunction.fqNameWhenAvailable}($fileName:$lineNum)" + override fun fixCallEntryPoint(irExpression: IrExpression) { + val fileEntry = getCurrentFrame().entryPoint?.irFile?.fileEntry ?: return + val lineNum = fileEntry.getLineNumber(irExpression.startOffset) + 1 + getCurrentFrame().entryPoint?.lineNumber = lineNum + } + + // TODO remove method and create EntryPoint in frame constructor + override fun withEntryPoint(irFunction: IrFunction?, irFile: IrFile?, block: () -> ExecutionResult): ExecutionResult { + val frame = getCurrentFrame() + if (frame.entryPoint == null && irFile != null) frame.entryPoint = FrameContainer.Companion.EntryPoint(irFunction, irFile) + return block() } override fun getStackTrace(): List { // TODO implement some sort of cache - return frameList.mapNotNull { it.frameEntryPoint } + return frameList.map { it.toString() } } override fun getStackCount(): Int = stackCount @@ -121,10 +138,16 @@ internal class StackImpl : Stack { } private class FrameContainer(current: Frame = InterpreterFrame()) { - var frameEntryPoint: String? = null + var entryPoint: EntryPoint? = null private val innerStack = mutableListOf(current) private fun getTopFrame() = innerStack.first() + companion object { + class EntryPoint(val irFunction: IrFunction?, val irFile: IrFile) { + var lineNumber: Int = -1 + } + } + fun addSubFrame(frame: Frame) { innerStack.add(0, frame) } @@ -149,5 +172,10 @@ private class FrameContainer(current: Frame = InterpreterFrame()) { fun popReturnValue() = getTopFrame().popReturnValue() fun peekReturnValue() = getTopFrame().peekReturnValue() - override fun toString() = frameEntryPoint ?: "Not defined" + override fun toString(): String { + entryPoint?.irFile ?: return "Not defined" + val fileNameCapitalized = entryPoint!!.irFile.name.replace(".kt", "Kt").capitalize() + val lineNum = if (entryPoint?.lineNumber != -1) ":${entryPoint?.lineNumber}" else "" + return "at $fileNameCapitalized.${entryPoint?.irFunction?.fqNameWhenAvailable ?: ""}(${entryPoint!!.irFile.name}$lineNum)" + } }