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.
This commit is contained in:
Alexander Udalov
2023-02-10 13:39:38 +01:00
parent 4ad594a3cd
commit 641d636c45
4 changed files with 20 additions and 8 deletions
@@ -570,8 +570,6 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
return fakeTypeParameters return fakeTypeParameters
} }
private val receiverField = context.ir.symbols.functionReferenceReceiverField.owner
fun build(): IrExpression = context.createJvmIrBuilder(currentScope!!).run { fun build(): IrExpression = context.createJvmIrBuilder(currentScope!!).run {
irBlock(irFunctionReference.startOffset, irFunctionReference.endOffset) { irBlock(irFunctionReference.startOffset, irFunctionReference.endOffset) {
val constructor = createConstructor() val constructor = createConstructor()
@@ -826,7 +824,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
irImplicitCast( irImplicitCast(
irGetField( irGetField(
irGet(dispatchReceiverParameter!!), irGet(dispatchReceiverParameter!!),
this@FunctionReferenceBuilder.receiverField functionReferenceClass.getReceiverField(backendContext)
), ),
boundReceiver.second.type boundReceiver.second.type
) )
@@ -1010,5 +1008,18 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
internal fun JvmIrBuilder.calculateOwnerKClass(irContainer: IrDeclarationParent): IrExpression = internal fun JvmIrBuilder.calculateOwnerKClass(irContainer: IrDeclarationParent): IrExpression =
kClassReference(irContainer.getCallableReferenceOwnerKClassType(backendContext)) 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
}
} }
} }
@@ -413,7 +413,6 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle
} }
val boundReceiver = expression.getBoundReceiver() val boundReceiver = expression.getBoundReceiver()
val backingField = superClass.properties.single { it.name.asString() == "receiver" }.backingField!!
val get = superClass.functions.find { it.name.asString() == "get" } val get = superClass.functions.find { it.name.asString() == "get" }
val set = superClass.functions.find { it.name.asString() == "set" } val set = superClass.functions.find { it.name.asString() == "set" }
val invoke = superClass.functions.find { it.name.asString() == "invoke" } 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 val field = expression.field?.owner
if (field == null) { if (field == null) {
fun IrBuilderWithScope.setCallArguments(call: IrCall, arguments: List<IrValueParameter>) { fun IrBuilderWithScope.setCallArguments(call: IrCall, arguments: List<IrValueParameter>) {
val backingField =
with(FunctionReferenceLowering) { referenceClass.getReceiverField(this@PropertyReferenceLowering.context) }
val receiverFromField = boundReceiver?.let { irImplicitCast(irGetField(irGet(arguments[0]), backingField), it.type) } val receiverFromField = boundReceiver?.let { irImplicitCast(irGetField(irGet(arguments[0]), backingField), it.type) }
if (expression.isJavaSyntheticPropertyReference) { if (expression.isJavaSyntheticPropertyReference) {
assert(call.typeArgumentsCount == 0) { "Unexpected type arguments: ${call.typeArgumentsCount}" } 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<IrValueParameter>) = when { fun IrBuilderWithScope.fieldReceiver(arguments: List<IrValueParameter>) = when {
field.isStatic -> field.isStatic ->
null null
expression.dispatchReceiver != null -> expression.dispatchReceiver != null -> {
val backingField =
with(FunctionReferenceLowering) { referenceClass.getReceiverField(this@PropertyReferenceLowering.context) }
irImplicitCast(irGetField(irGet(arguments[0]), backingField), expression.receiverType) irImplicitCast(irGetField(irGet(arguments[0]), backingField), expression.receiverType)
}
else -> else ->
irImplicitCast(irGet(arguments[1]), expression.receiverType) irImplicitCast(irGet(arguments[1]), expression.receiverType)
} }
@@ -371,7 +371,6 @@ class JvmSymbols(
generateCallableReferenceMethods(klass) generateCallableReferenceMethods(klass)
} }
val functionReferenceReceiverField: IrFieldSymbol = functionReference.fieldByName("receiver")
val functionReferenceGetSignature: IrSimpleFunctionSymbol = functionReference.functionByName("getSignature") val functionReferenceGetSignature: IrSimpleFunctionSymbol = functionReference.functionByName("getSignature")
val functionReferenceGetName: IrSimpleFunctionSymbol = functionReference.functionByName("getName") val functionReferenceGetName: IrSimpleFunctionSymbol = functionReference.functionByName("getName")
val functionReferenceGetOwner: IrSimpleFunctionSymbol = functionReference.functionByName("getOwner") val functionReferenceGetOwner: IrSimpleFunctionSymbol = functionReference.functionByName("getOwner")
@@ -4,7 +4,6 @@ synthetic final class A$testDefaultArguments$1 {
enclosing method A.testDefaultArguments()V enclosing method A.testDefaultArguments()V
inner (anonymous) class A$testDefaultArguments$1 inner (anonymous) class A$testDefaultArguments$1
method <init>(p0: java.lang.Object): void method <init>(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 synthetic bridge method invoke(): java.lang.Object
public final @org.jetbrains.annotations.NotNull method invoke(): java.lang.String 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 enclosing method A.testDefaultArguments()V
inner (anonymous) class A$testDefaultArguments$2 inner (anonymous) class A$testDefaultArguments$2
method <init>(p0: java.lang.Object): void method <init>(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 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 public synthetic bridge method invoke(p0: java.lang.Object): java.lang.Object
} }