Fix interpretation of default varargs
This commit is contained in:
committed by
TeamCityServer
parent
0ce3dd117e
commit
3932acf843
+10
-8
@@ -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<IrValueDeclaration?>(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<IrVariable>() + 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
|
||||
|
||||
@@ -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<Any?>().toPrimitiveStateArray((result as Primitive<*>).type)
|
||||
else -> result
|
||||
}
|
||||
val state = callStack.popState()
|
||||
|
||||
state.checkNullability(valueParameter.type, environment) {
|
||||
if (isReceiver()) return@checkNullability NullPointerException()
|
||||
|
||||
+34
-1
@@ -23,7 +23,20 @@ class IrInterpreterEnvironment(
|
||||
internal val irExceptions = mutableListOf<IrClass>()
|
||||
internal var mapOfEnums = mutableMapOf<IrSymbol, Complex>()
|
||||
internal var mapOfObjects = mutableMapOf<IrSymbol, Complex>()
|
||||
internal var createdFunctionsCache = mutableMapOf<IrFunctionSymbol, IrFunctionSymbol>()
|
||||
|
||||
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<CacheFunctionSignature, IrFunctionSymbol>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+27
-11
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+7
-9
@@ -36,16 +36,14 @@ internal class KFunctionState(
|
||||
private var _returnType: KType? = null
|
||||
private var _typeParameters: List<KTypeParameter>? = 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(
|
||||
|
||||
Reference in New Issue
Block a user