Implement simple cache for dynamically created wrapper for defaults

This commit is contained in:
Ivan Kylchik
2021-07-27 01:07:21 +03:00
committed by TeamCityServer
parent 09c31b0900
commit 3fc5405d57
3 changed files with 52 additions and 20 deletions
@@ -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<IrValueDeclaration?>(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<IrVariable>() + defaultFun.createReturn(callWithAllArgs)).wrapWithBlockBody()
environment.cachedFunctionsWithDefaults[ownerWithDefaults.symbol] = defaultFun.symbol
val callToDefault = defaultFun.createCall().apply { expression.copyArgsInto(this) }
callStack.addInstruction(CompoundInstruction(callToDefault))
} else {
@@ -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<IrClass>()
internal var mapOfEnums = mutableMapOf<IrSymbol, Complex>()
internal var mapOfObjects = mutableMapOf<IrSymbol, Complex>()
internal var cachedFunctionsWithDefaults = mutableMapOf<IrFunctionSymbol, IrFunctionSymbol>()
internal var cachedLambdasAndReferences = mutableMapOf<IrFunctionSymbol, IrFunctionSymbol>()
init {
@@ -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) }
}
.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) }
}