[JVM_IR] Optimize properties delegated to const val

We can omit `get` call to delegated property and inline
constant value directly. If we are not going to
do that, we can get a runtime exception because
all usages of const property will be inlined and the property
itself will be dropped.

#KT-63567 Fixed
#KT-63580 Fixed
This commit is contained in:
Ivan Kylchik
2023-11-27 14:09:49 +01:00
committed by Space Team
parent 1f97c268e1
commit 2b6845c032
24 changed files with 221 additions and 14 deletions
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.util.shallowCopyOrNull
import org.jetbrains.kotlin.ir.util.statements
internal val IrSimpleFunction.returnsResultOfStdlibCall: Boolean
@@ -68,3 +69,14 @@ internal fun IrProperty.getSingletonOrConstantForOptimizableDelegatedProperty():
fun IrProperty.isJvmOptimizableDelegate(): Boolean =
isDelegated && !isFakeOverride && backingField != null && // fast path
(getPropertyReferenceForOptimizableDelegatedProperty() != null || getSingletonOrConstantForOptimizableDelegatedProperty() != null)
internal val IrMemberAccessExpression<*>.constInitializer: IrExpression?
get() {
if (this !is IrPropertyReference) return null
val constPropertyField = if (field == null) {
symbol.owner.takeIf { it.isConst }?.backingField
} else {
field!!.owner.takeIf { it.isFinal && it.isStatic }
}
return constPropertyField?.initializer?.expression?.shallowCopyOrNull()
}
@@ -41,9 +41,14 @@ private class PropertyReferenceDelegationLowering(val context: JvmBackendContext
}
private class PropertyReferenceDelegationTransformer(val context: JvmBackendContext) : IrElementTransformerVoid() {
private fun IrSimpleFunction.accessorBody(delegate: IrPropertyReference, receiverFieldOrExpression: IrStatement?) =
private fun IrSimpleFunction.accessorBody(delegate: IrPropertyReference, receiverFieldOrExpression: IrStatement?): IrBody =
context.createIrBuilder(symbol, startOffset, endOffset).run {
val value = valueParameters.singleOrNull()?.let(::irGet)
val isGetter = value == null
if (isGetter) {
delegate.constInitializer?.let { return@run irExprBody(it) }
}
var boundReceiver = when (receiverFieldOrExpression) {
null -> null
is IrField -> irGetField(dispatchReceiverParameter?.let(::irGet), receiverFieldOrExpression)
@@ -54,7 +59,7 @@ private class PropertyReferenceDelegationTransformer(val context: JvmBackendCont
val unboundReceiver = extensionReceiverParameter ?: dispatchReceiverParameter
val field = delegate.field?.owner
val access = if (field == null) {
val accessor = if (value == null) delegate.getter!! else delegate.setter!!
val accessor = if (isGetter) delegate.getter!! else delegate.setter!!
irCall(accessor).apply {
// This has the same assumptions about receivers as `PropertyReferenceLowering.propertyReferenceKindFor`:
// only one receiver can be bound, and if the property has both, the extension receiver cannot be bound.
@@ -72,7 +77,7 @@ private class PropertyReferenceDelegationTransformer(val context: JvmBackendCont
}
} else {
val receiver = if (field.isStatic) null else boundReceiver ?: irGet(unboundReceiver!!)
if (value == null) irGetField(receiver, field) else irSetField(receiver, field, value)
if (isGetter) irGetField(receiver, field) else irSetField(receiver, field, value!!)
}
irExprBody(access)
}
@@ -67,17 +67,6 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : IrEle
private val IrMemberAccessExpression<*>.field: IrFieldSymbol?
get() = (this as? IrPropertyReference)?.field
private val IrMemberAccessExpression<*>.constInitializer: IrExpression?
get() {
if (this !is IrPropertyReference) return null
val constPropertyField = if (field == null) {
symbol.owner.takeIf { it.isConst }?.backingField
} else {
field!!.owner.takeIf { it.isFinal && it.isStatic }
}
return constPropertyField?.initializer?.expression?.shallowCopyOrNull()
}
private val arrayItemGetter =
context.ir.symbols.array.owner.functions.single { it.name.asString() == "get" }