Place variables in interpreter's memory at the beginning

In case than in single SubFrame can appear several variables with
the same symbol, it is important to get the latest one. It is
relevant for local functions.
This commit is contained in:
Ivan Kylchik
2021-06-17 18:30:27 +03:00
committed by TeamCityServer
parent e5ca646de4
commit d41c6e900a
4 changed files with 35 additions and 19 deletions
@@ -187,27 +187,28 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
callStack.dropSubFrame() // drop intermediate frame that contains variables for default arg evaluation
callStack.newFrame(irFunction)
callStack.addInstruction(SimpleInstruction(irFunction))
// 3. load up values onto stack; do it at first to set low priority of these variables
if (dispatchReceiver is StateWithClosure) callStack.loadUpValues(dispatchReceiver)
if (extensionReceiver is StateWithClosure) callStack.loadUpValues(extensionReceiver)
if (irFunction.isLocal) callStack.copyUpValuesFromPreviousFrame()
// TODO: if using KTypeState then it's class must be corresponding
// `call.type` is used in check cast and emptyArray
callStack.addVariable(Variable(irFunction.symbol, KTypeState(call.type, irBuiltIns.anyClass.owner)))
// 3. store arguments in memory (remap args on actual names)
// 4. store arguments in memory (remap args on actual names)
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> callStack.addVariable(Variable(it, receiver)) } }
irFunction.getExtensionReceiver()?.let { callStack.addVariable(Variable(it, extensionReceiver ?: valueArguments.removeFirst())) }
irFunction.valueParameters.forEach { callStack.addVariable(Variable(it.symbol, valueArguments.removeFirst())) }
// 4. store reified type parameters
// 5. store reified type parameters
irFunction.typeParameters.filter { it.isReified }
.forEach { callStack.addVariable(Variable(it.symbol, KTypeState(call.getTypeArgument(it.index)!!, irBuiltIns.anyClass.owner))) }
// 5. load outer class object
// 6. load outer class object
if (dispatchReceiver is Complex && irFunction.parentClassOrNull?.isInner == true) dispatchReceiver.loadOuterClassesInto(callStack)
// 6. load up values onto stack
if (dispatchReceiver is StateWithClosure) callStack.loadUpValues(dispatchReceiver)
if (extensionReceiver is StateWithClosure) callStack.loadUpValues(extensionReceiver)
if (irFunction.isLocal) callStack.copyUpValuesFromPreviousFrame()
callInterceptor.interceptCall(call, irFunction, args) {
callStack.addInstruction(CompoundInstruction(irFunction))
}
@@ -259,6 +260,8 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
callStack.dropSubFrame()
callStack.newFrame(constructor)
callStack.addInstruction(SimpleInstruction(constructor))
if (irClass.isLocal) callStack.loadUpValues(objectState as StateWithClosure)
callStack.addVariable(Variable(constructorCall.getThisReceiver(), objectState))
constructor.valueParameters.forEachIndexed { i, param -> callStack.addVariable(Variable(param.symbol, valueArguments[i])) }
@@ -274,16 +277,15 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
if (outerClass != null) {
val outerClassVar = Variable(irClass.parentAsClass.thisReceiver!!.symbol, outerClass)
(objectState as Complex).outerClass = outerClassVar
if (superReceiver != receiverSymbol) {
if (superReceiver?.owner?.type != receiverSymbol.owner.type) {
// This check is needed to test that this inner class is not subclass of its outer.
// If it is true and if we add the next symbol, it will interfere with super symbol in memory.
// In other case, we need this variable when inner class has inner super class.
callStack.addVariable(Variable(receiverSymbol, outerClass))
// used to get information from outer class
objectState.loadOuterClassesInto(callStack, constructorCall.getThisReceiver())
}
// used to get information from outer class
objectState.loadOuterClassesInto(callStack)
}
if (irClass.isLocal) callStack.loadUpValues(objectState as StateWithClosure)
callInterceptor.interceptConstructor(constructorCall, valueArguments) {
callStack.addInstruction(CompoundInstruction(constructor))
@@ -183,7 +183,11 @@ internal class CallStack {
fun storeUpValues(state: StateWithClosure) {
// TODO save only necessary declarations
state.upValues.addAll(currentFrame.getAll().toMutableList())
currentFrame.getAll().reversed().forEach { variable ->
// TODO replace list with map
val index = state.upValues.indexOfFirst { it.symbol == variable.symbol }
if (index == -1) state.upValues.add(variable) else state.upValues[index] = variable
}
}
fun loadUpValues(state: StateWithClosure) {
@@ -123,7 +123,7 @@ internal class SubFrame(val owner: IrElement) {
// Methods to work with memory
fun addVariable(variable: Variable) {
memory += variable
memory.add(0, variable)
}
private fun getVariable(symbol: IrSymbol): Variable? = memory.firstOrNull { it.symbol == symbol }
@@ -5,14 +5,12 @@
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.overrides
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
@@ -48,8 +46,20 @@ internal interface Complex : State {
return (irFunction as IrSimpleFunction).resolveFakeOverride()
}
fun loadOuterClassesInto(callStack: CallStack) {
fun loadOuterClassesInto(callStack: CallStack, receiver: IrValueSymbol? = null) {
fun <T> List<T>.takeFromEndWhile(predicate: (T) -> Boolean): List<T> {
val list = mutableListOf<T>()
for (i in this.lastIndex downTo 0) {
if (!predicate(this[i]))
break
list.add(this[i])
}
return list
}
generateSequence(outerClass) { (it.state as? Complex)?.outerClass }
.toList()
.takeFromEndWhile { receiver == null || it.symbol != receiver } // only state's below receiver must be loaded on stack
.forEach { variable ->
callStack.addVariable(variable)
(variable.state as? StateWithClosure)?.let { callStack.loadUpValues(it) }