[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.builders.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrLocalDelegatedPropertyReference
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.expressions.impl.* 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.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.IrType 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.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
internal class PropertyDelegationLowering(val context: Context) : FileLoweringPass { internal class PropertyDelegationLowering(val context: Context) : FileLoweringPass {
private var tempIndex = 0 private var tempIndex = 0
private fun getKPropertyImplConstructor(receiverTypes: List<IrType>, private fun getKPropertyImpl(receiverTypes: List<IrType>,
returnType: IrType,
isLocal: Boolean, isLocal: Boolean,
isMutable: Boolean) : Pair<IrConstructorSymbol, List<IrType>> { isMutable: Boolean): IrClass {
val symbols = context.ir.symbols val symbols = context.ir.symbols
@@ -64,40 +58,31 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
} }
} }
val arguments = (receiverTypes + listOf(returnType)) return classSymbol.owner
return classSymbol.constructors.single() to arguments
} }
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
// Somehow there is no reasonable common ancestor for IrProperty and IrLocalDelegatedProperty, // Somehow there is no reasonable common ancestor for IrProperty and IrLocalDelegatedProperty,
// so index by IrDeclaration. // 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 fun kPropertyField(value: IrExpressionBody, id:Int) =
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( IrFieldImpl(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION, DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION,
IrFieldSymbolImpl(), IrFieldSymbolImpl(),
"KPROPERTIES".synthesizedName, "KPROPERTY${id}".synthesizedName,
kPropertiesFieldType, value.expression.type,
DescriptorVisibilities.PRIVATE, DescriptorVisibilities.PRIVATE,
isFinal = true, isFinal = true,
isExternal = false, isExternal = false,
isStatic = true, isStatic = true,
).apply { ).apply {
parent = irFile parent = irFile
annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.sharedImmutable.owner)
annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.eagerInitialization.owner) 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() { irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
@@ -111,19 +96,19 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
irBuilder.run { irBuilder.run {
val receiversCount = listOf(expression.dispatchReceiver, expression.extensionReceiver).count { it != null } val receiversCount = listOf(expression.dispatchReceiver, expression.extensionReceiver).count { it != null }
return when (receiversCount) { 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}") 2 -> error("Callable reference to properties with two receivers is not allowed: ${expression.symbol.owner.name}")
else -> { // Cache KProperties with no arguments. else -> { // Cache KProperties with no arguments.
val field = kProperties.getOrPut(expression.symbol.owner) { 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 { irGetField(null, field)
dispatchReceiver = irGetField(null, kPropertiesField)
putValueArgument(0, irInt(field.second))
}
} }
} }
} }
@@ -142,35 +127,28 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
else { // Cache KProperties with no arguments. else { // Cache KProperties with no arguments.
// TODO: what about `receiversCount == 1` case? // TODO: what about `receiversCount == 1` case?
val field = kProperties.getOrPut(expression.symbol.owner) { val field = kProperties.getOrPut(expression.symbol.owner) {
createLocalKProperty( kPropertyField(irExprBody(createLocalKProperty(
expression.symbol.owner.name.asString(), expression.symbol.owner.name.asString(),
expression.getter.owner.returnType, expression.getter.owner.returnType,
KTypeGenerator(this@PropertyDelegationLowering.context, irFile, expression), KTypeGenerator(this@PropertyDelegationLowering.context, irFile, expression),
this this
) to kProperties.size )), kProperties.size)
} }
return irCall(arrayItemGetter).apply { return irGetField(null, field)
dispatchReceiver = irGetField(null, kPropertiesField)
putValueArgument(0, irInt(field.second))
}
} }
} }
} }
}) })
irFile.declarations.addAll(0, kProperties.values)
if (kProperties.isNotEmpty()) { irFile.declarations.addAll(generatedClasses)
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)
}
} }
private fun createKProperty( private fun createKProperty(
expression: IrPropertyReference, expression: IrPropertyReference,
irBuilder: IrBuilderWithScope irBuilder: IrBuilderWithScope,
irFile: IrFile,
generatedClasses: MutableList<IrClass>
): IrExpression { ): IrExpression {
val startOffset = expression.startOffset val startOffset = expression.startOffset
val endOffset = expression.endOffset 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 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 { getter.extensionReceiverParameter.let {
if (it != null && expression.extensionReceiver == null) if (it != null && expression.extensionReceiver == null)
receiverTypes.add(it.type) receiverTypes.add(it.type)
@@ -243,14 +221,35 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
} }
} }
val (symbol, constructorTypeArguments) = getKPropertyImplConstructor( val clazz = getKPropertyImpl(
receiverTypes = receiverTypes, receiverTypes = receiverTypes,
returnType = returnType,
isLocal = false, isLocal = false,
isMutable = setterCallableReference != null) isMutable = setterCallableReference != null)
val initializer = irCall(symbol.owner, constructorTypeArguments).apply {
putValueArgument(0, irString(expression.symbol.owner.name.asString())) val name = irString(expression.symbol.owner.name.asString())
if (getterCallableReference != null)
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) putValueArgument(1, getterCallableReference)
if (setterCallableReference != null) if (setterCallableReference != null)
putValueArgument(2, setterCallableReference) putValueArgument(2, setterCallableReference)
@@ -262,18 +261,16 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
private fun createLocalKProperty(propertyName: String, private fun createLocalKProperty(propertyName: String,
propertyType: IrType, propertyType: IrType,
kTypeGenerator: KTypeGenerator, kTypeGenerator: KTypeGenerator,
irBuilder: IrBuilderWithScope): IrExpression { irBuilder: IrBuilderWithScope): IrConstantValue {
irBuilder.run { val symbols = context.ir.symbols
val (symbol, constructorTypeArguments) = getKPropertyImplConstructor( return irBuilder.run {
receiverTypes = emptyList(), irConstantObject(
returnType = propertyType, symbols.kLocalDelegatedPropertyImpl.owner,
isLocal = true, mapOf(
isMutable = false) "name" to irConstantPrimitive(irString(propertyName)),
val initializer = irCall(symbol.owner, constructorTypeArguments).apply { "returnType" to with(kTypeGenerator) { irKType(propertyType) }
putValueArgument(0, irString(propertyName)) )
putValueArgument(1, with(kTypeGenerator) { irKType(propertyType) }) )
}
return initializer
} }
} }