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 32d29a10a1e..791a41417d5 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 @@ -113,15 +113,15 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro val callStack = environment.callStack val hasDefaults = (0 until expression.valueArgumentsCount).any { expression.getValueArgument(it) == null } if (hasDefaults) { - val ownerWithDefaults = expression.getFunctionThatContainsDefaults() - environment.createdFunctionsCache[ownerWithDefaults.symbol]?.let { - val callToDefault = it.owner.createCall().apply { expression.copyArgsInto(this) } + environment.getCachedFunction(expression.symbol, fromDelegatingCall = expression is IrDelegatingConstructorCall)?.let { + val callToDefault = it.owner.createCall().apply { environment.irBuiltIns.copyArgs(expression, 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 @@ -138,14 +138,15 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro val originalParameter = ownerWithDefaults.valueParameters[index] val copiedParameter = originalParameter.deepCopyWithSymbols(this) this.valueParameters += copiedParameter - actualParameters[index] = if (copiedParameter.defaultValue != null) { + actualParameters[index] = if (copiedParameter.defaultValue != null || copiedParameter.isVararg) { copiedParameter.type = copiedParameter.type.makeNullable() // make nullable type to keep consistency; parameter can be null if it is missing val irGetParameter = copiedParameter.createGetValue() + // if parameter is vararg and it is missing then create constructor call for empty array val defaultInitializer = originalParameter.getDefaultWithActualParameters(this@apply, actualParameters) - ?: expression.getVarargType(index)?.let { null.toIrConst(it) } // if parameter is vararg and it is missing + ?: environment.irBuiltIns.emptyArrayConstructor(expression.getVarargType(index)!!.getTypeIfReified(callStack)) copiedParameter.createTempVariable().apply variable@{ - this@variable.initializer = environment.irBuiltIns.irIfNullThenElse(irGetParameter, defaultInitializer!!, irGetParameter) + this@variable.initializer = environment.irBuiltIns.irIfNullThenElse(irGetParameter, defaultInitializer, irGetParameter) } } else { copiedParameter @@ -159,8 +160,9 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro (0 until expression.valueArgumentsCount).forEach { callWithAllArgs.putValueArgument(it, actualParameters[it]?.createGetValue()) } defaultFun.body = (actualParameters.filterIsInstance() + defaultFun.createReturn(callWithAllArgs)).wrapWithBlockBody() - environment.createdFunctionsCache[ownerWithDefaults.symbol] = defaultFun.symbol - val callToDefault = defaultFun.createCall().apply { expression.copyArgsInto(this) } + val callToDefault = environment.setCachedFunction( + expression.symbol, fromDelegatingCall = expression is IrDelegatingConstructorCall, newFunction = defaultFun.symbol + ).owner.createCall().apply { environment.irBuiltIns.copyArgs(expression, this) } callStack.addInstruction(CompoundInstruction(callToDefault)) } else { val irFunction = expression.symbol.owner 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 bd948f0f548..a617f1f50d6 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 @@ -142,12 +142,7 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal val irFunction = valueParameter.parent as IrFunction fun isReceiver() = irFunction.dispatchReceiverParameter == valueParameter || irFunction.extensionReceiverParameter == valueParameter - val result = callStack.popState() - val state = when { - // if vararg is empty - result.isNull() && valueParameter.isVararg -> listOf().toPrimitiveStateArray((result as Primitive<*>).type) - else -> result - } + val state = callStack.popState() state.checkNullability(valueParameter.type, environment) { if (isReceiver()) return@checkNullability NullPointerException() 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 d3c2bb97a42..acbc67b78b2 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 @@ -23,7 +23,20 @@ class IrInterpreterEnvironment( internal val irExceptions = mutableListOf() internal var mapOfEnums = mutableMapOf() internal var mapOfObjects = mutableMapOf() - internal var createdFunctionsCache = mutableMapOf() + + private data class CacheFunctionSignature( + val symbol: IrFunctionSymbol, + + // must create different invoke function for function expression with and without receivers + val hasDispatchReceiver: Boolean, + val hasExtensionReceiver: Boolean, + + // must create different default functions for constructor call and delegating call; + // their symbols are the same but calls are different, so default function must return different calls + val fromDelegatingCall: Boolean + ) + + private var functionCache = mutableMapOf() init { mapOfObjects[irBuiltIns.unitClass] = Common(irBuiltIns.unitClass.owner) @@ -47,4 +60,24 @@ class IrInterpreterEnvironment( fun copyWithNewCallStack(): IrInterpreterEnvironment { return IrInterpreterEnvironment(this) } + + internal fun getCachedFunction( + symbol: IrFunctionSymbol, + hasDispatchReceiver: Boolean = false, + hasExtensionReceiver: Boolean = false, + fromDelegatingCall: Boolean = false + ): IrFunctionSymbol? { + return functionCache[CacheFunctionSignature(symbol, hasDispatchReceiver, hasExtensionReceiver, fromDelegatingCall)] + } + + internal fun setCachedFunction( + symbol: IrFunctionSymbol, + hasDispatchReceiver: Boolean = false, + hasExtensionReceiver: Boolean = false, + fromDelegatingCall: Boolean = false, + newFunction: IrFunctionSymbol + ): IrFunctionSymbol { + functionCache[CacheFunctionSignature(symbol, hasDispatchReceiver, hasExtensionReceiver, fromDelegatingCall)] = newFunction + return newFunction + } } 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 756085a175e..7dc9bd97aee 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 @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl -import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* @@ -28,6 +27,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET +import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.name.Name @@ -104,8 +104,8 @@ internal fun IrFunction.createCall(origin: IrStatementOrigin? = null): IrCall { return IrCallImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol, typeParameters.size, valueParameters.size, origin) } -internal fun IrConstructor.createConstructorCall(): IrConstructorCall { - return IrConstructorCallImpl.fromSymbolOwner(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol) +internal fun IrConstructor.createConstructorCall(irType: IrType = returnType): IrConstructorCall { + return IrConstructorCallImpl.fromSymbolOwner(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, irType, symbol) } internal fun IrValueDeclaration.createGetValue(): IrGetValue { @@ -165,14 +165,13 @@ 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) - .map { this.getValueArgument(it) } +internal fun IrBuiltIns.copyArgs(from: IrFunctionAccessExpression, into: IrFunctionAccessExpression) { + into.dispatchReceiver = from.dispatchReceiver + into.extensionReceiver = from.extensionReceiver + (0 until from.valueArgumentsCount) + .map { from.getValueArgument(it) } .forEachIndexed { i, arg -> - newCall.putValueArgument(i, arg ?: IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, symbol.valueParameters[i].type)) + into.putValueArgument(i, arg ?: IrConstImpl.constNull(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, this.anyNType)) } } @@ -186,7 +185,24 @@ internal fun IrBuiltIns.irEquals(arg1: IrExpression, arg2: IrExpression): IrCall 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) + val elseBranch = IrElseBranchImpl(IrConstImpl.constTrue(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, this.booleanType), ifFalse) return IrIfThenElseImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, ifTrue.type).apply { branches += listOf(trueBranch, elseBranch) } } + +internal fun IrBuiltIns.emptyArrayConstructor(arrayType: IrType): IrConstructorCall { + val arrayClass = arrayType.classOrNull!!.owner + val constructor = arrayClass.constructors.firstOrNull { it.valueParameters.size == 1 } ?: arrayClass.constructors.first() + val constructorCall = constructor.createConstructorCall(arrayType) + + constructorCall.putValueArgument(0, 0.toIrConst(this.intType)) + if (constructor.valueParameters.size == 2) { + // TODO find a way to avoid creation of empty lambda + val tempFunction = createTempFunction(Name.identifier("TempForVararg"), this.anyType) + tempFunction.parent = arrayClass // can be anything, will not be used in any case + val initLambda = IrFunctionExpressionImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, constructor.valueParameters[1].type, tempFunction, IrStatementOrigin.LAMBDA) + constructorCall.putValueArgument(1, initLambda) + constructorCall.putTypeArgument(0, (arrayType as IrSimpleType).arguments.singleOrNull()?.typeOrNull) + } + return constructorCall +} 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 3d01744b32e..7beb8468f1c 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 @@ -33,10 +33,7 @@ val evaluateIntrinsicAnnotation = FqName("kotlin.EvaluateIntrinsic") val contractsDslAnnotation = FqName("kotlin.internal.ContractsDsl") internal val IrElement.fqName: String - get() = when (this) { - is IrExpression -> (this as? IrSymbolOwner)?.symbol?.owner?.fqName ?: "" - else -> (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "" - } + get() = (this as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "" internal fun IrFunction.getDispatchReceiver(): IrValueParameterSymbol? = this.dispatchReceiverParameter?.symbol diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/reflection/KFunctionState.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/reflection/KFunctionState.kt index 48d5a19a965..4d2aaa8700c 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/reflection/KFunctionState.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/reflection/KFunctionState.kt @@ -36,16 +36,14 @@ internal class KFunctionState( private var _returnType: KType? = null private var _typeParameters: List? = null - val invokeSymbol: IrFunctionSymbol = environment.createdFunctionsCache - .getOrDefault( - irFunction.symbol, - createInvokeFunction( - irFunction, - irClass, - irFunction.dispatchReceiverParameter?.let { getField(it.symbol) } != null, - irFunction.extensionReceiverParameter?.let { getField(it.symbol) } != null - ).symbol + val invokeSymbol: IrFunctionSymbol = run { + val hasDispatchReceiver = irFunction.dispatchReceiverParameter?.let { getField(it.symbol) } != null + val hasExtensionReceiver = irFunction.extensionReceiverParameter?.let { getField(it.symbol) } != null + environment.getCachedFunction(irFunction.symbol, hasDispatchReceiver, hasExtensionReceiver) ?: environment.setCachedFunction( + irFunction.symbol, hasDispatchReceiver, hasExtensionReceiver, + newFunction = createInvokeFunction(irFunction, irClass, hasDispatchReceiver, hasExtensionReceiver).symbol ) + } companion object { fun createInvokeFunction(