org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression.kt Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.CompilerVersionOfApiDeprecation
import org.jetbrains.kotlin.DeprecatedCompilerApi
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrParameterKind
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.symbols.IrBindableSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrFakeOverrideSymbolBase
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.resolveFakeOverrideMaybeAbstractOrFail
import org.jetbrains.kotlin.ir.util.transformInPlace
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.utils.setSize
// This class is not autogenerated to for the sake refactoring IR parameters - see KT-68003.
// However, it must be kept in sync with [org.jetbrains.kotlin.ir.generator.IrTree.memberAccessExpression].
abstract class IrMemberAccessExpression : IrDeclarationReference() {
abstract override val symbol: S
abstract var origin: IrStatementOrigin?
/**
* A list of all value arguments.
*
* It corresponds 1 to 1 with [IrFunction.parameters], and therefore should have the same size.
* `null` value usually means that the default value of the corresponding parameter will be used.
*/
val arguments: ValueArgumentsList = ValueArgumentsList()
// Those properties indicate the shape of `this.symbol.owner`. They are filled
// when the element is created, and whenever `symbol` changes. They are used
// to enable usage of old argument API, which embeds partial information of
// target's shape, on top of new API, which doesn't.
// There are two exceptions:
// - If `symbol` is unbound, they represent the expected shape of the target.
// It should become actual when the symbol is bound.
// - On calls to insert/removeDispatch/extensionReceiver, `targetHas*Receiver` is overridden.
// In that case, we assume the call knows better than the declarations itself on whether
// it has a particular receiver parameter or not. This is usually because the receiver
// has been added/removed to the declaration after the call to it has been created.
// In that situation it would not be possible to signal back to all calls that the shape
// was changed. Even more, it is possible that a receiver argument is added to a call
// slightly before the corresponding receiver parameter is added to a declaration.
// In order not to break such code, we assume the shape specified on call is,
// or will eventually be right.
internal var targetContextParameterCount: Int = 0
private set
internal var targetHasDispatchReceiver: Boolean = false
private set
internal var targetHasExtensionReceiver: Boolean = false
private set
private var targetRegularParameterCount: Int = 0
internal fun initializeTargetShapeExplicitly(
hasDispatchReceiver: Boolean,
hasExtensionReceiver: Boolean,
contextParameterCount: Int,
regularParameterCount: Int,
isFromTargetUpdate: Boolean = false,
) {
if (isFromTargetUpdate) {
require(hasDispatchReceiver == targetHasDispatchReceiver)
{ "New symbol has different shape w.r.t. dispatch receiver" }
require(hasExtensionReceiver == targetHasExtensionReceiver)
{ "New symbol has different shape w.r.t. extension receiver" }
require(regularParameterCount + contextParameterCount == targetRegularParameterCount + targetContextParameterCount)
{ "New symbol has different shape w.r.t. value parameter count" }
}
// Make a snapshot of current arguments, segregated by kind.
var i = 0
val existingNonReceiverParameters = ArrayList(targetRegularParameterCount + targetContextParameterCount)
val existingDispatchReceiver = if (targetHasDispatchReceiver) arguments[i++] else null
existingNonReceiverParameters += arguments.subList(i, i + targetContextParameterCount)
i += targetContextParameterCount
val existingExtensionReceiver = if (targetHasExtensionReceiver) arguments[i++] else null
existingNonReceiverParameters += arguments.subList(i, i + targetRegularParameterCount)
targetHasDispatchReceiver = hasDispatchReceiver
targetHasExtensionReceiver = hasExtensionReceiver
targetContextParameterCount = contextParameterCount
targetRegularParameterCount = regularParameterCount
val newAllParametersCount =
(if (targetHasDispatchReceiver) 1 else 0) +
targetContextParameterCount +
(if (targetHasExtensionReceiver) 1 else 0) +
targetRegularParameterCount
// Reapply the snapshot to [arguments]. In case we received different numbers of individual
// argument kinds than it was before, either trim them or fill with nulls.
arguments.clear()
arguments.ensureCapacity(newAllParametersCount)
existingNonReceiverParameters.setSize(regularParameterCount + contextParameterCount)
if (hasDispatchReceiver) {
arguments.add(existingDispatchReceiver)
}
arguments.addAll(existingNonReceiverParameters.take(contextParameterCount))
if (hasExtensionReceiver) {
arguments.add(existingExtensionReceiver)
}
arguments.addAll(existingNonReceiverParameters.drop(contextParameterCount))
}
internal fun initializeTargetShapeFromSymbol(isFromTargetUpdate: Boolean = false) {
@Suppress("UNCHECKED_CAST")
val target = (symbol as IrBindableSymbol<*, IrSymbolOwner>).getRealOwner()
when (target) {
is IrFunction -> {
var hasDispatchReceiver = false
var hasExtensionReceiver = false
var contextParameterCount = 0
var regularParameterCount = 0
for (param in target.parameters) {
when (param.kind) {
IrParameterKind.DispatchReceiver -> hasDispatchReceiver = true
IrParameterKind.ExtensionReceiver -> hasExtensionReceiver = true
IrParameterKind.Context -> contextParameterCount++
IrParameterKind.Regular -> regularParameterCount++
}
}
initializeTargetShapeExplicitly(
hasDispatchReceiver,
hasExtensionReceiver,
contextParameterCount,
regularParameterCount,
isFromTargetUpdate = isFromTargetUpdate,
)
}
is IrProperty -> {
val hasDispatchReceiver: Boolean
val hasExtensionReceiver: Boolean
val accessor = when (this) {
is IrPropertyReference -> (getter ?: setter)?.getRealOwner()
is IrLocalDelegatedPropertyReference -> getter.owner
else -> error("Unexpected reference to a property from $this")
}
if (accessor != null) {
hasDispatchReceiver = accessor.dispatchReceiverParameter != null
hasExtensionReceiver = accessor.extensionReceiverParameter != null
} else {
val realProperty = target.resolveFakeOverrideMaybeAbstractOrFail()
hasDispatchReceiver = !realProperty.backingField!!.isStatic
hasExtensionReceiver = false
}
initializeTargetShapeExplicitly(
hasDispatchReceiver,
hasExtensionReceiver,
0,
0,
isFromTargetUpdate = isFromTargetUpdate,
)
}
}
}
protected fun updateTargetSymbol() {
initializeTargetShapeFromSymbol(isFromTargetUpdate = true)
}
/**
* Like [updateTargetSymbol], but doesn't validate that the initial shape is the same as that of the symbol.
*/
fun forceUpdateShapeFromTargetSymbol() {
initializeTargetShapeFromSymbol(isFromTargetUpdate = false)
}
private fun , D : IrSymbolOwner> S.getRealOwner(): D {
var symbol = this
while (symbol is IrFakeOverrideSymbolBase<*, *, *>) {
@Suppress("UNCHECKED_CAST")
symbol = symbol.originalSymbol as S
}
return symbol.owner
}
/**
* Number of those arguments that correspond to [IrParameterKind.Context] and [IrParameterKind.Regular] parameters.
*
* ##### This is a deprecated API!
* - If you want a number of _all_ arguments (which you should most of the time), use [arguments.size] instead.
* - If you want a number of a particular [IrParameterKind] of parameters, you have to reach out to the corresponding function.
*
* A drop-in replacement:
* ```
* symbol.owner.parameters.count { it.kind == IrParameterKind.Regular || it.kind == IrParameterKind.Context }
* ```
*
* Details on the API migration: KT-68003
*/
@DeprecatedCompilerApi(CompilerVersionOfApiDeprecation._2_1_20)
val valueArgumentsCount: Int
get() = targetRegularParameterCount + targetContextParameterCount
/**
* Argument corresponding to the [IrParameterKind.DispatchReceiver] parameter, if any.
*
* When assigning a value other than `null`, it is `require`d that the callee has a corresponding dispatch receiver parameter.
*
* ##### Note on usage
* Please try to use [arguments] instead, unless usage of [dispatchReceiver] makes for a cleaner/simpler code.
*
* For example, the given pseudocode:
* ```kotlin
* if (call.symbol.owner.dispatchReceiverParameter != null)
* gen.invokevirtual(call.arguments[0])
* else
* gen.invokestatic()
* ```
* can be written more concisely:
* ```kotlin
* call.dispatchReceiver
* ?.let { gen.invokevirtual(it) }
* ?: gen.invokestatic()
* ```
*
* Counter example: when dealing with calls of known shape, please just use [arguments] with hardcoded indices:
* ```kotlin
* call.arguments[0] = ...
* call.arguments[1] = ...
* ```
* instead of:
* ```kotlin
* call.dispatchReceiver = ...
* call.arguments[1] = ...
* ```
*
* In future, if we evaluate [dispatchReceiver] is not really useful (provides little win over just reaching to
* function.dispatchReceiverParameter and then using [arguments]), it will be deprecated.
*
* Details on the API migration: KT-68003
*/
var dispatchReceiver: IrExpression?
get() {
return if (targetHasDispatchReceiver) {
arguments[0]
} else {
null
}
}
set(value) {
if (targetHasDispatchReceiver) {
arguments[0] = value
} else {
require(value == null) {
"${this.javaClass.simpleName} has no argument slot for the corresponding dispatch receiver parameter. " +
"If you are sure you want to add it (most likely for when the parameter has been added to the function since), " +
"use insertDispatchReceiver() instead."
}
}
}
/**
* Argument corresponding to the [IrParameterKind.ExtensionReceiver] parameter, if any.
*
* ##### This is a deprecated API!
* Only use [arguments] instead. If you need to know the meaning of the arguments, reach out to the corresponding function's parameters.
* A drop-in replacement:
* ```
* arguments[symbol.owner.parameters.indexOfFirst { it.kind == IrParameterKind.ExtensionReceiver }]
* ```
*
* Details on the API migration: KT-68003
*/
@DeprecatedCompilerApi(CompilerVersionOfApiDeprecation._2_1_20)
var extensionReceiver: IrExpression?
get() {
return if (targetHasExtensionReceiver) {
val index = getExtensionReceiverIndex()
arguments[index]
} else {
null
}
}
set(value) {
if (targetHasExtensionReceiver) {
arguments[getExtensionReceiverIndex()] = value
} else {
require(value == null) {
"${this.javaClass.simpleName} has no argument slot for the corresponding extension receiver parameter. " +
"If you are sure you want to add it (most likely for when the parameter has been added to the function since), " +
"use insertExtensionReceiver() instead."
}
}
}
private fun getExtensionReceiverIndex(): Int {
return (if (targetHasDispatchReceiver) 1 else 0) + targetContextParameterCount
}
/**
* Methods below should be used to add/remove a receiver argument in correspondence to target function/property,
* whose parameters changed _after_ this [IrMemberAccessExpression] was created.
*
* It can happen, e.g., in a lowering phase that modifies an existing function in-place by adding a `dispatchReceiverParameter`,
* then adjusts all calls to that function by setting the corresponding `dispatchReceiver` argument.
* In that case, you need to call [insertDispatchReceiver] method rather setting the argument directly via `dispatchReceiver` property.
*
* In practice, it is expected to be a very rare situation.
*
* #### Reasoning
*
* [KT-70054](https://youtrack.jetbrains.com/issue/KT-70054) introduces a single [arguments] list, that also
* includes dispatch and extension receivers.
* The existing (old) API will be implemented on top of that list for the period of migration. Then:
* - `dispatchReceiver = ...` means `arguments[0] = ...`
* - `insertDispatchReceiver(...)` means `arguments.add(0, ...)`
* - `removeDispatchReceiver(...)` means `arguments.removeAt(0)`
*
* We need a way to distinguish those two cases so that [arguments] list stays intact with the corresponding [IrFunction.parameters] list
* (i.e., so that they have the same sizes).
*
* Note: before the migration is finished you still need to use those methods, rather than `arguments.add()` / `arguments.removeAt()`
* directly, in order for the old API to continue working.
*/
/**
* Adds a dispatch receiver to [IrMemberAccessExpression], whose target function/property has gotten
* the corresponding dispatch receiver parameter some time _after_ the [IrMemberAccessExpression] was created.
*
* For details see the comments above.
*/
fun insertDispatchReceiver(value: IrExpression?) {
if (targetHasDispatchReceiver) {
arguments[0] = value
} else {
arguments.add(0, value)
targetHasDispatchReceiver = true
}
}
/**
* Removes a dispatch receiver from [IrMemberAccessExpression], whose target function/property has lost
* the corresponding dispatch receiver parameter some time _after_ the [IrMemberAccessExpression] was created.
*
* For details see the comments above.
*/
fun removeDispatchReceiver() {
if (targetHasDispatchReceiver) {
arguments.removeAt(0)
targetHasDispatchReceiver = false
}
}
/**
* Adds an extension receiver to [IrMemberAccessExpression], whose target function/property has gotten
* the corresponding extension receiver parameter some time _after_ the [IrMemberAccessExpression] was created.
*
* For details see the comments above.
*/
fun insertExtensionReceiver(value: IrExpression?) {
val index = getExtensionReceiverIndex()
if (targetHasExtensionReceiver) {
arguments[index] = value
} else {
arguments.add(index, value)
targetHasExtensionReceiver = true
}
}
/**
* Removes an extension receiver from [IrMemberAccessExpression], whose target function/property has lost
* the corresponding extension receiver parameter some time _after_ the [IrMemberAccessExpression] was created.
*
* For details see the comments above.
*/
fun removeExtensionReceiver() {
if (targetHasExtensionReceiver) {
arguments.removeAt(getExtensionReceiverIndex())
targetHasExtensionReceiver = false
}
}
/**
* Gets one of the arguments that correspond to either [IrParameterKind.Context] or [IrParameterKind.Regular] parameters.
*
* This is, [index] corresponds to [IrFunction.valueParameters] list, and not [IrFunction.parameters], which also includes
* receiver parameters.
*
* ##### This is a deprecated API!
* Use [arguments] instead.
*
* E.g. for code
* ```
* call.getValueArgument(parameter.indexInOldValueParameters)
* ```
*
* the replacement should be
* ```
* call.arguments[parameter.indexInParameters]
* ```
* If you need to know the [IrParameterKind] of the argument, reach out to the corresponding function's parameter.
*
* Details on the API migration: KT-68003
*/
@DeprecatedCompilerApi(CompilerVersionOfApiDeprecation._2_1_20)
fun getValueArgument(index: Int): IrExpression? {
val actualIndex = getRealValueArgumentIndex(index)
checkArgumentSlotAccess("value", actualIndex, this.arguments.size)
return this.arguments[actualIndex]
}
/**
* Sets one of arguments that correspond to [IrParameterKind.Context] or [IrParameterKind.Regular] parameters.
*
* This is, [index] corresponds to [IrFunction.valueParameters] list, and not [IrFunction.parameters], which also includes
* receiver parameters.
*
* ##### This is a deprecated API!
* Use [arguments] instead.
*
* E.g. for code
* ```
* call.putValueArgument(parameter.indexInOldValueParameters, ...)
* ```
*
* the replacement should be
* ```
* call.arguments[parameter.indexInParameters] = ...
* ```
* If you need to know the [IrParameterKind] of the arguments, reach out to the corresponding function's parameter.
*
* Details on the API migration: KT-68003
*/
@DeprecatedCompilerApi(CompilerVersionOfApiDeprecation._2_1_20)
fun putValueArgument(index: Int, valueArgument: IrExpression?) {
val actualIndex = getRealValueArgumentIndex(index)
checkArgumentSlotAccess("value", actualIndex, this.arguments.size)
this.arguments[actualIndex] = valueArgument
}
private fun getRealValueArgumentIndex(index: Int): Int =
(if (targetHasDispatchReceiver) 1 else 0) +
(if (targetHasExtensionReceiver && index >= targetContextParameterCount) 1 else 0) +
index
protected abstract val typeArguments: Array
val typeArgumentsCount: Int
get() = typeArguments.size
fun getTypeArgument(index: Int): IrType? {
checkArgumentSlotAccess("type", index, typeArguments.size)
return typeArguments[index]
}
fun putTypeArgument(index: Int, type: IrType?) {
checkArgumentSlotAccess("type", index, typeArguments.size)
typeArguments[index] = type
}
override fun acceptChildren(visitor: IrElementVisitor, data: D) {
arguments.forEach { it?.accept(visitor, data) }
}
override fun transformChildren(transformer: IrElementTransformer, data: D) {
arguments.transformInPlace(transformer, data)
}
inner class ValueArgumentsList : ArrayList() {
operator fun get(parameter: IrValueParameter): IrExpression? {
checkIndexingByParameter(parameter)
return this[parameter.indexInParameters]
}
operator fun set(parameter: IrValueParameter, value: IrExpression?): IrExpression? {
checkIndexingByParameter(parameter)
return this.set(parameter.indexInParameters, value)
}
private fun checkIndexingByParameter(parameter: IrValueParameter) {
require(parameter.parent == symbol.owner) {
"Attempting to access argument corresponding to a parameter of different function.\n" +
"This IR element references ${symbol.owner.render()}, while asking about a parameter of ${parameter.parent.render()}"
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy