[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): IrClass {
isMutable: Boolean) : Pair<IrConstructorSymbol, List<IrType>> {
val symbols = context.ir.symbols val symbols = context.ir.symbols
@@ -64,41 +58,32 @@ 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) =
IrFieldImpl(
val arrayItemGetter = arrayClass.functions.single { it.name == Name.identifier("get") } SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION,
val anyType = context.irBuiltIns.anyType IrFieldSymbolImpl(),
val kPropertyImplType = context.ir.symbols.kProperty1Impl.typeWith(anyType, anyType) "KPROPERTY${id}".synthesizedName,
value.expression.type,
val kPropertiesFieldType: IrType = context.ir.symbols.array.typeWith(kPropertyImplType) DescriptorVisibilities.PRIVATE,
isFinal = true,
val kPropertiesField = isExternal = false,
IrFieldImpl( isStatic = true,
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, ).apply {
DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION, parent = irFile
IrFieldSymbolImpl(), annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.eagerInitialization.owner)
"KPROPERTIES".synthesizedName, annotations += buildSimpleAnnotation(context.irBuiltIns, startOffset, endOffset, context.ir.symbols.sharedImmutable.owner)
kPropertiesFieldType, initializer = value
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)
}
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)
@@ -204,10 +182,10 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
receiverTypes receiverTypes
) )
IrFunctionReferenceImpl( IrFunctionReferenceImpl(
startOffset = startOffset, startOffset = startOffset,
endOffset = endOffset, endOffset = endOffset,
type = getterKFunctionType, type = getterKFunctionType,
symbol = expression.getter!!, symbol = expression.getter!!,
typeArgumentsCount = getter.typeParameters.size, typeArgumentsCount = getter.typeParameters.size,
valueArgumentsCount = getter.valueParameters.size, valueArgumentsCount = getter.valueParameters.size,
reflectionTarget = expression.getter!! reflectionTarget = expression.getter!!
@@ -227,10 +205,10 @@ internal class PropertyDelegationLowering(val context: Context) : FileLoweringPa
receiverTypes + returnType receiverTypes + returnType
) )
IrFunctionReferenceImpl( IrFunctionReferenceImpl(
startOffset = startOffset, startOffset = startOffset,
endOffset = endOffset, endOffset = endOffset,
type = setterKFunctionType, type = setterKFunctionType,
symbol = expression.setter!!, symbol = expression.setter!!,
typeArgumentsCount = setter.typeParameters.size, typeArgumentsCount = setter.typeParameters.size,
valueArgumentsCount = setter.valueParameters.size, valueArgumentsCount = setter.valueParameters.size,
reflectionTarget = expression.setter!! reflectionTarget = expression.setter!!
@@ -243,15 +221,36 @@ 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 { val name = irString(expression.symbol.owner.name.asString())
putValueArgument(0, irString(expression.symbol.owner.name.asString()))
if (getterCallableReference != null) val initializer = if (dispatchReceiver == null && extensionReceiver == null) {
putValueArgument(1, getterCallableReference) 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) 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
} }
} }