From b4463b948cd19b2d4cd8f042cf9a2f816debc6c1 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Mon, 12 Apr 2021 14:04:01 +0300 Subject: [PATCH] Rewrite unfoldValueParameters to take into account recursive calls The workflow of evaluation value arguments is now like this: 1. Calculate not null arguments 2. Add them into intermediate stack 3. Calculate defaults for those parameters that have no argument This flow allow us to avoid early addition of variable onto stack. --- .../ir/interpreter/InstructionsUnfolder.kt | 114 ++++++++---------- .../kotlin/ir/interpreter/IrInterpreter.kt | 23 ++-- .../jetbrains/kotlin/ir/interpreter/Utils.kt | 5 - .../kotlin/ir/interpreter/stack/CallStack.kt | 2 +- 4 files changed, 61 insertions(+), 83 deletions(-) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt index 716436ee6b5..2594e35ad72 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt @@ -83,84 +83,67 @@ private fun unfoldConstructor(constructor: IrConstructor, callStack: CallStack) } private fun unfoldCall(call: IrCall, callStack: CallStack) { - val function = call.symbol.owner - // new sub frame is used to store value arguments, in case then they are used in default args evaluation - callStack.newSubFrame(call, listOf()) - callStack.addInstruction(SimpleInstruction(call)) unfoldValueParameters(call, callStack) - - // must save receivers in memory in case then they are used in default args evaluation - call.extensionReceiver?.let { - callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!)) - callStack.addInstruction(CompoundInstruction(it)) - } - call.dispatchReceiver?.let { - callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!)) - callStack.addInstruction(CompoundInstruction(it)) - } } private fun unfoldConstructorCall(constructorCall: IrFunctionAccessExpression, callStack: CallStack) { val constructor = constructorCall.symbol.owner - callStack.newSubFrame(constructorCall, listOf()) // used to store value arguments, in case then they are use as default args + unfoldValueParameters(constructorCall, callStack) // this variable is used to create object once callStack.addVariable(Variable(constructorCall.getThisReceiver(), Common(constructor.parentAsClass))) - callStack.addInstruction(SimpleInstruction(constructorCall)) - unfoldValueParameters(constructorCall, callStack) - - constructorCall.dispatchReceiver?.let { - callStack.addInstruction(SimpleInstruction(constructor.dispatchReceiverParameter!!)) - callStack.addInstruction(CompoundInstruction(it)) - } } private fun unfoldEnumConstructorCall(enumConstructorCall: IrEnumConstructorCall, callStack: CallStack) { - callStack.newSubFrame(enumConstructorCall, listOf()) // used to store value arguments, in case then they are use as default args - callStack.addInstruction(SimpleInstruction(enumConstructorCall)) unfoldValueParameters(enumConstructorCall, callStack) } private fun unfoldDelegatingConstructorCall(delegatingConstructorCall: IrFunctionAccessExpression, callStack: CallStack) { - callStack.newSubFrame(delegatingConstructorCall, listOf()) // used to store value arguments, in case then they are use as default args - callStack.addInstruction(SimpleInstruction(delegatingConstructorCall)) unfoldValueParameters(delegatingConstructorCall, callStack) } private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callStack: CallStack) { - fun IrValueParameter.getDefault(): IrExpressionBody? { - return defaultValue - ?: (this.parent as? IrSimpleFunction)?.overriddenSymbols - ?.map { it.owner.valueParameters[this.index].getDefault() } - ?.firstNotNullOfOrNull { it } - } + val irFunction = expression.symbol.owner + // new sub frame is used to store value arguments, in case then they are used in default args evaluation + callStack.newSubFrame(expression, listOf(SimpleInstruction(expression))) fun getDefaultForParameterAt(index: Int): IrExpression? { - return expression.symbol.owner.valueParameters[index].getDefault()?.expression - } - - val irFunction = expression.symbol.owner - val valueParametersSymbols = irFunction.valueParameters.map { it.symbol } - val valueArguments = (0 until expression.valueArgumentsCount).map { expression.getValueArgument(it) } - - for (i in valueParametersSymbols.indices.reversed()) { - callStack.addInstruction(SimpleInstruction(valueParametersSymbols[i].owner)) - val arg = valueArguments[i] ?: getDefaultForParameterAt(i) - when { - arg != null -> callStack.addInstruction(CompoundInstruction(arg)) - else -> - // case when value parameter is vararg and it is missing - expression.getVarargType(i)?.let { - callStack.addInstruction(SimpleInstruction(IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it))) - } + fun IrValueParameter.getDefault(): IrExpressionBody? { + return defaultValue + ?: (this.parent as? IrSimpleFunction)?.overriddenSymbols + ?.map { it.owner.valueParameters[this.index].getDefault() } + ?.firstNotNullOfOrNull { it } } + + return irFunction.valueParameters[index].getDefault()?.expression } - // hack for extension receiver in lambda - if (expression.valueArgumentsCount != irFunction.valueParameters.size) { - val extensionReceiver = irFunction.getExtensionReceiver() ?: return - callStack.addInstruction(SimpleInstruction(extensionReceiver.owner)) - valueArguments[0]?.let { callStack.addInstruction(CompoundInstruction(it)) } + val valueParametersSymbols = irFunction.valueParameters.map { it.symbol } + + // interpret defaults for null arguments (at the end) + (expression.valueArgumentsCount - 1 downTo 0).forEach { index -> + if (expression.getValueArgument(index) != null) return@forEach + callStack.addInstruction(SimpleInstruction(valueParametersSymbols[index].owner)) + + getDefaultForParameterAt(index) + ?.let { callStack.addInstruction(CompoundInstruction(it)) } + ?: expression.getVarargType(index)?.let { // case when value parameter is vararg and it is missing + callStack.addInstruction(SimpleInstruction(IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it))) + } } + + // interpret not null arguments (after evaluation of arguments) + expression.dispatchReceiver?.let { callStack.addInstruction(SimpleInstruction(irFunction.dispatchReceiverParameter!!)) } + expression.extensionReceiver?.let { callStack.addInstruction(SimpleInstruction(irFunction.extensionReceiverParameter!!)) } + (0 until expression.valueArgumentsCount).forEach { + if (expression.getValueArgument(it) != null) callStack.addInstruction(SimpleInstruction(irFunction.valueParameters[it])) + } + + // evaluate arguments + (expression.valueArgumentsCount - 1 downTo 0).forEach { + callStack.addInstruction(CompoundInstruction(expression.getValueArgument(it))) + } + expression.extensionReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) } + expression.dispatchReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) } } private fun unfoldInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall, callStack: CallStack) { @@ -360,7 +343,7 @@ private fun unfoldStringConcatenation(expression: IrStringConcatenation, environ toStringCall.dispatchReceiver = IrConstImpl.constNull(0, 0, receiver.type) // just stub receiver callStack.newSubFrame(toStringCall, listOf(SimpleInstruction(toStringCall))) - callStack.pushState(state) + callStack.addVariable(Variable(receiver.symbol, state)) } } } @@ -382,22 +365,21 @@ private fun unfoldComposite(element: IrComposite, callStack: CallStack) { private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: CallStack) { val function = reference.symbol.owner - callStack.newSubFrame(reference, listOf()) - callStack.addInstruction(SimpleInstruction(reference)) + callStack.newSubFrame(reference, listOf(SimpleInstruction(reference))) - reference.extensionReceiver?.let { - callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!)) - callStack.addInstruction(CompoundInstruction(it)) - } - reference.dispatchReceiver?.let { - callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!)) - callStack.addInstruction(CompoundInstruction(it)) - } + reference.dispatchReceiver?.let { callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!)) } + reference.extensionReceiver?.let { callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!)) } + + reference.extensionReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) } + reference.dispatchReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) } } private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) { callStack.addInstruction(SimpleInstruction(propertyReference)) - callStack.addInstruction(CompoundInstruction(propertyReference.dispatchReceiver)) + propertyReference.dispatchReceiver?.let { + callStack.addInstruction(SimpleInstruction(propertyReference.getter!!.owner.dispatchReceiverParameter!!)) + callStack.addInstruction(CompoundInstruction(it)) + } } private fun unfoldClassReference(classReference: IrClassReference, callStack: CallStack) { 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 daee54f18f6..48349659811 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 @@ -163,14 +163,14 @@ class IrInterpreter private constructor( //must add value argument in current stack because it can be used later as default argument callStack.addVariable(Variable(valueParameter.symbol, state)) - callStack.pushState(state) } private fun interpretCall(call: IrCall) { + val owner = call.symbol.owner // 1. load evaluated arguments from stack - val valueArguments = call.symbol.owner.valueParameters.map { callStack.popState() }.reversed() - val extensionReceiver = call.extensionReceiver?.let { callStack.popState() } - var dispatchReceiver = call.dispatchReceiver?.let { callStack.popState() } + var dispatchReceiver = call.dispatchReceiver?.let { callStack.getVariable(owner.getDispatchReceiver()!!) }?.state + val extensionReceiver = call.extensionReceiver?.let { callStack.getVariable(owner.getExtensionReceiver()!!) }?.state + val valueArguments = owner.valueParameters.map { callStack.getVariable(it.symbol).state } // 2. get correct function for interpretation val irFunction = dispatchReceiver?.getIrFunctionByIrCall(call) ?: call.symbol.owner @@ -179,13 +179,13 @@ class IrInterpreter private constructor( else -> dispatchReceiver } - callStack.dropSubFrame() + callStack.dropSubFrame() // drop intermediate frame that contains variables for default arg evaluation callStack.newFrame(irFunction, listOf(SimpleInstruction(irFunction))) // 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 + // 3. store arguments in memory (remap args on actual names) val args = mutableListOf() irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> args.add(Variable(it, receiver)) } } irFunction.getExtensionReceiver()?.let { args.add(Variable(it, extensionReceiver ?: valueArguments.first())) } @@ -241,12 +241,12 @@ class IrInterpreter private constructor( } private fun interpretConstructorCall(constructorCall: IrFunctionAccessExpression) { - val valueArguments = constructorCall.symbol.owner.valueParameters.map { callStack.popState() }.reversed() + val valueArguments = constructorCall.symbol.owner.valueParameters.map { callStack.getVariable(it.symbol).state } val constructor = constructorCall.symbol.owner val irClass = constructor.parentAsClass val objectVar = callStack.getVariable(constructorCall.getThisReceiver()) if (irClass.isLocal) callStack.storeUpValues(objectVar.state as StateWithClosure) - val outerClass = if (irClass.isInner) callStack.popState() else null + val outerClass = if (irClass.isInner) callStack.getVariable(constructor.dispatchReceiverParameter!!.symbol).state else null callStack.dropSubFrame() callStack.newFrame(constructor, listOf(SimpleInstruction(constructor))) @@ -564,8 +564,8 @@ class IrInterpreter private constructor( val function = KFunctionState(reference) val irFunction = function.irFunction - val extensionReceiver = reference.extensionReceiver?.let { callStack.popState() } - val dispatchReceiver = reference.dispatchReceiver?.let { callStack.popState() } + val dispatchReceiver = reference.dispatchReceiver?.let { callStack.getVariable(irFunction.getDispatchReceiver()!!) }?.state + val extensionReceiver = reference.extensionReceiver?.let { callStack.getVariable(irFunction.getExtensionReceiver()!!) }?.state callStack.dropSubFrame() irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> function.fields.add(Variable(it, receiver)) } } @@ -576,7 +576,8 @@ class IrInterpreter private constructor( } private fun interpretPropertyReference(propertyReference: IrPropertyReference) { - val dispatchReceiver = propertyReference.dispatchReceiver?.let { callStack.popState() } + val dispatchReceiverSymbol = propertyReference.getter?.owner?.getDispatchReceiver() + val dispatchReceiver = propertyReference.dispatchReceiver?.let { callStack.getVariable(dispatchReceiverSymbol!!) }?.state // it is impossible to get KProperty2 through ::, so extension receiver is always null val propertyState = KPropertyState(propertyReference, dispatchReceiver) callStack.pushState(propertyState) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt index 5981888cdda..5ab6fbdf935 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt @@ -179,11 +179,6 @@ fun IrFunctionAccessExpression.getVarargType(index: Int): IrType? { } } -internal fun State?.extractNonLocalDeclarations(): List { - this ?: return listOf() - return this.fields.filter { it.symbol !is IrFieldSymbol } -} - internal fun IrFunction.getCapitalizedFileName() = this.file.name.replace(".kt", "Kt").capitalizeAsciiOnly() internal fun IrType.isUnsigned() = this.isUByte() || this.isUShort() || this.isUInt() || this.isULong() 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 ab43a53232e..536fa632ae5 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 @@ -187,7 +187,7 @@ internal class CallStack { } fun copyUpValuesFromPreviousFrame() { - frames[frames.size - 2].getAll().forEach { addVariable(it) } + frames[frames.size - 2].getAll().forEach { if (!containsVariable(it.symbol)) addVariable(it) } } fun getStackTrace(): List {