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
|
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.IrElementTransformerVoidWithContext
|
||||||
import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor
|
import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor
|
||||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
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.common.phaser.makeIrFilePhase
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
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.createJvmIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.irArrayOf
|
import org.jetbrains.kotlin.backend.jvm.ir.irArrayOf
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.needsAccessor
|
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.IrSimpleTypeImpl
|
||||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
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.JavaDescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -54,7 +52,7 @@ internal val propertyReferencePhase = makeIrFilePhase(
|
|||||||
prerequisite = setOf(functionReferencePhase, suspendLambdaPhase)
|
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.
|
// Reflection metadata for local properties is serialized under the signature "<v#$N>" attached to the containing class.
|
||||||
// This maps properties to values of N.
|
// This maps properties to values of N.
|
||||||
private val localPropertyIndices = mutableMapOf<IrSymbol, Int>()
|
private val localPropertyIndices = mutableMapOf<IrSymbol, Int>()
|
||||||
@@ -69,11 +67,6 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
|
|||||||
private val IrMemberAccessExpression<*>.field: IrFieldSymbol?
|
private val IrMemberAccessExpression<*>.field: IrFieldSymbol?
|
||||||
get() = (this as? IrPropertyReference)?.field
|
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 =
|
private val arrayItemGetter =
|
||||||
context.ir.symbols.array.owner.functions.single { it.name.asString() == "get" }
|
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
|
return current.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrBuilderWithScope.buildReflectedContainerReference(expression: IrMemberAccessExpression<*>): IrExpression =
|
// Plain Java fields do not have a getter, but can be referenced nonetheless. The signature should be the one
|
||||||
calculateOwner(expression.propertyContainer, this@PropertyReferenceLowering.context)
|
// that a getter would have, if it existed.
|
||||||
|
private val IrField.fakeGetterSignature: String
|
||||||
private fun JvmIrBuilder.buildReflectedContainerReferenceKClass(expression: IrMemberAccessExpression<*>): IrExpression =
|
get() = "${JvmAbi.getterName(name.asString())}()${context.methodSignatureMapper.mapReturnType(this)}"
|
||||||
calculateOwnerKClass(expression.propertyContainer, backendContext)
|
|
||||||
|
|
||||||
private fun IrBuilderWithScope.computeSignatureString(expression: IrMemberAccessExpression<*>): IrExpression {
|
private fun IrBuilderWithScope.computeSignatureString(expression: IrMemberAccessExpression<*>): IrExpression {
|
||||||
return expression.getter?.let { getter ->
|
if (expression is IrLocalDelegatedPropertyReference) {
|
||||||
localPropertyIndices[getter]?.let { irString("<v#$it>") }
|
// Local delegated properties are stored as a plain list, and the runtime library extracts the index from this string:
|
||||||
?: irCall(signatureStringIntrinsic).apply {
|
val index = localPropertyIndices[expression.getter] ?: throw AssertionError("no index for ${expression.render()}")
|
||||||
// Work around for differences between `RuntimeTypeMapper.KotlinProperty` and the real Kotlin type mapper.
|
return irString("<v#$index>")
|
||||||
// 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
|
val getter = expression.getter ?: return irString(expression.field!!.owner.fakeGetterSignature)
|
||||||
// is no getter method in the bytecode. In that case we need to avoid inline class mangling for the
|
// Work around for differences between `RuntimeTypeMapper.KotlinProperty` and the real Kotlin type mapper.
|
||||||
// function reference used in the <signature-string> intrinsic.
|
// 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
|
||||||
// Note that we cannot compute the signature at this point, since we still need to mangle the names of
|
// is no getter method in the bytecode. In that case we need to avoid inline class mangling for the
|
||||||
// private properties in multifile-part classes.
|
// function reference used in the <signature-string> intrinsic.
|
||||||
val needsDummySignature =
|
//
|
||||||
getter.owner.correspondingPropertySymbol?.owner?.needsAccessor(getter.owner) == false ||
|
// Note that we cannot compute the signature at this point, since we still need to mangle the names of
|
||||||
// Internal underlying vals of inline classes have no getter method
|
// private properties in multifile-part classes.
|
||||||
getter.owner.isInlineClassFieldGetter && getter.owner.visibility == DescriptorVisibilities.INTERNAL
|
val needsDummySignature = getter.owner.correspondingPropertySymbol?.owner?.needsAccessor(getter.owner) == false ||
|
||||||
|
// Internal underlying vals of inline classes have no getter method
|
||||||
putValueArgument(
|
getter.owner.isInlineClassFieldGetter && getter.owner.visibility == DescriptorVisibilities.INTERNAL
|
||||||
0,
|
val origin = if (needsDummySignature) InlineClassAbi.UNMANGLED_FUNCTION_REFERENCE else null
|
||||||
IrFunctionReferenceImpl(
|
val reference = IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, expression.type, getter, 0, getter, origin)
|
||||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, expression.type, getter, 0, getter,
|
return irCall(signatureStringIntrinsic).apply { putValueArgument(0, reference) }
|
||||||
if (needsDummySignature) InlineClassAbi.UNMANGLED_FUNCTION_REFERENCE else null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} ?: irString(expression.field!!.owner.signature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrClass.addOverride(method: IrSimpleFunction, buildBody: IrBuilderWithScope.(List<IrValueParameter>) -> IrExpression) =
|
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)
|
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 kProperties = mutableMapOf<IrSymbol, PropertyInstance>()
|
||||||
val kPropertiesField = context.irFactory.buildField {
|
val kPropertiesField = context.irFactory.buildField {
|
||||||
name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME)
|
name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME)
|
||||||
@@ -202,222 +189,231 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
|
|||||||
visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY
|
visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY
|
||||||
}
|
}
|
||||||
var localPropertiesInClass = 0
|
var localPropertiesInClass = 0
|
||||||
|
}
|
||||||
|
|
||||||
irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
|
private var currentClassData: ClassData? = null
|
||||||
override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement {
|
|
||||||
localPropertyIndices[declaration.getter.symbol] = localPropertiesInClass++
|
|
||||||
return super.visitLocalDelegatedProperty(declaration)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression =
|
override fun lower(irFile: IrFile) =
|
||||||
cachedKProperty(expression)
|
irFile.transformChildrenVoid()
|
||||||
|
|
||||||
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression =
|
override fun visitClassNew(declaration: IrClass): IrStatement {
|
||||||
cachedKProperty(expression)
|
val data = ClassData()
|
||||||
|
val parentClassData = currentClassData
|
||||||
private fun cachedKProperty(expression: IrCallableReference<*>): IrExpression {
|
currentClassData = data
|
||||||
expression.transformChildrenVoid()
|
declaration.transformChildrenVoid()
|
||||||
if (expression.origin != IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE)
|
currentClassData = parentClassData
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Put the new field at the beginning so that static delegated properties with initializers work correctly.
|
// 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.
|
// Since we do not cache property references, the new field does not reference anything else.
|
||||||
if (kProperties.isNotEmpty()) {
|
if (data.kProperties.isNotEmpty()) {
|
||||||
irClass.declarations.add(0, kPropertiesField.apply {
|
declaration.declarations.add(0, data.kPropertiesField.apply {
|
||||||
parent = irClass
|
parent = declaration
|
||||||
initializer = context.createJvmIrBuilder(irClass.symbol).run {
|
initializer = context.createJvmIrBuilder(declaration.symbol).run {
|
||||||
val initializers = kProperties.values.sortedBy { it.index }.map { it.initializer }
|
val initializers = data.kProperties.values.sortedBy { it.index }.map { it.initializer }
|
||||||
irExprBody(irArrayOf(kPropertiesFieldType, initializers))
|
irExprBody(irArrayOf(kPropertiesFieldType, initializers))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
context.localDelegatedProperties[irClass.attributeOwnerId] =
|
context.localDelegatedProperties[declaration.attributeOwnerId] =
|
||||||
kProperties.keys.filterIsInstance<IrLocalDelegatedPropertySymbol>()
|
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