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.
This commit is contained in:
pyos
2020-10-09 09:36:49 +02:00
committed by Alexander Udalov
parent d7b9920eba
commit 2974004de4
@@ -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 "<v#$N>" attached to the containing class.
// This maps properties to values of N.
private val localPropertyIndices = mutableMapOf<IrSymbol, Int>()
@@ -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("<v#$it>") }
?: 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 <signature-string> 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("<v#$index>")
}
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 <signature-string> 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<IrValueParameter>) -> 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<IrSymbol, PropertyInstance>()
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<IrValueParameter>) {
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<IrValueParameter>) = 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<IrLocalDelegatedPropertySymbol>()
context.localDelegatedProperties[declaration.attributeOwnerId] =
data.kProperties.keys.filterIsInstance<IrLocalDelegatedPropertySymbol>()
}
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<IrValueParameter>) {
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<IrValueParameter>) = 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)
}
}
}
}
}