IR: enhancements in default parameter extension

- simplify argument count
- refactoring
- inner class support
This commit is contained in:
Vasily Levchenko
2017-02-03 10:32:15 +03:00
committed by vvlevchenko
parent fca160d09d
commit 2a6450446d
4 changed files with 393 additions and 382 deletions
@@ -31,7 +31,7 @@ internal class KonanLower(val context: Context) {
VarargInjectionLowering(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
DefaultParameterStubGenerator(context).runOnFilePostfix(irFile)
DefaultArgumentStubGenerator(context).runOnFilePostfix(irFile)
DefaultParameterInjector(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_BUILTIN_OPERATORS) {
@@ -1,7 +1,6 @@
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.lower.DefaultParameterDescription
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
@@ -10,7 +9,7 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
internal class Ir(val context: Context, val irModule: IrModuleFragment) {
val propertiesWithBackingFields = mutableSetOf<PropertyDescriptor>()
val defaultParameterDescriptions = mutableMapOf<FunctionDescriptor, DefaultParameterDescription>()
val defaultParameterDescriptorsCache = mutableMapOf<FunctionDescriptor, FunctionDescriptor>()
val originalModuleIndex = ModuleIndex(irModule)
@@ -0,0 +1,391 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanPlatform
import org.jetbrains.kotlin.backend.konan.ir.ir2string
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
class DefaultArgumentStubGenerator internal constructor(val context: Context): DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.declarations.transformFlat { memberDeclaration ->
if (memberDeclaration is IrFunction)
lower(memberDeclaration)
else
null
}
}
object DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER :
IrDeclarationOriginImpl("DEFAULT_PARAMETER_EXTENT")
private fun lower(irFunction: IrFunction): List<IrFunction> {
val functionDescriptor = irFunction.descriptor
if (hasNotDefaultParameters(functionDescriptor))
return irFunction.singletonList()
val bodies = functionDescriptor.valueParameters
.mapNotNull{irFunction.getDefault(it)}
log("detected ${functionDescriptor.name.asString()} has got #${bodies.size} default expressions")
functionDescriptor.overriddenDescriptors.forEach { context.log("DEFAULT-REPLACER: $it") }
if (bodies.isNotEmpty()) {
val descriptor = functionDescriptor.generateDefaultsDescription(context)
log("$functionDescriptor -> $descriptor")
val builder = context.createIrBuilder(descriptor)
val body = builder.irBlockBody(irFunction) {
val params = mutableListOf<VariableDescriptor>()
val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>()
val newExtensionReceiver =
if (descriptor.extensionReceiverParameter == null) {
null
} else {
IrGetValueImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
descriptor = descriptor.extensionReceiverParameter!!,
origin = null
)
}
for (valueParameter in functionDescriptor.valueParameters) {
val parameterDescriptor = descriptor.valueParameters[valueParameter.index]
if (valueParameter.hasDefaultValue()) {
val variable = scope.createTemporaryVariable(
irExpression = nullConst(irFunction, valueParameter.type)!!,
isMutable = true)
val variableDescriptor = variable.descriptor
params.add(variableDescriptor)
+variable
val condition = irNotEquals(irCall(Helper.kIntAnd).apply {
dispatchReceiver = irGet(maskParameterDescriptor(descriptor))
putValueArgument(0, irInt(1 shl valueParameter.index))
}, irInt(0))
val expressionBody = getDefaultParameterExpressionBody(irFunction, valueParameter)
/* Use previously calculated values in next expression. */
expressionBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
if (expression.descriptor == functionDescriptor.extensionReceiverParameter)
return newExtensionReceiver!!
if (!variables.containsKey(expression.descriptor))
return expression
return irGet(variables[expression.descriptor] as VariableDescriptor)
}
})
/* Mapping calculated values with its origin variables. */
variables.put(valueParameter, variableDescriptor)
+irIfThenElse(
type = KonanPlatform.builtIns.unitType,
condition = condition,
thenPart = irSetVar(variableDescriptor, expressionBody.expression),
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
} else {
params.add(parameterDescriptor)
}
}
if (functionDescriptor is ClassConstructorDescriptor) {
+ irReturn(IrDelegatingConstructorCallImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
descriptor = functionDescriptor
).apply {
params.forEachIndexed { i, variable ->
putValueArgument(i, irGet(variable))
}
if (functionDescriptor.dispatchReceiverParameter != null) {
dispatchReceiver = irThis()
}
})
} else {
+irReturn(irCall(functionDescriptor).apply {
if (functionDescriptor.dispatchReceiverParameter != null) {
dispatchReceiver = irThis()
}
if (functionDescriptor.extensionReceiverParameter != null) {
extensionReceiver = newExtensionReceiver
}
params.forEachIndexed { i, variable ->
putValueArgument(i, irGet(variable))
}
})
}
}
return if (functionDescriptor is ClassConstructorDescriptor)
listOf(irFunction, IrConstructorImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
descriptor = descriptor as ClassConstructorDescriptor,
origin = DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
body = body))
else
listOf(irFunction, IrFunctionImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
descriptor = descriptor,
origin = DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
body = body))
}
return irFunction.singletonList()
}
private fun log(msg:String) = context.log("DEFAULT-REPLACER: $msg")
}
private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody {
return irFunction.getDefault(valueParameter) as? IrExpressionBody ?: TODO("FIXME!!!")
}
private fun maskParameterDescriptor(descriptor: FunctionDescriptor) = descriptor.valueParameters.single { it.name == Helper.kParameterMaskName }
private fun markerParameterDescriptor(descriptor: FunctionDescriptor) = descriptor.valueParameters.single {it.name == Helper.kConstructorMarkerName}
private fun nullConst(expression: IrElement, type: KotlinType): IrExpression? {
when {
KotlinBuiltIns.isFloat(type) -> return IrConstImpl.float (expression.startOffset, expression.endOffset, type, 0.0F)
KotlinBuiltIns.isDouble(type) -> return IrConstImpl.double (expression.startOffset, expression.endOffset, type, 0.0)
KotlinBuiltIns.isBoolean(type) -> return IrConstImpl.boolean (expression.startOffset, expression.endOffset, type, false)
KotlinBuiltIns.isByte(type) -> return IrConstImpl.byte (expression.startOffset, expression.endOffset, type, 0)
KotlinBuiltIns.isChar(type) -> return IrConstImpl.char (expression.startOffset, expression.endOffset, type, 0.toChar())
KotlinBuiltIns.isShort(type) -> return IrConstImpl.short (expression.startOffset, expression.endOffset, type, 0)
KotlinBuiltIns.isInt(type) -> return IrConstImpl.int (expression.startOffset, expression.endOffset, type, 0)
KotlinBuiltIns.isLong(type) -> return IrConstImpl.long (expression.startOffset, expression.endOffset, type, 0)
else -> return IrConstImpl.constNull (expression.startOffset, expression.endOffset, type)
}
}
class DefaultParameterInjector internal constructor(val context: Context): BodyLoweringPass {
override fun lower(irBody: IrBody) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
super.visitDelegatingConstructorCall(expression)
val descriptor = expression.descriptor
if (hasNotDefaultParameters(descriptor))
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == descriptor.valueParameters.size)
return expression
val (desc, params) = parametersForCall(expression)
return IrDelegatingConstructorCallImpl(
startOffset = expression.startOffset,
endOffset = expression.endOffset,
descriptor = desc as ClassConstructorDescriptor)
.apply {
params.forEach {
log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}")
putValueArgument(it.first.index, it.second)
}
}
}
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
val functionDescriptor = expression.descriptor as FunctionDescriptor
if (hasNotDefaultParameters(functionDescriptor))
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == functionDescriptor.valueParameters.size)
return expression
val (descriptor, params) = parametersForCall(expression)
descriptor.typeParameters.forEach { log("$descriptor [${it.index}]: $it") }
descriptor.original.typeParameters.forEach { log("${descriptor.original}[${it.index}] : $it") }
return IrCallImpl(
startOffset = expression.startOffset,
endOffset = expression.endOffset,
descriptor = descriptor,
typeArguments = expression.descriptor.typeParameters.map{it to (expression.getTypeArgument(it) ?: it.defaultType) }.toMap())
.apply {
params.forEach {
log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}")
putValueArgument(it.first.index, it.second)
}
expression.extensionReceiver?.apply{
extensionReceiver = expression.extensionReceiver
}
expression.dispatchReceiver?.apply {
dispatchReceiver = expression.dispatchReceiver
}
log("call::extension@: ${ir2string(expression.extensionReceiver)}")
log("call::dispatch@: ${ir2string(expression.dispatchReceiver)}")
}
}
private fun parametersForCall(expression: IrMemberAccessExpression): Pair<FunctionDescriptor, List<Pair<ValueParameterDescriptor, IrExpression?>>> {
val descriptor = expression.descriptor as FunctionDescriptor
val keyDescriptor = if (DescriptorUtils.isOverride(descriptor))
DescriptorUtils.getAllOverriddenDescriptors(descriptor).first()
else
descriptor.original
val realDescriptor = keyDescriptor.generateDefaultsDescription(context)
log("$descriptor -> $realDescriptor")
var maskValue = 0
val params = mutableListOf<Pair<ValueParameterDescriptor, IrExpression?>>()
params.addAll(descriptor.valueParameters.mapIndexed { i, _ ->
val valueArgument = expression.getValueArgument(i)
if (valueArgument == null) maskValue = maskValue or (1 shl i)
val valueParameterDescriptor = realDescriptor.valueParameters[i]
val pair = valueParameterDescriptor to (valueArgument ?: nullConst(expression, valueParameterDescriptor.type))
return@mapIndexed pair
})
params += maskParameterDescriptor(realDescriptor) to IrConstImpl<Int>(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
type = KonanPlatform.builtIns.intType,
kind = IrConstKind.Int,
value = maskValue)
if (expression.descriptor is ClassConstructorDescriptor) {
params += markerParameterDescriptor(realDescriptor) to IrGetObjectValueImpl(
irBody.startOffset,
irBody.endOffset,
Helper.kDefaultArgumentMarkerClassDescriptor.defaultType,
Helper.kDefaultArgumentMarkerClassDescriptor)
}
params.forEach {
log("descriptor::${realDescriptor.name.asString()}#${it.first.index}: ${it.first.name.asString()}")
}
return Pair(realDescriptor, params)
}
private fun argumentCount(expression: IrMemberAccessExpression) =
expression.descriptor.valueParameters.count { expression.getValueArgument(it) != null }
})
}
private fun log(msg: String) = context.log("DEFAULT-INJECTOR: $msg")
}
private fun hasNotDefaultParameters(descriptor: CallableDescriptor) = descriptor.valueParameters.none { it.hasDefaultValue() }
private fun FunctionDescriptor.generateDefaultsDescription(context: Context): FunctionDescriptor {
return context.ir.defaultParameterDescriptorsCache.getOrPut(this) {
val descriptor = when (this) {
is ClassConstructorDescriptor ->
ClassConstructorDescriptorImpl.create(
/* containingDeclaration = */ containingDeclaration,
/* annotations = */ annotations,
/* isPrimary = */ false,
/* source = */ SourceElement.NO_SOURCE)
is FunctionDescriptor -> {
val name = Name.identifier("$name\$default")
SimpleFunctionDescriptorImpl.create(
/* containingDeclaration = */ if (DescriptorUtils.isOverride(this)) this.overriddenDescriptors.first().containingDeclaration as ClassDescriptor else containingDeclaration,
/* annotations = */ annotations,
/* name = */ name,
/* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED,
/* source = */ SourceElement.NO_SOURCE)
}
else -> TODO("FIXME: $this")
}
val syntheticParameters = mutableListOf(valueParameter(descriptor, valueParameters.size, Helper.kParameterMaskName, KonanPlatform.builtIns.intType))
if (this is ClassConstructorDescriptor) {
syntheticParameters += valueParameter(descriptor, syntheticParameters.last().index + 1,
Helper.kConstructorMarkerName,
Helper.kDefaultArgumentMarkerClassDescriptor.defaultType)
}
descriptor.initialize(
/* receiverParameterType = */ extensionReceiverParameter?.type,
/* dispatchReceiverParameter = */ dispatchReceiverParameter,
/* typeParameters = */ typeParameters.map {
TypeParameterDescriptorImpl.createForFurtherModification(
/* containingDeclaration = */ descriptor,
/* annotations = */ it.annotations,
/* reified = */ it.isReified,
/* variance = */ it.variance,
/* name = */ it.name,
/* index = */ it.index,
/* source = */ it.source,
/* reportCycleError = */ null,
/* supertypeLoopsChecker = */ SupertypeLoopChecker.EMPTY
).apply {
it.upperBounds.forEach { addUpperBound(it) }
setInitialized()
}
},
/* unsubstitutedValueParameters = */ valueParameters.map {
ValueParameterDescriptorImpl(
containingDeclaration = descriptor,
original = null, /* ValueParameterDescriptorImpl::copy do not save original. */
index = it.index,
annotations = it.annotations,
name = it.name,
outType = it.type,
declaresDefaultValue = false,
isCrossinline = it.isCrossinline,
isNoinline = it.isNoinline,
varargElementType = it.varargElementType,
source = it.source)
} + syntheticParameters,
/* unsubstitutedReturnType = */ returnType,
/* modality = */ Modality.FINAL,
/* visibility = */ this.visibility)
context.log("adds to cache[$this] = $descriptor")
descriptor
}
}
private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: Name, type: KotlinType):ValueParameterDescriptor {
return ValueParameterDescriptorImpl(
containingDeclaration = descriptor,
original = null,
index = index,
annotations = Annotations.EMPTY,
name = name,
outType = type,
declaresDefaultValue = false,
isCrossinline = false,
isNoinline = false,
varargElementType = null,
source = SourceElement.NO_SOURCE
)
}
internal object Helper {
val kIntDesctiptor = DescriptorUtils.getClassDescriptorForType(KonanPlatform.builtIns.intType)
val kIntAnd = DescriptorUtils.getFunctionByName(kIntDesctiptor.unsubstitutedMemberScope, Name.identifier("and"))
val kKonanInternalPackageDescriptor = KonanPlatform.builtIns.builtInsModule.getPackage(FqName("konan.internal"))
val kDefaultArgumentMarkerName = Name.identifier("DefaultArgumentMarker")
val kParameterMaskName = Name.identifier("__\$mask\$__")
val kConstructorMarkerName = Name.identifier("__\$marker\$__")
val kDefaultArgumentMarkerClassDescriptor = DescriptorUtils.getAllDescriptors(kKonanInternalPackageDescriptor.memberScope)
.first{ it is ClassDescriptor && it.name == kDefaultArgumentMarkerName} as ClassDescriptor
}
@@ -1,379 +0,0 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanPlatform
import org.jetbrains.kotlin.backend.konan.ir.ir2string
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
class DefaultParameterStubGenerator internal constructor(val context: Context): DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.declarations.transformFlat { memberDeclaration ->
if (memberDeclaration is IrFunction)
lower(memberDeclaration)
else
null
}
}
object DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER :
IrDeclarationOriginImpl("DEFAULT_PARAMETER_EXTENT")
private fun lower(irFunction: IrFunction): List<IrFunction> {
val bodies = mutableListOf<IrExpressionBody>()
val functionDescriptor = irFunction.descriptor
if (hasNotDefaultParameters(functionDescriptor))
return irFunction.singletonList()
irFunction.acceptChildrenVoid(object:IrElementVisitorVoid{
override fun visitExpressionBody(body: IrExpressionBody) {
bodies.add(body)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
})
log("detected ${functionDescriptor.name.asString()} has got #${bodies.size} default expressions")
functionDescriptor.overriddenDescriptors.forEach { context.log("DEFAULT-REPLACER: $it") }
if (bodies.isNotEmpty()) {
val description = functionDescriptor.getOrCreateDefaultsDescription(context)
val builder = context.createIrBuilder(description.function)
val body = builder.irBlockBody(irFunction) {
val params = mutableListOf<VariableDescriptor>()
val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>()
val newExtensionReceiver =
if (description.function.extensionReceiverParameter == null) {
null
} else {
IrGetValueImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
descriptor = description.function.extensionReceiverParameter!!,
origin = null
)
}
for (valueParameter in functionDescriptor.valueParameters) {
val parameterDescriptor = description.function.valueParameters[valueParameter.index]
if (valueParameter.hasDefaultValue()) {
val variable = scope.createTemporaryVariable(
irExpression = nullConst(valueParameter.type)!!,
isMutable = true)
val variableDescriptor = variable.descriptor
params.add(variableDescriptor)
+variable
val condition = irNotEquals(irCall(intAnd).apply {
dispatchReceiver = irGet(description.mask)
putValueArgument(0, irInt(1 shl valueParameter.index))
}, irInt(0))
val exprBody = getDefaultParameterExpressionBody(irFunction, valueParameter)
/* Use previously calculated values in next expression. */
exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
if (expression.descriptor == functionDescriptor.extensionReceiverParameter)
return newExtensionReceiver!!
if (!variables.containsKey(expression.descriptor))
return expression
return irGet(variables[expression.descriptor] as VariableDescriptor)
}
})
/* Mapping calculated values with its origin variables. */
variables.put(valueParameter, variableDescriptor)
+irIfThenElse(
type = KonanPlatform.builtIns.unitType,
condition = condition,
thenPart = irSetVar(variableDescriptor, exprBody.expression),
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
} else {
params.add(parameterDescriptor)
}
}
if (functionDescriptor !is ClassConstructorDescriptor) {
+irReturn(irCall(functionDescriptor).apply {
if (functionDescriptor.dispatchReceiverParameter != null) {
dispatchReceiver = irThis()
}
if (functionDescriptor.extensionReceiverParameter != null) {
extensionReceiver = newExtensionReceiver
}
params.forEachIndexed { i, variable ->
putValueArgument(i, irGet(variable))
}
})
} else {
+ irReturn(IrDelegatingConstructorCallImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
descriptor = functionDescriptor
).apply {
params.forEachIndexed { i, variable ->
putValueArgument(i, irGet(variable))
}
})
}
}
// TODO: replace irFunction with new one without expression bodies.
return listOf(irFunction, IrFunctionImpl(
irFunction.startOffset,
irFunction.endOffset,
DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
description.function, body))
}
return irFunction.singletonList()
}
private fun log(msg:String) = context.log("DEFAULT-REPLACER: $msg")
}
private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody {
return irFunction.getDefault(valueParameter) as? IrExpressionBody ?: TODO("FIXME!!!")
}
private fun nullConst(type: KotlinType): IrExpression? {
when {
KotlinBuiltIns.isFloat(type) -> return IrConstImpl<Float> (0, 0, type, IrConstKind.Float, 0.0F)
KotlinBuiltIns.isDouble(type) -> return IrConstImpl<Double> (0, 0, type, IrConstKind.Double, 0.0)
KotlinBuiltIns.isBoolean(type) -> return IrConstImpl<Boolean> (0, 0, type, IrConstKind.Boolean, false)
KotlinBuiltIns.isByte(type) -> return IrConstImpl<Byte> (0, 0, type, IrConstKind.Byte, 0)
KotlinBuiltIns.isShort(type) -> return IrConstImpl<Short> (0, 0, type, IrConstKind.Short, 0)
KotlinBuiltIns.isInt(type) -> return IrConstImpl<Int> (0, 0, type, IrConstKind.Int, 0)
KotlinBuiltIns.isLong(type) -> return IrConstImpl<Long> (0, 0, type, IrConstKind.Long, 0)
else -> return IrConstImpl<Nothing?>(0, 0, type, IrConstKind.Null, null)
}
}
class DefaultParameterInjector internal constructor(val context: Context): BodyLoweringPass {
override fun lower(irBody: IrBody) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
super.visitDelegatingConstructorCall(expression)
val descriptor = expression.descriptor
if (hasNotDefaultParameters(descriptor))
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == descriptor.valueParameters.size)
return expression
val (desc, params) = parametersForCall(expression)
return IrDelegatingConstructorCallImpl(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
descriptor = desc.function as ClassConstructorDescriptor)
.apply {
params.forEach {
log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}")
putValueArgument(it.first.index, it.second)
}
}
}
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
val functionDescriptor = expression.descriptor as FunctionDescriptor
if (hasNotDefaultParameters(functionDescriptor))
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == functionDescriptor.valueParameters.size)
return expression
val (desc, params) = parametersForCall(expression)
return IrCallImpl(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
type = desc.function.returnType!!,
descriptor = desc.function,
typeArguments = null)
.apply {
params.forEach {
log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}")
putValueArgument(it.first.index, it.second)
}
expression.extensionReceiver?.apply{
extensionReceiver = expression.extensionReceiver
}
expression.dispatchReceiver?.apply {
dispatchReceiver = expression.dispatchReceiver
}
log("call::extension@: ${ir2string(expression.extensionReceiver)}")
log("call::dispatch@: ${ir2string(expression.dispatchReceiver)}")
}
}
private fun parametersForCall(expression: IrMemberAccessExpression): Pair<DefaultParameterDescription, List<Pair<ValueParameterDescriptor, IrExpression?>>> {
var maskValue = 0
val rawDescriptor = expression.descriptor as FunctionDescriptor
val descriptor = rawDescriptor.overriddenDescriptors.firstOrNull()?:rawDescriptor
val desc = descriptor.getOrCreateDefaultsDescription(context)
descriptor.valueParameters.forEach {
log("descriptor::${descriptor.name.asString()}#${it.index}: ${it.name.asString()}")
}
val params = descriptor.valueParameters.mapIndexed { i, _ ->
val valueArgument = expression.getValueArgument(i)
if (valueArgument == null) maskValue = maskValue or (1 shl i)
val valueParameterDescriptor = desc.function.valueParameters[i]
val pair = valueParameterDescriptor to (valueArgument ?: nullConst(valueParameterDescriptor.type))
return@mapIndexed pair
} + (desc.mask to IrConstImpl<Int>(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
type = KonanPlatform.builtIns.intType,
kind = IrConstKind.Int,
value = maskValue))
params.forEach {
log("descriptor::${desc.function.name.asString()}#${it.first.index}: ${it.first.name.asString()}")
}
return Pair(desc, params)
}
private fun argumentCount(expression: IrMemberAccessExpression): Int {
var argumentsCount = 0
expression.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
argumentsCount++
}
})
val descriptor = expression.descriptor
if (descriptor !is ConstructorDescriptor && (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null))
argumentsCount--
return argumentsCount
}
})
}
private fun log(msg: String) = context.log("DEFAULT-INJECTOR: $msg")
}
private fun hasNotDefaultParameters(descriptor: CallableDescriptor) = descriptor.valueParameters.none { it.hasDefaultValue() }
val intDesctiptor = DescriptorUtils.getClassDescriptorForType(KonanPlatform.builtIns.intType)
val intAnd = DescriptorUtils.getFunctionByName(intDesctiptor.unsubstitutedMemberScope, Name.identifier("and"))
data class DefaultParameterDescription(val function: FunctionDescriptor, val mask:ValueParameterDescriptor,
val hasExtensionReceiver:Boolean, val hasDispatchReceiver: Boolean)
private fun FunctionDescriptor.getOrCreateDefaultsDescription(context: Context): DefaultParameterDescription {
return context.ir.defaultParameterDescriptions.getOrPut(this) {
this.generateDefaultsDescription(context)
}
}
private fun FunctionDescriptor.generateDefaultsDescription(context: Context): DefaultParameterDescription {
val descriptor = when (this){
is ConstructorDescriptor -> DefaultParameterClassConstructorDescriptor(
containingDeclaration = this.containingDeclaration as ClassDescriptor,
annotations = annotations,
original = null,
isPrimary = false,
source = SourceElement.NO_SOURCE,
kind = CallableMemberDescriptor.Kind.SYNTHESIZED)
is FunctionDescriptor -> SimpleFunctionDescriptorImpl.create(
/* containingDeclaration = */ this.containingDeclaration,
/* annotations = */ Annotations.EMPTY,
/* name = */ name,
/* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED,
/* source = */ SourceElement.NO_SOURCE)
else -> TODO("FIXME!!!")
}
val parameterList = mutableListOf<ValueParameterDescriptor>()
val maskVariable = valueParameter(descriptor, valueParameters.size , "__\$mask\$__", KonanPlatform.builtIns.intType)
parameterList += valueParameters.map{
ValueParameterDescriptorImpl(
containingDeclaration = it.containingDeclaration,
original = it.original,
index = it.index,
annotations = it.annotations,
name = it.name,
outType = it.type,
declaresDefaultValue = false,
isCrossinline = it.isCrossinline,
isNoinline = it.isNoinline,
varargElementType = it.varargElementType,
source = it.source)
}
parameterList.add(maskVariable)
descriptor.initialize(
/* receiverParameterType = */ extensionReceiverParameter?.type,
/* dispatchReceiverParameter = */ dispatchReceiverParameter,
/* typeParameters = */ typeParameters,
/* unsubstitutedValueParameters = */ parameterList,
/* unsubstitutedReturnType = */ returnType,
/* modality = */ Modality.FINAL,
/* visibility = */ this.visibility)
return DefaultParameterDescription(
function = descriptor,
mask = maskVariable,
hasDispatchReceiver = (extensionReceiverParameter != null),
hasExtensionReceiver = (dispatchReceiverParameter != null))
}
private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: String, type: KotlinType):ValueParameterDescriptor {
return ValueParameterDescriptorImpl(
containingDeclaration = descriptor,
original = null,
index = index,
annotations = Annotations.EMPTY,
name = Name.identifier(name),
outType = type,
declaresDefaultValue = false,
isCrossinline = false,
isNoinline = false,
varargElementType = null,
source = SourceElement.NO_SOURCE
)
}
/**
* This descriptor overrides name property, for class constructor : instead of <init> -> <init>$defeult symbol.
*/
class DefaultParameterClassConstructorDescriptor(
containingDeclaration: ClassDescriptor,
original: ConstructorDescriptor?,
annotations: Annotations,
isPrimary: Boolean,
kind: CallableMemberDescriptor.Kind,
source: SourceElement
) : ClassConstructorDescriptorImpl(containingDeclaration, original, annotations, isPrimary, kind, source) {
override fun getName(): Name {
return Name.identifier("${super.getName().asString()}\$default")
}
}