JVM_IR: generate $delegate methods for val x by ::y

This commit is contained in:
pyos
2021-06-30 19:26:18 +02:00
committed by Alexander Udalov
parent 7ae4303e1b
commit 5d0102a966
4 changed files with 76 additions and 23 deletions
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JvmAbi
@@ -164,7 +163,7 @@ class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrE
JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS,
// TODO: technically JVM permits having fields with same name but different type, so we could potentially
// generate two properties like that; should this be the getter's return type instead?
returnType = backendContext.irBuiltIns.unitType
isStatic = true, returnType = backendContext.irBuiltIns.unitType
).apply {
body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
annotations = declaration.annotations
@@ -175,7 +174,8 @@ class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrE
declaration: IrProperty,
suffix: String,
origin: IrDeclarationOrigin,
returnType: IrType = IrUninitializedType
isStatic: Boolean,
returnType: IrType
) = irFactory.buildFun {
name = Name.identifier(computeSyntheticMethodName(declaration, suffix))
modality = Modality.OPEN
@@ -183,14 +183,28 @@ class JvmPropertiesLowering(private val backendContext: JvmBackendContext) : IrE
this.origin = origin
this.returnType = returnType
}.apply {
declaration.getter?.extensionReceiverParameter?.let {
// Synthetic methods don't get generic type signatures anyway, so not exactly useful to preserve type parameters.
extensionReceiverParameter = it.copyTo(this, type = it.type.eraseTypeParameters())
}
var index = 0
valueParameters = listOfNotNull(
declaration.getter?.dispatchReceiverParameter?.takeIf { !isStatic }?.let {
// Synthetic methods don't get generic type signatures anyway, so not exactly useful to preserve type parameters.
it.copyTo(this, type = it.type.eraseTypeParameters(), index = index++)
},
declaration.getter?.extensionReceiverParameter?.let {
it.copyTo(this, type = it.type.eraseTypeParameters(), index = index)
}
)
parent = declaration.parent
metadata = declaration.metadata
}
fun JvmBackendContext.createSyntheticMethodForPropertyDelegate(declaration: IrProperty) =
createSyntheticMethodForProperty(
declaration,
JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX,
IrDeclarationOrigin.PROPERTY_DELEGATE,
isStatic = false, returnType = irBuiltIns.anyNType
)
private fun JvmBackendContext.computeSyntheticMethodName(property: IrProperty, suffix: String): String {
val baseName =
if (state.languageVersionSettings.supportsFeature(LanguageFeature.UseGetterNameForPropertyAnnotationsMethodOnJvm)) {
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.lower.JvmPropertiesLowering.Companion.createSyntheticMethodForPropertyDelegate
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
@@ -17,8 +19,10 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildVariable
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl
import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.Name
@@ -80,29 +84,53 @@ private class PropertyReferenceDelegationTransformer(val context: JvmBackendCont
private val IrStatement.isStdlibCall: Boolean
get() = this is IrCall && symbol.owner.getPackageFragment()?.fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME
override fun visitProperty(declaration: IrProperty): IrStatement {
if (!declaration.isDelegated || declaration.isFakeOverride) return super.visitProperty(declaration)
override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformChildren(this, null)
declaration.transformDeclarationsFlat {
(it as? IrProperty)?.transform()
}
return declaration
}
val oldField = declaration.backingField
private fun IrProperty.transform(): List<IrDeclaration>? {
if (!isDelegated || isFakeOverride) return null
val oldField = backingField
val delegate = oldField?.initializer?.expression
if (delegate !is IrPropertyReference ||
declaration.getter?.returnsResultOfStdlibCall == false ||
declaration.setter?.returnsResultOfStdlibCall == false
) return super.visitProperty(declaration)
getter?.returnsResultOfStdlibCall == false ||
setter?.returnsResultOfStdlibCall == false
) return null
declaration.backingField = (delegate.dispatchReceiver ?: delegate.extensionReceiver)?.let { receiver ->
backingField = (delegate.dispatchReceiver ?: delegate.extensionReceiver)?.let { receiver ->
context.irFactory.buildField {
updateFrom(oldField)
name = Name.identifier("${declaration.name}\$receiver")
name = Name.identifier("${this@transform.name}\$receiver")
type = receiver.type
}.apply {
parent = oldField.parent
initializer = context.irFactory.createExpressionBody(receiver.transform(this@PropertyReferenceDelegationTransformer, null))
}
}
declaration.getter?.apply { body = accessorBody(delegate, declaration.backingField) }
declaration.setter?.apply { body = accessorBody(delegate, declaration.backingField) }
return declaration
getter?.apply { body = accessorBody(delegate, backingField) }
setter?.apply { body = accessorBody(delegate, backingField) }
val delegateMethod = context.createSyntheticMethodForPropertyDelegate(this).apply {
body = context.createJvmIrBuilder(symbol).run {
val propertyOwner = if (getter?.dispatchReceiverParameter != null) irGet(valueParameters[0]) else null
val boundReceiver = backingField?.let { irGetField(propertyOwner, it) }
irExprBody(with(delegate) {
val origin = PropertyReferenceLowering.REFLECTED_PROPERTY_REFERENCE
IrPropertyReferenceImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, field, getter, setter, origin)
}.apply {
when {
delegate.dispatchReceiver != null -> dispatchReceiver = boundReceiver
delegate.extensionReceiver != null -> extensionReceiver = boundReceiver
}
})
}
}
return listOf(this, delegateMethod)
}
override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement {
@@ -60,7 +60,10 @@ internal val propertyReferencePhase = makeIrFilePhase(
prerequisite = setOf(functionReferencePhase, suspendLambdaPhase, propertyReferenceDelegationPhase)
)
private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
// Marking a property reference with this origin causes it to not generate a class.
object REFLECTED_PROPERTY_REFERENCE : IrStatementOriginImpl("REFLECTED_PROPERTY_REFERENCE")
// TODO: join IrLocalDelegatedPropertyReference and IrPropertyReference via the class hierarchy?
private val IrMemberAccessExpression<*>.getter: IrSimpleFunctionSymbol?
get() = (this as? IrPropertyReference)?.getter ?: (this as? IrLocalDelegatedPropertyReference)?.getter
@@ -299,6 +302,8 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem
private fun cachedKProperty(expression: IrCallableReference<*>): IrExpression {
expression.transformChildrenVoid()
if (expression.origin == REFLECTED_PROPERTY_REFERENCE)
return createReflectedKProperty(expression)
if (expression.origin != IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE)
return createSpecializedKProperty(expression)
@@ -30,9 +30,15 @@ fun local() {
// 0 kotlin/jvm/internal/PropertyReference[0-2]Impl\.\<init\>
// JVM_IR_TEMPLATES
// Optimized all to direct accesses:
// 0 kotlin/jvm/internal/MutablePropertyReference[0-2]Impl\.\<init\>
// Optimized all to direct accesses, with `$delegate` methods generating reflected references on demand:
// 0 extends kotlin/jvm/internal/MutablePropertyReference[0-2]Impl
// 0 private final( static)? Lkotlin/reflect/KMutableProperty[0-2]; [xyz]m?\$delegate
// 0 LOCALVARIABLE [xyz]m? Lkotlin/reflect/KMutableProperty[0-2];
// 12 static get[XYZ]m?\$delegate
// JVM_TEMPLATES
// Not optimized:
// 16 kotlin/jvm/internal/MutablePropertyReference[0-2]Impl\.\<init\>
// Not optimized, references created as classes and stored in fields:
// 16 extends kotlin/jvm/internal/MutablePropertyReference[0-2]Impl
// 12 private final( static)? Lkotlin/reflect/KMutableProperty[0-2]; [xyz]m?\$delegate
// 4 LOCALVARIABLE [xyz]m? Lkotlin/reflect/KMutableProperty[0-2];
// 0 static get[XYZ]m?\$delegate