Simplify work with stack by adding additional newFrame method

This commit is contained in:
Ivan Kylchik
2020-10-22 15:03:53 +03:00
committed by TeamCityServer
parent d578aa0ec5
commit 7fe776fe10
2 changed files with 38 additions and 40 deletions
@@ -73,10 +73,9 @@ 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, parentFile: IrFile? = null): IrExpression { fun interpret(expression: IrExpression, rootFile: IrFile? = null): IrExpression {
stack.clean() stack.clean(rootFile)
val result = stack.withEntryPoint(irFile = parentFile) { expression.interpret() } return 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()
@@ -377,12 +376,12 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
val irClass = owner.parent as IrClass val irClass = owner.parent as IrClass
if (irClass.hasAnnotation(evaluateIntrinsicAnnotation) || irClass.fqNameWhenAvailable!!.startsWith(Name.identifier("java"))) { if (irClass.hasAnnotation(evaluateIntrinsicAnnotation) || irClass.fqNameWhenAvailable!!.startsWith(Name.identifier("java"))) {
return stack.newFrame(initPool = valueArguments) { Wrapper.getConstructorMethod(owner).invokeMethod(owner) } return stack.newFrame(owner, initPool = valueArguments) { Wrapper.getConstructorMethod(owner).invokeMethod(owner) }
} }
if (irClass.defaultType.isArray() || irClass.defaultType.isPrimitiveArray()) { if (irClass.defaultType.isArray() || irClass.defaultType.isPrimitiveArray()) {
// array constructor doesn't have body so must be treated separately // array constructor doesn't have body so must be treated separately
return stack.newFrame(initPool = valueArguments) { handleIntrinsicMethods(owner) } return stack.newFrame(owner, initPool = valueArguments) { handleIntrinsicMethods(owner) }
.apply { .apply {
val array = stack.popReturnValue() as Primitive<*> val array = stack.popReturnValue() as Primitive<*>
stack.pushReturnValue(Primitive(array.value, constructorCall.type)) stack.pushReturnValue(Primitive(array.value, constructorCall.type))
@@ -413,7 +412,7 @@ class IrInterpreter(val irBuiltIns: IrBuiltIns, private val bodyMap: Map<IdSigna
} }
valueArguments.add(Variable(constructorCall.getThisReceiver(), state)) //used to set up fields in body valueArguments.add(Variable(constructorCall.getThisReceiver(), state)) //used to set up fields in body
return stack.newFrame(initPool = valueArguments) { return stack.newFrame(owner, initPool = valueArguments) {
val statements = constructorCall.getBody()!!.statements val statements = constructorCall.getBody()!!.statements
when (val irStatement = statements[0]) { when (val irStatement = statements[0]) {
is IrTypeOperatorCall -> { is IrTypeOperatorCall -> {
@@ -21,13 +21,13 @@ 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 newFrame(irFunction: IrFunction, initPool: List<Variable> = listOf(), block: () -> ExecutionResult): ExecutionResult
fun newFrame(irFile: IrFile?, initPool: List<Variable> = listOf(), block: () -> ExecutionResult): ExecutionResult
fun fixCallEntryPoint(irExpression: IrExpression) 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
fun clean() fun clean(rootFile: IrFile?)
fun addVar(variable: Variable) fun addVar(variable: Variable)
fun addAll(variables: List<Variable>) fun addAll(variables: List<Variable>)
fun getVariable(symbol: IrSymbol): Variable fun getVariable(symbol: IrSymbol): Variable
@@ -41,16 +41,21 @@ internal interface Stack {
} }
internal class StackImpl : Stack { internal class StackImpl : Stack {
private val frameList = mutableListOf(FrameContainer()) // first frame is default, it is easier to work when last() is not null private val frameList = mutableListOf<FrameContainer>()
private fun getCurrentFrame() = frameList.last() private fun getCurrentFrame() = frameList.last()
private var stackCount = 0 private var stackCount = 0
override fun newFrame(asSubFrame: Boolean, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult { private fun addNewFrame(asSubFrame: Boolean, initPool: List<Variable>, irFunction: IrFunction? = null, irFile: IrFile? = null) {
val typeArgumentsPool = initPool.filter { it.symbol is IrTypeParameterSymbol } val typeArgumentsPool = initPool.filter { it.symbol is IrTypeParameterSymbol }
val valueArguments = initPool.filter { it.symbol !is IrTypeParameterSymbol } val valueArguments = initPool.filter { it.symbol !is IrTypeParameterSymbol }
val newFrame = InterpreterFrame(valueArguments.toMutableList(), typeArgumentsPool) val newFrame = InterpreterFrame(valueArguments.toMutableList(), typeArgumentsPool)
if (asSubFrame) getCurrentFrame().addSubFrame(newFrame) else frameList.add(FrameContainer(newFrame)) when {
asSubFrame -> getCurrentFrame().addSubFrame(newFrame)
else -> frameList.add(FrameContainer(irFile, irFunction, newFrame))
}
}
private fun withStackIncrement(asSubFrame: Boolean, block: () -> ExecutionResult): ExecutionResult {
return try { return try {
stackCount++ stackCount++
block() block()
@@ -60,13 +65,20 @@ internal class StackImpl : Stack {
} }
} }
override fun newFrame(asSubFrame: Boolean, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult {
addNewFrame(asSubFrame, initPool, null, null)
return withStackIncrement(asSubFrame, block)
}
override fun newFrame(irFunction: IrFunction, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult { override fun newFrame(irFunction: IrFunction, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult {
val asSubFrame = irFunction.isInline || irFunction.isLocal val asSubFrame = irFunction.isInline || irFunction.isLocal
return newFrame(asSubFrame, initPool) { addNewFrame(asSubFrame, initPool, irFunction, irFunction.fileOrNull)
withEntryPoint(irFunction, irFunction.fileOrNull) { return withStackIncrement(asSubFrame, block)
block() }
}
} override fun newFrame(irFile: IrFile?, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult {
addNewFrame(false, initPool, null, irFile)
return withStackIncrement(false, block)
} }
private fun removeLastFrame() { private fun removeLastFrame() {
@@ -75,16 +87,9 @@ internal class StackImpl : Stack {
} }
override fun fixCallEntryPoint(irExpression: IrExpression) { override fun fixCallEntryPoint(irExpression: IrExpression) {
val fileEntry = getCurrentFrame().entryPoint?.irFile?.fileEntry ?: return val fileEntry = getCurrentFrame().irFile?.fileEntry ?: return
val lineNum = fileEntry.getLineNumber(irExpression.startOffset) + 1 val lineNum = fileEntry.getLineNumber(irExpression.startOffset) + 1
getCurrentFrame().entryPoint?.lineNumber = lineNum getCurrentFrame().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> { override fun getStackTrace(): List<String> {
@@ -94,10 +99,10 @@ internal class StackImpl : Stack {
override fun getStackCount(): Int = stackCount override fun getStackCount(): Int = stackCount
override fun clean() { override fun clean(rootFile: IrFile?) {
stackCount = 0 stackCount = 0
frameList.clear() frameList.clear()
frameList.add(FrameContainer()) rootFile?.let { frameList.add(FrameContainer(rootFile)) }
} }
override fun addVar(variable: Variable) { override fun addVar(variable: Variable) {
@@ -137,17 +142,11 @@ internal class StackImpl : Stack {
} }
} }
private class FrameContainer(current: Frame = InterpreterFrame()) { private class FrameContainer(val irFile: IrFile? = null, private val entryPoint: IrFunction? = null, current: Frame = InterpreterFrame()) {
var entryPoint: EntryPoint? = null var lineNumber: Int = -1
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)
} }
@@ -173,9 +172,9 @@ private class FrameContainer(current: Frame = InterpreterFrame()) {
fun peekReturnValue() = getTopFrame().peekReturnValue() fun peekReturnValue() = getTopFrame().peekReturnValue()
override fun toString(): String { override fun toString(): String {
entryPoint?.irFile ?: return "Not defined" irFile ?: return "Not defined"
val fileNameCapitalized = entryPoint!!.irFile.name.replace(".kt", "Kt").capitalize() val fileNameCapitalized = irFile.name.replace(".kt", "Kt").capitalize()
val lineNum = if (entryPoint?.lineNumber != -1) ":${entryPoint?.lineNumber}" else "" val lineNum = if (lineNumber != -1) ":$lineNumber" else ""
return "at $fileNameCapitalized.${entryPoint?.irFunction?.fqNameWhenAvailable ?: "<clinit>"}(${entryPoint!!.irFile.name}$lineNum)" return "at $fileNameCapitalized.${entryPoint?.fqNameWhenAvailable ?: "<clinit>"}(${irFile.name}$lineNum)"
} }
} }