From d41c6e900acb5410cae533a710efd4585f1f118f Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Thu, 17 Jun 2021 18:30:27 +0300 Subject: [PATCH] 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. --- .../kotlin/ir/interpreter/IrInterpreter.kt | 26 ++++++++++--------- .../kotlin/ir/interpreter/stack/CallStack.kt | 6 ++++- .../kotlin/ir/interpreter/stack/Frame.kt | 2 +- .../kotlin/ir/interpreter/state/Complex.kt | 20 ++++++++++---- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index 5df049130d7..46c183aeef1 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -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)) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/CallStack.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/CallStack.kt index d0f03115039..d9cc2bad7a2 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/CallStack.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/CallStack.kt @@ -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) { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/Frame.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/Frame.kt index 11d9481f79b..a0da26b4ae2 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/Frame.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/stack/Frame.kt @@ -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 } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt index 7225e100960..9d8875a8be3 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt @@ -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 List.takeFromEndWhile(predicate: (T) -> Boolean): List { + val list = mutableListOf() + 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) }