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:
+242
-246
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user