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 9ee548aad69..bb3a39d6ba5 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 @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.interpreter.stack.Variable import org.jetbrains.kotlin.ir.interpreter.state.* import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.isUnit +import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name @@ -35,10 +36,10 @@ internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEn null -> return is IrSimpleFunction -> unfoldFunction(element, environment) is IrConstructor -> unfoldConstructor(element, callStack) - is IrCall -> unfoldValueParameters(element, callStack) - is IrConstructorCall -> unfoldValueParameters(element, callStack) - is IrEnumConstructorCall -> unfoldValueParameters(element, callStack) - is IrDelegatingConstructorCall -> unfoldValueParameters(element, callStack) + is IrCall -> unfoldValueParameters(element, environment) + is IrConstructorCall -> unfoldValueParameters(element, environment) + is IrEnumConstructorCall -> unfoldValueParameters(element, environment) + is IrDelegatingConstructorCall -> unfoldValueParameters(element, environment) is IrInstanceInitializerCall -> unfoldInstanceInitializerCall(element, callStack) is IrField -> unfoldField(element, callStack) is IrBody -> unfoldBody(element, callStack) @@ -108,13 +109,19 @@ private fun unfoldConstructor(constructor: IrConstructor, callStack: CallStack) } } -private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callStack: CallStack) { +private fun unfoldValueParameters(expression: IrFunctionAccessExpression, environment: IrInterpreterEnvironment) { + val callStack = environment.callStack val hasDefaults = (0 until expression.valueArgumentsCount).any { expression.getValueArgument(it) == null } if (hasDefaults) { + val ownerWithDefaults = expression.getFunctionThatContainsDefaults() + environment.cachedFunctionsWithDefaults[ownerWithDefaults.symbol]?.let { + val callToDefault = it.owner.createCall().apply { expression.copyArgsInto(this) } + callStack.addInstruction(CompoundInstruction(callToDefault)) + return + } + // if some arguments are not defined, then it is necessary to create temp function where defaults will be evaluated val actualParameters = MutableList(expression.valueArgumentsCount) { null } - val ownerWithDefaults = expression.getFunctionThatContainsDefaults() - val visibility = when (expression) { is IrEnumConstructorCall, is IrDelegatingConstructorCall -> DescriptorVisibilities.LOCAL else -> ownerWithDefaults.visibility @@ -128,14 +135,20 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callSt this.dispatchReceiverParameter = ownerWithDefaults.dispatchReceiverParameter?.deepCopyWithSymbols(this) this.extensionReceiverParameter = ownerWithDefaults.extensionReceiverParameter?.deepCopyWithSymbols(this) (0 until expression.valueArgumentsCount).forEach { index -> - val parameter = ownerWithDefaults.valueParameters[index] - actualParameters[index] = if (expression.getValueArgument(index) != null) { - parameter.deepCopyWithSymbols(this).also { this.valueParameters += it } - } else { - parameter.createTempVariable().apply variable@{ - this@variable.initializer = parameter.getDefaultWithActualParameters(this@apply, actualParameters) - ?: expression.getVarargType(index)?.let { null.toIrConst(it) } // if parameter is vararg and it is missing + val originalParameter = ownerWithDefaults.valueParameters[index] + val copiedParameter = originalParameter.deepCopyWithSymbols(this) + this.valueParameters += copiedParameter + actualParameters[index] = if (copiedParameter.defaultValue != null) { + copiedParameter.type = copiedParameter.type.makeNullable() // make nullable type to keep consistency; parameter can be null if it is missing + val irGetParameter = copiedParameter.createGetValue() + val defaultInitializer = originalParameter.getDefaultWithActualParameters(this@apply, actualParameters) + ?: expression.getVarargType(index)?.let { null.toIrConst(it) } // if parameter is vararg and it is missing + + copiedParameter.createTempVariable().apply variable@{ + this@variable.initializer = environment.irBuiltIns.irIfNullThenElse(irGetParameter, defaultInitializer!!, irGetParameter) } + } else { + copiedParameter } } } @@ -146,6 +159,7 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callSt (0 until expression.valueArgumentsCount).forEach { callWithAllArgs.putValueArgument(it, actualParameters[it]?.createGetValue()) } defaultFun.body = (actualParameters.filterIsInstance() + defaultFun.createReturn(callWithAllArgs)).wrapWithBlockBody() + environment.cachedFunctionsWithDefaults[ownerWithDefaults.symbol] = defaultFun.symbol val callToDefault = defaultFun.createCall().apply { expression.copyArgsInto(this) } callStack.addInstruction(CompoundInstruction(callToDefault)) } else { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterEnvironment.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterEnvironment.kt index 15018dfc6ec..da6a56fe6c5 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterEnvironment.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterEnvironment.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.ir.interpreter import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.interpreter.stack.CallStack import org.jetbrains.kotlin.ir.interpreter.state.Common @@ -24,6 +23,7 @@ class IrInterpreterEnvironment( internal val irExceptions = mutableListOf() internal var mapOfEnums = mutableMapOf() internal var mapOfObjects = mutableMapOf() + internal var cachedFunctionsWithDefaults = mutableMapOf() internal var cachedLambdasAndReferences = mutableMapOf() init { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrTreeBuildUtils.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrTreeBuildUtils.kt index f9afeadc427..6a06a1bbe59 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrTreeBuildUtils.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrTreeBuildUtils.kt @@ -99,9 +99,9 @@ internal fun State.toIrExpression(expression: IrExpression): IrExpression { } } -internal fun IrFunction.createCall(): IrCall { +internal fun IrFunction.createCall(origin: IrStatementOrigin? = null): IrCall { this as IrSimpleFunction - return IrCallImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol, typeParameters.size, valueParameters.size) + return IrCallImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol, typeParameters.size, valueParameters.size, origin) } internal fun IrConstructor.createConstructorCall(): IrConstructorCall { @@ -166,9 +166,27 @@ internal fun IrFunctionAccessExpression.shallowCopy(copyTypeArguments: Boolean = } internal fun IrFunctionAccessExpression.copyArgsInto(newCall: IrFunctionAccessExpression) { + val symbol = this.symbol.owner newCall.dispatchReceiver = this.dispatchReceiver newCall.extensionReceiver = this.extensionReceiver (0 until this.valueArgumentsCount) - .mapNotNull { this.getValueArgument(it) } - .forEachIndexed { i, arg -> newCall.putValueArgument(i, arg) } -} \ No newline at end of file + .map { this.getValueArgument(it) } + .forEachIndexed { i, arg -> + newCall.putValueArgument(i, arg ?: IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, symbol.valueParameters[i].type)) + } +} + +internal fun IrBuiltIns.irEquals(arg1: IrExpression, arg2: IrExpression): IrCall { + val equalsCall = this.eqeqSymbol.owner.createCall(IrStatementOrigin.EQEQ) + equalsCall.putValueArgument(0, arg1) + equalsCall.putValueArgument(1, arg2) + return equalsCall +} + +internal fun IrBuiltIns.irIfNullThenElse(nullableArg: IrExpression, ifTrue: IrExpression, ifFalse: IrExpression): IrWhen { + val nullCondition = this.irEquals(nullableArg, IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, this.anyNType)) + val trueBranch = IrBranchImpl(nullCondition, ifTrue) // use default + val elseBranch = IrElseBranchImpl(IrConstImpl.constTrue(0, 0, this.booleanType), ifFalse) + + return IrIfThenElseImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, ifTrue.type).apply { branches += listOf(trueBranch, elseBranch) } +}