JVM_IR: generate $delegate methods for val x by ::y
This commit is contained in:
+21
-7
@@ -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)) {
|
||||
|
||||
+39
-11
@@ -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 {
|
||||
|
||||
+6
-1
@@ -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)
|
||||
|
||||
|
||||
+10
-4
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user