[K/N] Transform some KProperties to ConstantValue

This commit is contained in:
Pavel Kunyavskiy
2021-07-16 14:38:24 +03:00
committed by Space
parent f03c897dc4
commit 89414eb214
@@ -16,26 +16,20 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrLocalDelegatedPropertyReference
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
internal class PropertyDelegationLowering(val context: Context) : FileLoweringPass {
private var tempIndex = 0
private fun getKPropertyImplConstructor(receiverTypes: List<IrType>,
returnType: IrType,
isLocal: Boolean,
isMutable: Boolean) : Pair<IrConstructorSymbol, List<IrType>> {
private fun getKPropertyImpl(receiverTypes: List<IrType>,
isLocal: Boolean,
isMutable: Boolean): IrClass {
val symbols = context.ir.symbols
@@ -64,41 +58,32 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
}
}
val arguments = (receiverTypes + listOf(returnType))
return classSymbol.constructors.single() to arguments
return classSymbol.owner
}
override fun lower(irFile: IrFile) {
// Somehow there is no reasonable common ancestor for IrProperty and IrLocalDelegatedProperty,
// so index by IrDeclaration.
val kProperties = mutableMapOf<IrDeclaration, Pair<IrExpression, Int>>()
val kProperties = mutableMapOf<IrDeclaration, IrField>()
val generatedClasses = mutableListOf<IrClass>()
val arrayClass = context.ir.symbols.array.owner
val arrayItemGetter = arrayClass.functions.single { it.name == Name.identifier("get") }
val anyType = context.irBuiltIns.anyType
val kPropertyImplType = context.ir.symbols.kProperty1Impl.typeWith(anyType, anyType)
val kPropertiesFieldType: IrType = context.ir.symbols.array.typeWith(kPropertyImplType)
val kPropertiesField =
IrFieldImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION,
IrFieldSymbolImpl(),
"KPROPERTIES".synthesizedName,
kPropertiesFieldType,
DescriptorVisibilities.PRIVATE,
isFinal = true,
isExternal = false,
isStatic = true,
).apply {
parent = irFile
annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.sharedImmutable.owner)
annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.eagerInitialization.owner)
}
fun kPropertyField(value: IrExpressionBody, id:Int) =
IrFieldImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION,
IrFieldSymbolImpl(),
"KPROPERTY${id}".synthesizedName,
value.expression.type,
DescriptorVisibilities.PRIVATE,
isFinal = true,
isExternal = false,
isStatic = true,
).apply {
parent = irFile
annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.eagerInitialization.owner)
annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.sharedImmutable.owner)
initializer = value
}
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
@@ -111,19 +96,19 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
irBuilder.run {
val receiversCount = listOf(expression.dispatchReceiver, expression.extensionReceiver).count { it != null }
return when (receiversCount) {
1 -> createKProperty(expression, this) // Has receiver.
1 -> createKProperty(expression, this, irFile, generatedClasses) // Has receiver.
2 -> error("Callable reference to properties with two receivers is not allowed: ${expression.symbol.owner.name}")
else -> { // Cache KProperties with no arguments.
val field = kProperties.getOrPut(expression.symbol.owner) {
createKProperty(expression, this) to kProperties.size
kPropertyField(
irExprBody(createKProperty(expression, this, irFile, generatedClasses) as IrConstantValue),
kProperties.size
)
}
irCall(arrayItemGetter).apply {
dispatchReceiver = irGetField(null, kPropertiesField)
putValueArgument(0, irInt(field.second))
}
irGetField(null, field)
}
}
}
@@ -142,35 +127,28 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
else { // Cache KProperties with no arguments.
// TODO: what about `receiversCount == 1` case?
val field = kProperties.getOrPut(expression.symbol.owner) {
createLocalKProperty(
kPropertyField(irExprBody(createLocalKProperty(
expression.symbol.owner.name.asString(),
expression.getter.owner.returnType,
KTypeGenerator(this@PropertyDelegationLowering.context, irFile, expression),
this
) to kProperties.size
)), kProperties.size)
}
return irCall(arrayItemGetter).apply {
dispatchReceiver = irGetField(null, kPropertiesField)
putValueArgument(0, irInt(field.second))
}
return irGetField(null, field)
}
}
}
})
if (kProperties.isNotEmpty()) {
val initializers = kProperties.values.sortedBy { it.second }.map { it.first }
// TODO: replace with static initialization.
kPropertiesField.initializer = IrExpressionBodyImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
context.createArrayOfExpression(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, kPropertyImplType, initializers))
irFile.declarations.add(0, kPropertiesField)
}
irFile.declarations.addAll(0, kProperties.values)
irFile.declarations.addAll(generatedClasses)
}
private fun createKProperty(
expression: IrPropertyReference,
irBuilder: IrBuilderWithScope
irBuilder: IrBuilderWithScope,
irFile: IrFile,
generatedClasses: MutableList<IrClass>
): IrExpression {
val startOffset = expression.startOffset
val endOffset = expression.endOffset
@@ -190,7 +168,7 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
}
val returnType = expression.getter?.owner?.returnType ?: expression.field!!.owner.type
val getterCallableReference = expression.getter?.owner?.let { getter ->
val getterCallableReference = expression.getter!!.owner.let { getter ->
getter.extensionReceiverParameter.let {
if (it != null && expression.extensionReceiver == null)
receiverTypes.add(it.type)
@@ -204,10 +182,10 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
receiverTypes
)
IrFunctionReferenceImpl(
startOffset = startOffset,
endOffset = endOffset,
type = getterKFunctionType,
symbol = expression.getter!!,
startOffset = startOffset,
endOffset = endOffset,
type = getterKFunctionType,
symbol = expression.getter!!,
typeArgumentsCount = getter.typeParameters.size,
valueArgumentsCount = getter.valueParameters.size,
reflectionTarget = expression.getter!!
@@ -227,10 +205,10 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
receiverTypes + returnType
)
IrFunctionReferenceImpl(
startOffset = startOffset,
endOffset = endOffset,
type = setterKFunctionType,
symbol = expression.setter!!,
startOffset = startOffset,
endOffset = endOffset,
type = setterKFunctionType,
symbol = expression.setter!!,
typeArgumentsCount = setter.typeParameters.size,
valueArgumentsCount = setter.valueParameters.size,
reflectionTarget = expression.setter!!
@@ -243,15 +221,36 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
}
}
val (symbol, constructorTypeArguments) = getKPropertyImplConstructor(
val clazz = getKPropertyImpl(
receiverTypes = receiverTypes,
returnType = returnType,
isLocal = false,
isMutable = setterCallableReference != null)
val initializer = irCall(symbol.owner, constructorTypeArguments).apply {
putValueArgument(0, irString(expression.symbol.owner.name.asString()))
if (getterCallableReference != null)
putValueArgument(1, getterCallableReference)
isLocal = false,
isMutable = setterCallableReference != null)
val name = irString(expression.symbol.owner.name.asString())
val initializer = if (dispatchReceiver == null && extensionReceiver == null) {
fun IrFunctionReference.convert() : IrConstantValue {
val builder = FunctionReferenceLowering.FunctionReferenceBuilder(
irFile,
irFile,
this,
this@PropertyDelegationLowering.context,
irBuilder,
)
val (newClass, newExpression) = builder.build()
generatedClasses.add(newClass)
return newExpression as IrConstantValue
}
return irConstantObject(clazz, @OptIn(ExperimentalStdlibApi::class) buildMap {
put("name", irConstantPrimitive(name))
put("getter", getterCallableReference.convert())
if (setterCallableReference != null) {
put("setter", setterCallableReference.convert())
}
})
} else irCall(clazz.constructors.single(), receiverTypes + listOf(returnType)).apply {
putValueArgument(0, name)
putValueArgument(1, getterCallableReference)
if (setterCallableReference != null)
putValueArgument(2, setterCallableReference)
}
@@ -262,18 +261,16 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
private fun createLocalKProperty(propertyName: String,
propertyType: IrType,
kTypeGenerator: KTypeGenerator,
irBuilder: IrBuilderWithScope): IrExpression {
irBuilder.run {
val (symbol, constructorTypeArguments) = getKPropertyImplConstructor(
receiverTypes = emptyList(),
returnType = propertyType,
isLocal = true,
isMutable = false)
val initializer = irCall(symbol.owner, constructorTypeArguments).apply {
putValueArgument(0, irString(propertyName))
putValueArgument(1, with(kTypeGenerator) { irKType(propertyType) })
}
return initializer
irBuilder: IrBuilderWithScope): IrConstantValue {
val symbols = context.ir.symbols
return irBuilder.run {
irConstantObject(
symbols.kLocalDelegatedPropertyImpl.owner,
mapOf(
"name" to irConstantPrimitive(irString(propertyName)),
"returnType" to with(kTypeGenerator) { irKType(propertyType) }
)
)
}
}