Save right entry point for expression

This commit is contained in:
Ivan Kylchik
2020-09-22 00:55:02 +03:00
committed by TeamCityServer
parent 9d9ab498aa
commit ec16c40bf4
2 changed files with 56 additions and 32 deletions
@@ -43,7 +43,7 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
private val mapOfEnums = mutableMapOf<IrSymbol, Complex>()
private val mapOfObjects = mutableMapOf<IrSymbol, Complex>()
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<IdSigna
if (commandCount >= 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<Variable>, 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<IdSigna
// this method is used to get stack trace after exception
private fun interpretFunction(irFunction: IrSimpleFunction): ExecutionResult {
if (stack.getStackCount() >= 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<IdSigna
generateSequence(dispatchReceiver.outerClass) { (it.state as? Complex)?.outerClass }.forEach { valueArguments.add(it) }
}
return stack.newFrame(asSubFrame = irFunction.isInline || irFunction.isLocal, initPool = valueArguments) {
stack.fixCallEntryPoint(expression)
return stack.newFrame(irFunction, initPool = valueArguments) {
// inline only methods are not presented in lookup table, so must be interpreted instead of execution
val isInlineOnly = irFunction.hasAnnotation(FqName("kotlin.internal.InlineOnly"))
return@newFrame when {
@@ -821,6 +816,7 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
private fun interpretThrow(expression: IrThrow): ExecutionResult {
expression.value.interpret().check { return it }
stack.fixCallEntryPoint(expression)
when (val exception = stack.popReturnValue()) {
is Common -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace()))
is Wrapper -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace()))
@@ -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<Variable> = listOf(), block: () -> ExecutionResult): ExecutionResult
fun newFrame(irFunction: IrFunction, initPool: List<Variable> = 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<String>
fun getStackCount(): Int
@@ -57,22 +60,36 @@ internal class StackImpl : Stack {
}
}
override fun newFrame(irFunction: IrFunction, initPool: List<Variable>, 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<String> {
// 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 ?: "<clinit>"}(${entryPoint!!.irFile.name}$lineNum)"
}
}