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 mapOfEnums = mutableMapOf<IrSymbol, Complex>()
private val mapOfObjects = mutableMapOf<IrSymbol, Complex>() private val mapOfObjects = mutableMapOf<IrSymbol, Complex>()
constructor(irModule: IrModuleFragment): this(irModule.irBuiltins) { constructor(irModule: IrModuleFragment) : this(irModule.irBuiltins) {
irExceptions.addAll( irExceptions.addAll(
irModule.files irModule.files
.flatMap { it.declarations } .flatMap { it.declarations }
@@ -73,25 +73,21 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
if (commandCount >= MAX_COMMANDS) InterpreterTimeOutError().throwAsUserException() if (commandCount >= MAX_COMMANDS) InterpreterTimeOutError().throwAsUserException()
} }
fun interpret(expression: IrExpression): IrExpression { fun interpret(expression: IrExpression, parentFile: IrFile? = null): IrExpression {
stack.clean() stack.clean()
return try { val result = stack.withEntryPoint(irFile = parentFile) { expression.interpret() }
when (val returnLabel = expression.interpret().returnLabel) { return when (val returnLabel = result.returnLabel) {
ReturnLabel.REGULAR -> stack.popReturnValue().toIrExpression(expression) ReturnLabel.REGULAR -> stack.popReturnValue().toIrExpression(expression)
ReturnLabel.EXCEPTION -> { ReturnLabel.EXCEPTION -> {
val message = (stack.popReturnValue() as ExceptionState).getFullDescription() val message = (stack.popReturnValue() as ExceptionState).getFullDescription()
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + message) IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + message)
}
else -> TODO("$returnLabel not supported as result of interpretation")
} }
} catch (e: Throwable) { else -> TODO("$returnLabel not supported as result of interpretation")
// TODO don't handle, throw to lowering
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + e.message)
} }
} }
internal fun IrFunction.interpret(valueArguments: List<Variable>, expectedResultClass: Class<*> = Any::class.java): Any? { 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() this@interpret.interpret()
} }
return when (returnLabel.returnLabel) { 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 // this method is used to get stack trace after exception
private fun interpretFunction(irFunction: IrSimpleFunction): ExecutionResult { private fun interpretFunction(irFunction: IrSimpleFunction): ExecutionResult {
if (stack.getStackCount() >= MAX_STACK) StackOverflowError().throwAsUserException() if (stack.getStackCount() >= MAX_STACK) StackOverflowError().throwAsUserException()
if (irFunction.fileOrNull != null) stack.setCurrentFrameName(irFunction)
if (irFunction.body is IrSyntheticBody) return handleIntrinsicMethods(irFunction) if (irFunction.body is IrSyntheticBody) return handleIntrinsicMethods(irFunction)
return irFunction.body?.interpret() ?: throw InterpreterError("Ir function must be with body") 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) } 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 // inline only methods are not presented in lookup table, so must be interpreted instead of execution
val isInlineOnly = irFunction.hasAnnotation(FqName("kotlin.internal.InlineOnly")) val isInlineOnly = irFunction.hasAnnotation(FqName("kotlin.internal.InlineOnly"))
return@newFrame when { return@newFrame when {
@@ -821,6 +816,7 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
private fun interpretThrow(expression: IrThrow): ExecutionResult { private fun interpretThrow(expression: IrThrow): ExecutionResult {
expression.value.interpret().check { return it } expression.value.interpret().check { return it }
stack.fixCallEntryPoint(expression)
when (val exception = stack.popReturnValue()) { when (val exception = stack.popReturnValue()) {
is Common -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace())) is Common -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace()))
is Wrapper -> 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 package org.jetbrains.kotlin.ir.interpreter.stack
import org.jetbrains.kotlin.ir.interpreter.ExecutionResult import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.interpreter.getCapitalizedFileName
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.name 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.interpreter.exceptions.InterpreterError
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.util.file import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.ir.util.fileEntry
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isLocal
internal interface Stack { internal interface Stack {
fun newFrame(asSubFrame: Boolean = false, initPool: List<Variable> = listOf(), block: () -> ExecutionResult): ExecutionResult 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 getStackTrace(): List<String>
fun getStackCount(): Int 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() { private fun removeLastFrame() {
if (frameList.size > 1 && getCurrentFrame().hasReturnValue()) frameList[frameList.lastIndex - 1].pushReturnValue(getCurrentFrame()) if (frameList.size > 1 && getCurrentFrame().hasReturnValue()) frameList[frameList.lastIndex - 1].pushReturnValue(getCurrentFrame())
frameList.removeAt(frameList.lastIndex) frameList.removeAt(frameList.lastIndex)
} }
override fun setCurrentFrameName(irFunction: IrFunction) { override fun fixCallEntryPoint(irExpression: IrExpression) {
val fileName = irFunction.file.name val fileEntry = getCurrentFrame().entryPoint?.irFile?.fileEntry ?: return
val fileNameCapitalized = irFunction.getCapitalizedFileName() val lineNum = fileEntry.getLineNumber(irExpression.startOffset) + 1
val lineNum = irFunction.fileEntry.getLineNumber(irFunction.startOffset) + 1 getCurrentFrame().entryPoint?.lineNumber = lineNum
if (getCurrentFrame().frameEntryPoint == null) }
getCurrentFrame().frameEntryPoint = "at $fileNameCapitalized.${irFunction.fqNameWhenAvailable}($fileName:$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> { override fun getStackTrace(): List<String> {
// TODO implement some sort of cache // TODO implement some sort of cache
return frameList.mapNotNull { it.frameEntryPoint } return frameList.map { it.toString() }
} }
override fun getStackCount(): Int = stackCount override fun getStackCount(): Int = stackCount
@@ -121,10 +138,16 @@ internal class StackImpl : Stack {
} }
private class FrameContainer(current: Frame = InterpreterFrame()) { private class FrameContainer(current: Frame = InterpreterFrame()) {
var frameEntryPoint: String? = null var entryPoint: EntryPoint? = null
private val innerStack = mutableListOf(current) private val innerStack = mutableListOf(current)
private fun getTopFrame() = innerStack.first() private fun getTopFrame() = innerStack.first()
companion object {
class EntryPoint(val irFunction: IrFunction?, val irFile: IrFile) {
var lineNumber: Int = -1
}
}
fun addSubFrame(frame: Frame) { fun addSubFrame(frame: Frame) {
innerStack.add(0, frame) innerStack.add(0, frame)
} }
@@ -149,5 +172,10 @@ private class FrameContainer(current: Frame = InterpreterFrame()) {
fun popReturnValue() = getTopFrame().popReturnValue() fun popReturnValue() = getTopFrame().popReturnValue()
fun peekReturnValue() = getTopFrame().peekReturnValue() 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)"
}
} }