From 2974004de423094c8e9fa42682fd9be473adecf6 Mon Sep 17 00:00:00 2001 From: pyos Date: Fri, 9 Oct 2020 09:36:49 +0200 Subject: [PATCH] JVM_IR: make PropertyReferenceLowering less quadratic ClassLoweringPass creates a visitor and calls lower() on each class, which then creates another visitor that does not override `visitClass` to ignore the nested classes. This is generally not a good idea. --- .../jvm/lower/PropertyReferenceLowering.kt | 488 +++++++++--------- 1 file changed, 242 insertions(+), 246 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt index 39dc92ed581..5aa34a5ecd7 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor import org.jetbrains.kotlin.backend.common.ir.copyTo @@ -14,7 +14,6 @@ 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.JvmLoweredDeclarationOrigin -import org.jetbrains.kotlin.backend.jvm.ir.JvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.irArrayOf import org.jetbrains.kotlin.backend.jvm.ir.needsAccessor @@ -38,7 +37,6 @@ import org.jetbrains.kotlin.ir.types.createType import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name @@ -54,7 +52,7 @@ internal val propertyReferencePhase = makeIrFilePhase( prerequisite = setOf(functionReferencePhase, suspendLambdaPhase) ) -internal class PropertyReferenceLowering(val context: JvmBackendContext) : ClassLoweringPass { +private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass { // Reflection metadata for local properties is serialized under the signature "" attached to the containing class. // This maps properties to values of N. private val localPropertyIndices = mutableMapOf() @@ -69,11 +67,6 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class private val IrMemberAccessExpression<*>.field: IrFieldSymbol? get() = (this as? IrPropertyReference)?.field - // Plain Java fields do not have a getter, but can be referenced nonetheless. The signature should be the one - // that a getter would have, if it existed. - private val IrField.signature: String - get() = "${JvmAbi.getterName(name.asString())}()${context.methodSignatureMapper.mapReturnType(this)}" - private val arrayItemGetter = context.ir.symbols.array.owner.functions.single { it.name.asString() == "get" } @@ -100,38 +93,32 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class return current.parent } - private fun IrBuilderWithScope.buildReflectedContainerReference(expression: IrMemberAccessExpression<*>): IrExpression = - calculateOwner(expression.propertyContainer, this@PropertyReferenceLowering.context) - - private fun JvmIrBuilder.buildReflectedContainerReferenceKClass(expression: IrMemberAccessExpression<*>): IrExpression = - calculateOwnerKClass(expression.propertyContainer, backendContext) + // Plain Java fields do not have a getter, but can be referenced nonetheless. The signature should be the one + // that a getter would have, if it existed. + private val IrField.fakeGetterSignature: String + get() = "${JvmAbi.getterName(name.asString())}()${context.methodSignatureMapper.mapReturnType(this)}" private fun IrBuilderWithScope.computeSignatureString(expression: IrMemberAccessExpression<*>): IrExpression { - return expression.getter?.let { getter -> - localPropertyIndices[getter]?.let { irString("") } - ?: irCall(signatureStringIntrinsic).apply { - // Work around for differences between `RuntimeTypeMapper.KotlinProperty` and the real Kotlin type mapper. - // Most notably, the runtime type mapper does not perform inline class name mangling. This is usually not - // a problem, since we will produce a getter signature as part of the Kotlin metadata, except when there - // is no getter method in the bytecode. In that case we need to avoid inline class mangling for the - // function reference used in the intrinsic. - // - // Note that we cannot compute the signature at this point, since we still need to mangle the names of - // private properties in multifile-part classes. - val needsDummySignature = - getter.owner.correspondingPropertySymbol?.owner?.needsAccessor(getter.owner) == false || - // Internal underlying vals of inline classes have no getter method - getter.owner.isInlineClassFieldGetter && getter.owner.visibility == DescriptorVisibilities.INTERNAL - - putValueArgument( - 0, - IrFunctionReferenceImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, expression.type, getter, 0, getter, - if (needsDummySignature) InlineClassAbi.UNMANGLED_FUNCTION_REFERENCE else null - ) - ) - } - } ?: irString(expression.field!!.owner.signature) + if (expression is IrLocalDelegatedPropertyReference) { + // Local delegated properties are stored as a plain list, and the runtime library extracts the index from this string: + val index = localPropertyIndices[expression.getter] ?: throw AssertionError("no index for ${expression.render()}") + return irString("") + } + val getter = expression.getter ?: return irString(expression.field!!.owner.fakeGetterSignature) + // Work around for differences between `RuntimeTypeMapper.KotlinProperty` and the real Kotlin type mapper. + // Most notably, the runtime type mapper does not perform inline class name mangling. This is usually not + // a problem, since we will produce a getter signature as part of the Kotlin metadata, except when there + // is no getter method in the bytecode. In that case we need to avoid inline class mangling for the + // function reference used in the intrinsic. + // + // Note that we cannot compute the signature at this point, since we still need to mangle the names of + // private properties in multifile-part classes. + val needsDummySignature = getter.owner.correspondingPropertySymbol?.owner?.needsAccessor(getter.owner) == false || + // Internal underlying vals of inline classes have no getter method + getter.owner.isInlineClassFieldGetter && getter.owner.visibility == DescriptorVisibilities.INTERNAL + val origin = if (needsDummySignature) InlineClassAbi.UNMANGLED_FUNCTION_REFERENCE else null + val reference = IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, expression.type, getter, 0, getter, origin) + return irCall(signatureStringIntrinsic).apply { putValueArgument(0, reference) } } private fun IrClass.addOverride(method: IrSimpleFunction, buildBody: IrBuilderWithScope.(List) -> IrExpression) = @@ -191,7 +178,7 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class private data class PropertyInstance(val initializer: IrExpression, val index: Int) - override fun lower(irClass: IrClass) { + private inner class ClassData { val kProperties = mutableMapOf() val kPropertiesField = context.irFactory.buildField { name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME) @@ -202,222 +189,231 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY } var localPropertiesInClass = 0 + } - irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() { - override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement { - localPropertyIndices[declaration.getter.symbol] = localPropertiesInClass++ - return super.visitLocalDelegatedProperty(declaration) - } + private var currentClassData: ClassData? = null - override fun visitPropertyReference(expression: IrPropertyReference): IrExpression = - cachedKProperty(expression) + override fun lower(irFile: IrFile) = + irFile.transformChildrenVoid() - override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression = - cachedKProperty(expression) - - private fun cachedKProperty(expression: IrCallableReference<*>): IrExpression { - expression.transformChildrenVoid() - if (expression.origin != IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE) - return createSpecializedKProperty(expression) - - // For delegated properties, the getter and setter contain a reference each as the second argument to getValue - // and setValue. Since it's highly unlikely that anyone will call get/set on these, optimize for space. - return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { - val (_, index) = kProperties.getOrPut(expression.symbol) { - PropertyInstance(createReflectedKProperty(expression), kProperties.size) - } - irCall(arrayItemGetter).apply { - dispatchReceiver = irGetField(null, kPropertiesField) - putValueArgument(0, irInt(index)) - } - } - } - - // Create an instance of KProperty that uses Java reflection to locate the getter and the setter. This kind of reference - // does not support local variables or bound receivers (e.g. `Class()::field`) and is slower, but takes up less space. - // Example: `C::property` -> `Reflection.property1(PropertyReference1Impl(C::class, "property", "getProperty()LType;"))`. - private fun createReflectedKProperty(expression: IrCallableReference<*>): IrExpression { - val referenceKind = propertyReferenceKindFor(expression) - return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { - irCall(referenceKind.wrapper).apply { - val constructor = referenceKind.implSymbol.constructors.single { it.owner.valueParameters.size == 3 } - putValueArgument(0, irCall(constructor).apply { - putValueArgument(0, buildReflectedContainerReference(expression)) - putValueArgument(1, irString(expression.referencedName.asString())) - putValueArgument(2, computeSignatureString(expression)) - }) - } - } - } - - // Create an instance of KProperty that overrides the get() and set() methods to directly call getX() and setX() on the object. - // This is (relatively) fast, but space-inefficient. Also, the instances can store bound receivers in their fields. Example: - // - // class C$property$0 : PropertyReference0Impl { - // constructor(boundReceiver: C) : super(boundReceiver, C::class.java, "property", "getProperty()LType;", 0) - // override fun get(): T = receiver.property - // override fun set(value: T) { receiver.property = value } - // } - // - // and then `C()::property` -> `C$property$0(C())`. - // - private fun createSpecializedKProperty(expression: IrCallableReference<*>): IrExpression { - val referenceClass = createKPropertySubclass(expression) - return context.createIrBuilder( - currentScope?.scope?.scopeOwnerSymbol ?: irClass.symbol, expression.startOffset, expression.endOffset - ) - .irBlock { - // TODO: Move this to the enclosing class, right now the parent field is wrong! - +referenceClass - +irCall(referenceClass.constructors.single()).apply { - var index = 0 - expression.dispatchReceiver?.let { putValueArgument(index++, it) } - expression.extensionReceiver?.let { putValueArgument(index++, it) } - } - } - } - - private fun createKPropertySubclass(expression: IrCallableReference<*>): IrClass { - val kind = propertyReferenceKindFor(expression) - val superClass = if (useOptimizedSuperClass) kind.implSymbol.owner else kind.interfaceSymbol.owner - val referenceClass = context.irFactory.buildClass { - setSourceRange(expression) - name = SpecialNames.NO_NAME_PROVIDED - origin = JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE - visibility = DescriptorVisibilities.LOCAL - }.apply { - parent = irClass - superTypes = listOf(superClass.defaultType) - createImplicitParameterDeclarationWithWrappedDescriptor() - }.copyAttributes(expression) - - addConstructor(expression, referenceClass, superClass) - - if (!useOptimizedSuperClass) { - val getName = superClass.functions.single { it.name.asString() == "getName" } - val getOwner = superClass.functions.single { it.name.asString() == "getOwner" } - val getSignature = superClass.functions.single { it.name.asString() == "getSignature" } - referenceClass.addOverride(getName) { irString(expression.referencedName.asString()) } - referenceClass.addOverride(getOwner) { buildReflectedContainerReference(expression) } - referenceClass.addOverride(getSignature) { computeSignatureString(expression) } - } - - 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" } - - val field = expression.field?.owner - if (field == null) { - fun IrBuilderWithScope.setCallArguments(call: IrCall, arguments: List) { - var index = 1 - call.copyTypeArgumentsFrom(expression) - call.dispatchReceiver = call.symbol.owner.dispatchReceiverParameter?.let { - if (expression.dispatchReceiver != null) - irImplicitCast(irGetField(irGet(arguments[0]), backingField), it.type) - else - irImplicitCast(irGet(arguments[index++]), it.type) - } - call.extensionReceiver = call.symbol.owner.extensionReceiverParameter?.let { - if (expression.extensionReceiver != null) - irImplicitCast(irGetField(irGet(arguments[0]), backingField), it.type) - else - irImplicitCast(irGet(arguments[index++]), it.type) - } - } - - expression.getter?.owner?.let { getter -> - referenceClass.addOverride(get!!) { arguments -> - irGet(getter.returnType, null, getter.symbol).apply { - setCallArguments(this, arguments) - } - } - referenceClass.addFakeOverride(invoke!!) - } - - expression.setter?.owner?.let { setter -> - referenceClass.addOverride(set!!) { arguments -> - irSet(setter.returnType, null, setter.symbol, irGet(arguments.last())).apply { - setCallArguments(this, arguments) - } - } - } - } else { - fun IrBuilderWithScope.fieldReceiver(arguments: List) = when { - field.isStatic -> - null - expression.dispatchReceiver != null -> - irImplicitCast(irGetField(irGet(arguments[0]), backingField), field.parentAsClass.defaultType) - else -> - irImplicitCast(irGet(arguments[1]), field.parentAsClass.defaultType) - } - - referenceClass.addOverride(get!!) { arguments -> - irGetField(fieldReceiver(arguments), field) - } - - if (!field.isFinal) { - referenceClass.addOverride(set!!) { arguments -> - irSetField(fieldReceiver(arguments), field, irGet(arguments.last())) - } - } - } - return referenceClass - } - - private fun addConstructor(expression: IrCallableReference<*>, referenceClass: IrClass, superClass: IrClass) { - // See propertyReferenceKindFor -- only one of them could ever be present. - val hasBoundReceiver = expression.dispatchReceiver != null || expression.extensionReceiver != null - val numOfSuperArgs = - (if (hasBoundReceiver) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0) - val superConstructor = superClass.constructors.single { it.valueParameters.size == numOfSuperArgs } - - if (!useOptimizedSuperClass) { - referenceClass.addSimpleDelegatingConstructor(superConstructor, context.irBuiltIns, isPrimary = true) - return - } - - referenceClass.addConstructor { - origin = JvmLoweredDeclarationOrigin.GENERATED_MEMBER_IN_CALLABLE_REFERENCE - isPrimary = true - }.apply { - if (hasBoundReceiver) { - addValueParameter("receiver", context.irBuiltIns.anyNType) - } - body = context.createJvmIrBuilder(symbol).run { - irBlockBody(startOffset, endOffset) { - +irDelegatingConstructorCall(superConstructor).apply { - var index = 0 - if (hasBoundReceiver) { - putValueArgument(index++, irGet(valueParameters.first())) - } - val callee = expression.symbol.owner as IrDeclaration - val owner = buildReflectedContainerReferenceKClass(expression) - putValueArgument(index++, kClassToJavaClass(owner, backendContext)) - putValueArgument(index++, irString(expression.referencedName.asString())) - putValueArgument(index++, computeSignatureString(expression)) - putValueArgument(index, irInt(FunctionReferenceLowering.getCallableReferenceTopLevelFlag(callee))) - } - +IrInstanceInitializerCallImpl(startOffset, endOffset, referenceClass.symbol, context.irBuiltIns.unitType) - } - } - } - } - }) + override fun visitClassNew(declaration: IrClass): IrStatement { + val data = ClassData() + val parentClassData = currentClassData + currentClassData = data + declaration.transformChildrenVoid() + currentClassData = parentClassData // Put the new field at the beginning so that static delegated properties with initializers work correctly. // Since we do not cache property references, the new field does not reference anything else. - if (kProperties.isNotEmpty()) { - irClass.declarations.add(0, kPropertiesField.apply { - parent = irClass - initializer = context.createJvmIrBuilder(irClass.symbol).run { - val initializers = kProperties.values.sortedBy { it.index }.map { it.initializer } + if (data.kProperties.isNotEmpty()) { + declaration.declarations.add(0, data.kPropertiesField.apply { + parent = declaration + initializer = context.createJvmIrBuilder(declaration.symbol).run { + val initializers = data.kProperties.values.sortedBy { it.index }.map { it.initializer } irExprBody(irArrayOf(kPropertiesFieldType, initializers)) } }) - context.localDelegatedProperties[irClass.attributeOwnerId] = - kProperties.keys.filterIsInstance() + context.localDelegatedProperties[declaration.attributeOwnerId] = + data.kProperties.keys.filterIsInstance() + } + return declaration + } + + override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement { + localPropertyIndices[declaration.getter.symbol] = currentClassData!!.localPropertiesInClass++ + return super.visitLocalDelegatedProperty(declaration) + } + + override fun visitPropertyReference(expression: IrPropertyReference): IrExpression = + cachedKProperty(expression) + + override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression = + cachedKProperty(expression) + + private fun cachedKProperty(expression: IrCallableReference<*>): IrExpression { + expression.transformChildrenVoid() + if (expression.origin != IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE) + return createSpecializedKProperty(expression) + + val data = currentClassData ?: throw AssertionError("property reference not in class: ${expression.render()}") + // For delegated properties, the getter and setter contain a reference each as the second argument to getValue + // and setValue. Since it's highly unlikely that anyone will call get/set on these, optimize for space. + return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { + val (_, index) = data.kProperties.getOrPut(expression.symbol) { + PropertyInstance(createReflectedKProperty(expression), data.kProperties.size) + } + irCall(arrayItemGetter).apply { + dispatchReceiver = irGetField(null, data.kPropertiesField) + putValueArgument(0, irInt(index)) + } + } + } + + // Create an instance of KProperty that uses Java reflection to locate the getter and the setter. This kind of reference + // does not support local variables or bound receivers (e.g. `Class()::field`) and is slower, but takes up less space. + // Example: `C::property` -> `Reflection.property1(PropertyReference1Impl(C::class, "property", "getProperty()LType;"))`. + private fun createReflectedKProperty(expression: IrCallableReference<*>): IrExpression { + val referenceKind = propertyReferenceKindFor(expression) + return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).run { + irCall(referenceKind.wrapper).apply { + val constructor = referenceKind.implSymbol.constructors.single { it.owner.valueParameters.size == 3 } + putValueArgument(0, irCall(constructor).apply { + putValueArgument(0, calculateOwner(expression.propertyContainer, this@PropertyReferenceLowering.context)) + putValueArgument(1, irString(expression.referencedName.asString())) + putValueArgument(2, computeSignatureString(expression)) + }) + } + } + } + + // Create an instance of KProperty that overrides the get() and set() methods to directly call getX() and setX() on the object. + // This is (relatively) fast, but space-inefficient. Also, the instances can store bound receivers in their fields. Example: + // + // class C$property$0 : PropertyReference0Impl { + // constructor(boundReceiver: C) : super(boundReceiver, C::class.java, "property", "getProperty()LType;", 0) + // override fun get(): T = receiver.property + // override fun set(value: T) { receiver.property = value } + // } + // + // and then `C()::property` -> `C$property$0(C())`. + // + private fun createSpecializedKProperty(expression: IrCallableReference<*>): IrExpression { + val referenceClass = createKPropertySubclass(expression) + return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset).irBlock { + +referenceClass + +irCall(referenceClass.constructors.single()).apply { + var index = 0 + expression.dispatchReceiver?.let { putValueArgument(index++, it) } + expression.extensionReceiver?.let { putValueArgument(index++, it) } + } + } + } + + private fun createKPropertySubclass(expression: IrCallableReference<*>): IrClass { + val kind = propertyReferenceKindFor(expression) + val superClass = if (useOptimizedSuperClass) kind.implSymbol.owner else kind.interfaceSymbol.owner + val referenceClass = context.irFactory.buildClass { + setSourceRange(expression) + name = SpecialNames.NO_NAME_PROVIDED + origin = JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE + visibility = DescriptorVisibilities.LOCAL + }.apply { + parent = currentDeclarationParent!! + superTypes = listOf(superClass.defaultType) + createImplicitParameterDeclarationWithWrappedDescriptor() + }.copyAttributes(expression) + + addConstructor(expression, referenceClass, superClass) + + if (!useOptimizedSuperClass) { + val getName = superClass.functions.single { it.name.asString() == "getName" } + val getOwner = superClass.functions.single { it.name.asString() == "getOwner" } + val getSignature = superClass.functions.single { it.name.asString() == "getSignature" } + referenceClass.addOverride(getName) { irString(expression.referencedName.asString()) } + referenceClass.addOverride(getOwner) { calculateOwner(expression.propertyContainer, this@PropertyReferenceLowering.context) } + referenceClass.addOverride(getSignature) { computeSignatureString(expression) } + } + + 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" } + + val field = expression.field?.owner + if (field == null) { + fun IrBuilderWithScope.setCallArguments(call: IrCall, arguments: List) { + var index = 1 + call.copyTypeArgumentsFrom(expression) + call.dispatchReceiver = call.symbol.owner.dispatchReceiverParameter?.let { + if (expression.dispatchReceiver != null) + irImplicitCast(irGetField(irGet(arguments[0]), backingField), it.type) + else + irImplicitCast(irGet(arguments[index++]), it.type) + } + call.extensionReceiver = call.symbol.owner.extensionReceiverParameter?.let { + if (expression.extensionReceiver != null) + irImplicitCast(irGetField(irGet(arguments[0]), backingField), it.type) + else + irImplicitCast(irGet(arguments[index++]), it.type) + } + } + + expression.getter?.owner?.let { getter -> + referenceClass.addOverride(get!!) { arguments -> + irGet(getter.returnType, null, getter.symbol).apply { + setCallArguments(this, arguments) + } + } + referenceClass.addFakeOverride(invoke!!) + } + + expression.setter?.owner?.let { setter -> + referenceClass.addOverride(set!!) { arguments -> + irSet(setter.returnType, null, setter.symbol, irGet(arguments.last())).apply { + setCallArguments(this, arguments) + } + } + } + } else { + fun IrBuilderWithScope.fieldReceiver(arguments: List) = when { + field.isStatic -> + null + expression.dispatchReceiver != null -> + irImplicitCast(irGetField(irGet(arguments[0]), backingField), field.parentAsClass.defaultType) + else -> + irImplicitCast(irGet(arguments[1]), field.parentAsClass.defaultType) + } + + referenceClass.addOverride(get!!) { arguments -> + irGetField(fieldReceiver(arguments), field) + } + + if (!field.isFinal) { + referenceClass.addOverride(set!!) { arguments -> + irSetField(fieldReceiver(arguments), field, irGet(arguments.last())) + } + } + } + return referenceClass + } + + private fun addConstructor(expression: IrCallableReference<*>, referenceClass: IrClass, superClass: IrClass) { + // See propertyReferenceKindFor -- only one of them could ever be present. + val hasBoundReceiver = expression.dispatchReceiver != null || expression.extensionReceiver != null + val numOfSuperArgs = + (if (hasBoundReceiver) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0) + val superConstructor = superClass.constructors.single { it.valueParameters.size == numOfSuperArgs } + + if (!useOptimizedSuperClass) { + referenceClass.addSimpleDelegatingConstructor(superConstructor, context.irBuiltIns, isPrimary = true) + return + } + + referenceClass.addConstructor { + origin = JvmLoweredDeclarationOrigin.GENERATED_MEMBER_IN_CALLABLE_REFERENCE + isPrimary = true + }.apply { + if (hasBoundReceiver) { + addValueParameter("receiver", context.irBuiltIns.anyNType) + } + body = context.createJvmIrBuilder(symbol).run { + irBlockBody(startOffset, endOffset) { + +irDelegatingConstructorCall(superConstructor).apply { + var index = 0 + if (hasBoundReceiver) { + putValueArgument(index++, irGet(valueParameters.first())) + } + val callee = expression.symbol.owner as IrDeclaration + val owner = calculateOwnerKClass(expression.propertyContainer, backendContext) + putValueArgument(index++, kClassToJavaClass(owner, backendContext)) + putValueArgument(index++, irString(expression.referencedName.asString())) + putValueArgument(index++, computeSignatureString(expression)) + putValueArgument(index, irInt(FunctionReferenceLowering.getCallableReferenceTopLevelFlag(callee))) + } + +IrInstanceInitializerCallImpl(startOffset, endOffset, referenceClass.symbol, context.irBuiltIns.unitType) + } + } } } }