From 641d636c457bcbef3cfa239fdebbe8964204c79d Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 10 Feb 2023 13:39:38 +0100 Subject: [PATCH] JVM IR: improve generation of receiver for bound callable references Trick codegen into generating getfield from the anonymous class instead of its supertype (e.g. kotlin.jvm.internal.FunctionReference, PropertyReference or AdaptedFunctionReference). This gets rid of unnecessary accessors in some cases, and will help in the subsequent commit that changes logic around access to fields from Java superclasses. --- .../jvm/lower/FunctionReferenceLowering.kt | 17 ++++++++++++++--- .../jvm/lower/PropertyReferenceLowering.kt | 8 ++++++-- .../jetbrains/kotlin/backend/jvm/JvmSymbols.kt | 1 - .../callableReference/adaptedReference.ir.txt | 2 -- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 62227664272..7ddd46191bd 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt @@ -570,8 +570,6 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) return fakeTypeParameters } - private val receiverField = context.ir.symbols.functionReferenceReceiverField.owner - fun build(): IrExpression = context.createJvmIrBuilder(currentScope!!).run { irBlock(irFunctionReference.startOffset, irFunctionReference.endOffset) { val constructor = createConstructor() @@ -826,7 +824,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) irImplicitCast( irGetField( irGet(dispatchReceiverParameter!!), - this@FunctionReferenceBuilder.receiverField + functionReferenceClass.getReceiverField(backendContext) ), boundReceiver.second.type ) @@ -1010,5 +1008,18 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) internal fun JvmIrBuilder.calculateOwnerKClass(irContainer: IrDeclarationParent): IrExpression = kClassReference(irContainer.getCallableReferenceOwnerKClassType(backendContext)) + + // This method creates an IrField for the field `kotlin.jvm.internal.CallableReference.receiver`, as if this field was declared + // in the anonymous class for this callable reference. Technically it's incorrect because this field is not declared here. It would + // be more correct to create a fake override but that seems like more work for no clear benefit. Codegen will generate the correct + // field access anyway, even if the field is not present in this parent. + internal fun IrClass.getReceiverField(context: JvmBackendContext): IrField = + context.irFactory.buildField { + name = Name.identifier("receiver") + type = context.irBuiltIns.anyNType + visibility = DescriptorVisibilities.PROTECTED + }.apply { + parent = this@getReceiverField + } } } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt index f495ab23f42..05398bef076 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt @@ -413,7 +413,6 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle } val boundReceiver = expression.getBoundReceiver() - val backingField = superClass.properties.single { it.name.asString() == "receiver" }.backingField!! val get = superClass.functions.find { it.name.asString() == "get" } val set = superClass.functions.find { it.name.asString() == "set" } val invoke = superClass.functions.find { it.name.asString() == "invoke" } @@ -421,6 +420,8 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle val field = expression.field?.owner if (field == null) { fun IrBuilderWithScope.setCallArguments(call: IrCall, arguments: List) { + val backingField = + with(FunctionReferenceLowering) { referenceClass.getReceiverField(this@PropertyReferenceLowering.context) } val receiverFromField = boundReceiver?.let { irImplicitCast(irGetField(irGet(arguments[0]), backingField), it.type) } if (expression.isJavaSyntheticPropertyReference) { assert(call.typeArgumentsCount == 0) { "Unexpected type arguments: ${call.typeArgumentsCount}" } @@ -458,8 +459,11 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle fun IrBuilderWithScope.fieldReceiver(arguments: List) = when { field.isStatic -> null - expression.dispatchReceiver != null -> + expression.dispatchReceiver != null -> { + val backingField = + with(FunctionReferenceLowering) { referenceClass.getReceiverField(this@PropertyReferenceLowering.context) } irImplicitCast(irGetField(irGet(arguments[0]), backingField), expression.receiverType) + } else -> irImplicitCast(irGet(arguments[1]), expression.receiverType) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index 6582fae6b1d..1bd175d2540 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -371,7 +371,6 @@ class JvmSymbols( generateCallableReferenceMethods(klass) } - val functionReferenceReceiverField: IrFieldSymbol = functionReference.fieldByName("receiver") val functionReferenceGetSignature: IrSimpleFunctionSymbol = functionReference.functionByName("getSignature") val functionReferenceGetName: IrSimpleFunctionSymbol = functionReference.functionByName("getName") val functionReferenceGetOwner: IrSimpleFunctionSymbol = functionReference.functionByName("getOwner") diff --git a/compiler/testData/codegen/bytecodeListing/callableReference/adaptedReference.ir.txt b/compiler/testData/codegen/bytecodeListing/callableReference/adaptedReference.ir.txt index e045311ae93..d4d857854b9 100644 --- a/compiler/testData/codegen/bytecodeListing/callableReference/adaptedReference.ir.txt +++ b/compiler/testData/codegen/bytecodeListing/callableReference/adaptedReference.ir.txt @@ -4,7 +4,6 @@ synthetic final class A$testDefaultArguments$1 { enclosing method A.testDefaultArguments()V inner (anonymous) class A$testDefaultArguments$1 method (p0: java.lang.Object): void - public synthetic final static method access$getReceiver$p(p0: A$testDefaultArguments$1): java.lang.Object public synthetic bridge method invoke(): java.lang.Object public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String } @@ -15,7 +14,6 @@ synthetic final class A$testDefaultArguments$2 { enclosing method A.testDefaultArguments()V inner (anonymous) class A$testDefaultArguments$2 method (p0: java.lang.Object): void - public synthetic final static method access$getReceiver$p(p0: A$testDefaultArguments$2): java.lang.Object public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object public synthetic bridge method invoke(p0: java.lang.Object): java.lang.Object }