Save right entry point for expression
This commit is contained in:
committed by
TeamCityServer
parent
9d9ab498aa
commit
ec16c40bf4
+13
-17
@@ -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)"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user