[atomicfu-JVM]: Commonization of K/N and JVM (part 2)

The following transformations were commonized:

* generation of the transformed atomic extension signature
* building atomic array fields (arrays are now supported on both backends JVM and K/N)
* transformation of function calls on atomic array elements
* Generation and invocation of atomic extensions (the only difference is generation of synthetic value parameters)
* Changed synthetic parameters passed to the generated atomic extensions

See, KT-60528

Merge-request: KT-MR-11249
Merged-by: Maria Sokolova <maria.sokolova@jetbrains.com>
This commit is contained in:
mvicsokolova
2023-07-28 11:45:10 +00:00
committed by Space Team
parent 8363bae527
commit b6f8991072
20 changed files with 810 additions and 701 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlinx.atomicfu.compiler.backend.common
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveType
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrBuiltIns
@@ -17,15 +18,13 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.ir.util.getSimpleFunction
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance
@@ -44,19 +43,76 @@ abstract class AbstractAtomicSymbols(
return IrConstructorCallImpl.fromSymbolOwner(volatileAnnotationConstructor.returnType, volatileAnnotationConstructor.symbol)
}
abstract val atomicIntArrayClassSymbol: IrClassSymbol
abstract val atomicLongArrayClassSymbol: IrClassSymbol
abstract val atomicRefArrayClassSymbol: IrClassSymbol
protected val ATOMIC_ARRAY_TYPES: Set<IrClassSymbol>
get() = setOf(
atomicIntArrayClassSymbol,
atomicLongArrayClassSymbol,
atomicRefArrayClassSymbol
)
fun isAtomicArrayHandlerType(valueType: IrType) = valueType.classOrNull in ATOMIC_ARRAY_TYPES
abstract fun getAtomicArrayConstructor(atomicArrayClassSymbol: IrClassSymbol): IrFunctionSymbol
fun getAtomicArrayClassByAtomicfuArrayType(atomicfuArrayType: IrType): IrClassSymbol =
when (atomicfuArrayType.classFqName?.shortName()?.asString()) {
"AtomicIntArray" -> atomicIntArrayClassSymbol
"AtomicLongArray" -> atomicLongArrayClassSymbol
"AtomicBooleanArray" -> atomicIntArrayClassSymbol
"AtomicArray" -> atomicRefArrayClassSymbol
else -> error("Unexpected atomicfu array type ${atomicfuArrayType.render()}.")
}
fun getAtomicArrayClassByValueType(valueType: IrType): IrClassSymbol =
when {
valueType == irBuiltIns.intType -> atomicIntArrayClassSymbol
valueType == irBuiltIns.booleanType -> atomicIntArrayClassSymbol
valueType == irBuiltIns.longType -> atomicLongArrayClassSymbol
!valueType.isPrimitiveType() -> atomicRefArrayClassSymbol
else -> error("No corresponding atomic array class found for the given value type ${valueType.render()}.")
}
fun getAtomicHandlerFunctionSymbol(atomicHandlerClass: IrClassSymbol, name: String): IrSimpleFunctionSymbol =
when (name) {
"<get-value>", "getValue" -> atomicHandlerClass.getSimpleFunction("get")
"<set-value>", "setValue", "lazySet" -> atomicHandlerClass.getSimpleFunction("set")
else -> atomicHandlerClass.getSimpleFunction(name)
} ?: error("No $name function found in ${atomicHandlerClass.owner.render()}")
abstract fun createBuilder(
symbol: IrSymbol,
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET
): AbstractAtomicfuIrBuilder
val invoke0Symbol = irBuiltIns.functionN(0).getSimpleFunction("invoke")!!
val invoke1Symbol = irBuiltIns.functionN(1).getSimpleFunction("invoke")!!
fun function0Type(returnType: IrType) = buildSimpleType(
irBuiltIns.functionN(0).symbol,
listOf(returnType)
)
fun function1Type(argType: IrType, returnType: IrType) = buildSimpleType(
irBuiltIns.functionN(1).symbol,
listOf(argType, returnType)
)
fun buildSimpleType(
symbol: IrClassifierSymbol,
typeParameters: List<IrType>
): IrSimpleType =
IrSimpleTypeImpl(
classifier = symbol,
hasQuestionMark = false,
arguments = typeParameters.map { makeTypeProjection(it, Variance.INVARIANT) },
annotations = emptyList()
)
object ATOMICFU_GENERATED_CLASS : IrDeclarationOriginImpl("ATOMICFU_GENERATED_CLASS", isSynthetic = true)
object ATOMICFU_GENERATED_FUNCTION : IrDeclarationOriginImpl("ATOMICFU_GENERATED_FUNCTION", isSynthetic = true)
object ATOMICFU_GENERATED_FIELD : IrDeclarationOriginImpl("ATOMICFU_GENERATED_FIELD", isSynthetic = true)
@@ -87,15 +143,4 @@ abstract class AbstractAtomicSymbols(
private fun buildAnnotationConstructor(annotationClass: IrClass): IrConstructor =
annotationClass.addConstructor { isPrimary = true }
private fun buildSimpleType(
symbol: IrClassifierSymbol,
typeParameters: List<IrType>
): IrSimpleType =
IrSimpleTypeImpl(
classifier = symbol,
hasQuestionMark = false,
arguments = typeParameters.map { makeTypeProjection(it, Variance.INVARIANT) },
annotations = emptyList()
)
}
@@ -15,10 +15,13 @@ import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.isInt
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
@@ -37,6 +40,13 @@ abstract class AbstractAtomicfuIrBuilder(
this.dispatchReceiver = dispatchReceiver?.deepCopyWithSymbols()
}
// atomicArr.get(index)
fun atomicGetArrayElement(atomicArrayClass: IrClassSymbol, receiver: IrExpression, index: IrExpression) =
irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicArrayClass, "get")).apply {
dispatchReceiver = receiver
putValueArgument(0, index)
}
fun irCallWithArgs(symbol: IrSimpleFunctionSymbol, dispatchReceiver: IrExpression?, extensionReceiver: IrExpression?, valueArguments: List<IrExpression?>) =
irCall(symbol).apply {
this.dispatchReceiver = dispatchReceiver
@@ -46,6 +56,13 @@ abstract class AbstractAtomicfuIrBuilder(
}
}
fun callAtomicExtension(
symbol: IrSimpleFunctionSymbol,
dispatchReceiver: IrExpression?,
syntheticValueArguments: List<IrExpression?>,
valueArguments: List<IrExpression?>
) = irCallWithArgs(symbol, dispatchReceiver, null, syntheticValueArguments + valueArguments)
fun irVolatileField(
name: String,
type: IrType,
@@ -66,6 +83,57 @@ abstract class AbstractAtomicfuIrBuilder(
this.parent = parentContainer
}
fun irAtomicArrayField(
name: Name,
arrayClass: IrClassSymbol,
isStatic: Boolean,
annotations: List<IrConstructorCall>,
size: IrExpression,
dispatchReceiver: IrExpression?,
parentContainer: IrDeclarationContainer
): IrField =
context.irFactory.buildField {
this.name = name
type = arrayClass.defaultType
this.isFinal = true
this.isStatic = isStatic
visibility = DescriptorVisibilities.PRIVATE
origin = AbstractAtomicSymbols.ATOMICFU_GENERATED_FIELD
}.apply {
this.initializer = IrExpressionBodyImpl(
newAtomicArray(arrayClass, size, dispatchReceiver)
)
this.annotations = annotations
this.parent = parentContainer
}
abstract fun newAtomicArray(
atomicArrayClass: IrClassSymbol,
size: IrExpression,
dispatchReceiver: IrExpression?
): IrFunctionAccessExpression
// atomicArr.compareAndSet(index, expect, update)
fun callAtomicArray(
arrayClassSymbol: IrClassSymbol,
functionName: String,
dispatchReceiver: IrExpression?,
index: IrExpression,
valueArguments: List<IrExpression?>,
isBooleanReceiver: Boolean
): IrCall {
val irCall = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(arrayClassSymbol, functionName)).apply {
this.dispatchReceiver = dispatchReceiver
putValueArgument(0, index) // array element index
valueArguments.forEachIndexed { index, arg ->
// as AtomicBooleanArray is represented with AtomicIntArray,
// boolean arguments should be cast to int
putValueArgument(index + 1, if (isBooleanReceiver) arg?.toInt() else arg)
}
}
return if (isBooleanReceiver && irCall.type.isInt()) irCall.toBoolean() else irCall
}
fun buildClassInstance(
irClass: IrClass,
parentContainer: IrDeclarationContainer,
@@ -89,6 +157,7 @@ abstract class AbstractAtomicfuIrBuilder(
}
fun IrExpression.toBoolean() = irNotEquals(this, irInt(0)) as IrCall
fun IrExpression.toInt() = irIfThenElse(irBuiltIns.intType, irEquals(this, irBoolean(true)), irInt(1), irInt(0))
fun irClassWithPrivateConstructor(
name: String,
@@ -156,7 +225,7 @@ abstract class AbstractAtomicfuIrBuilder(
private fun IrProperty.addGetter(isStatic: Boolean, parentContainer: IrDeclarationContainer, irBuiltIns: IrBuiltIns) {
val property = this
val field = requireNotNull(backingField) { "BackingField of the property $property should not be null"}
val field = requireNotNull(backingField) { "The backing field of the property $property should not be null."}
addGetter {
visibility = property.visibility
returnType = field.type
@@ -189,7 +258,7 @@ abstract class AbstractAtomicfuIrBuilder(
private fun IrProperty.addSetter(isStatic: Boolean, parentClass: IrDeclarationContainer, irBuiltIns: IrBuiltIns) {
val property = this
val field = requireNotNull(property.backingField) { "BackingField of the property $property should not be null"}
val field = requireNotNull(property.backingField) { "The backing field of the property $property should not be null."}
this@addSetter.addSetter {
visibility = property.visibility
returnType = irBuiltIns.unitType
@@ -222,4 +291,9 @@ abstract class AbstractAtomicfuIrBuilder(
)
}
}
abstract fun atomicfuLoopBody(valueType: IrType, valueParameters: List<IrValueParameter>): IrBlockBody
abstract fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>): IrBlockBody
abstract fun atomicfuUpdateBody(functionName: String, valueType: IrType, valueParameters: List<IrValueParameter>): IrBlockBody
abstract fun atomicfuArrayUpdateBody(functionName: String, valueType: IrType, atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>): IrBlockBody
}
@@ -12,9 +12,13 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.builders.irSetField
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.*
@@ -25,6 +29,7 @@ import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
private const val ATOMICFU = "atomicfu"
@@ -37,6 +42,10 @@ private const val APPEND = "append"
private const val GET = "get"
private const val VOLATILE = "\$volatile"
private const val VOLATILE_WRAPPER_SUFFIX = "\$VolatileWrapper\$$ATOMICFU"
private const val LOOP = "loop"
private const val ACTION = "action\$$ATOMICFU"
private const val INDEX = "index\$$ATOMICFU"
private const val UPDATE = "update"
abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
@@ -77,8 +86,10 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
private val ATOMIC_VALUE_TYPES = setOf("AtomicInt", "AtomicLong", "AtomicBoolean", "AtomicRef")
private val ATOMIC_ARRAY_TYPES = setOf("AtomicIntArray", "AtomicLongArray", "AtomicBooleanArray", "AtomicArray")
protected val atomicPropertyToVolatile = mutableMapOf<IrProperty, IrProperty>()
protected val propertyToAtomicHandler = mutableMapOf<IrProperty, IrProperty>()
// Maps atomicfu atomic property to the corresponding volatile property.
protected val atomicfuPropertyToVolatile = mutableMapOf<IrProperty, IrProperty>()
// Maps atomicfu property to the atomic handler (field updater/atomic array).
protected val atomicfuPropertyToAtomicHandler = mutableMapOf<IrProperty, IrProperty>()
fun transform(moduleFragment: IrModuleFragment) {
transformAtomicProperties(moduleFragment)
@@ -91,7 +102,6 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
}
protected abstract val atomicPropertiesTransformer: AtomicPropertiesTransformer
protected abstract val atomicExtensionsTransformer: AtomicExtensionTransformer
protected abstract val atomicFunctionsTransformer: AtomicFunctionCallTransformer
private fun transformAtomicProperties(moduleFragment: IrModuleFragment) {
@@ -102,7 +112,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
private fun transformAtomicExtensions(moduleFragment: IrModuleFragment) {
for (irFile in moduleFragment.files) {
irFile.transform(atomicExtensionsTransformer, null)
irFile.transform(AtomicExtensionTransformer(), null)
}
}
@@ -156,7 +166,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
}
atomicProperty.isAtomicArray() -> {
atomicProperty.checkVisibility()
parentContainer.addTransformedAtomicArray(atomicProperty, index)?.also {
parentContainer.addTransformedAtomicArray(atomicProperty, index).also {
declarationsToBeRemoved.add(atomicProperty)
}
}
@@ -167,26 +177,45 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
}
/**
* Generates a volatile property that can be atomically updated instead of the given atomic property
* and adds it to the parent class.
* Returns the volatile property.
* Builds a volatile property that can be atomically updated instead of the given atomicfu property,
* and replaces the original declaration in the parent class.
* Returns the new volatile property.
*/
abstract fun IrClass.addTransformedInClassAtomic(atomicProperty: IrProperty, index: Int): IrProperty
/**
* Generates a volatile property that can be atomically updated instead of the given static atomic property
* and adds it to the parent container.
* Returns the voaltile property.
* Builds a volatile property that can be atomically updated instead of the given static atomicfu property
* and replaces the original declaration in the parent container.
* Returns the new volatile property.
*/
abstract fun IrDeclarationContainer.addTransformedStaticAtomic(atomicProperty: IrProperty, index: Int): IrProperty
/**
* Generates an array that can be atomically updated instead of the given atomic array
* and adds it to the parent class.
* Returns the new property or null if transformation was skipped.
* NOTE: skipping transformation is supported for K/N backend, because arrays are currently not supported there.
* Builds an array that can be atomically updated instead of the given atomicfu atomic array
* and replaces the original declaration in the parent class.
* Returns the generated array.
* For JVM: atomic arrays are replaced with the corresponding java.util.concurrent.Atomic*Array
* For Native: atomic arrays are replaced with the corresponding kotlin.concurrent.Atomic*Array
* (In the future atomic arrays will be commonized in Kotlin stdlib)
*
* val intArr = kotlinx.atomicfu.AtomicIntArray(45) --> val intArr = java.util.concurrent.AtomicIntegerArray(45) // JVM
* val intArr = kotlinx.atomicfu.AtomicIntArray(45) --> val intArr = kotlin.concurrent.AtomicIntArray(45) // Native
*/
abstract fun IrDeclarationContainer.addTransformedAtomicArray(atomicProperty: IrProperty, index: Int): IrProperty?
private fun IrDeclarationContainer.addTransformedAtomicArray(atomicProperty: IrProperty, index: Int): IrProperty {
val parentContainer = this
with(atomicSymbols.createBuilder(atomicProperty.symbol)) {
val javaAtomicArrayField = buildAtomicArrayField(atomicProperty, parentContainer)
return parentContainer.replacePropertyAtIndex(
javaAtomicArrayField,
atomicProperty.visibility,
isVar = false,
isStatic = parentContainer is IrFile,
index
).also {
atomicfuPropertyToAtomicHandler[atomicProperty] = it
}
}
}
/**
* Transforms the given property that was delegated to the atomic property:
@@ -194,7 +223,8 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
*/
private fun IrDeclarationContainer.transformDelegatedAtomic(atomicProperty: IrProperty) {
val getDelegate = atomicProperty.backingField?.initializer?.expression
require(getDelegate is IrCall) { "Expected initializer of the delegated property ${this.render()} is IrCall but found ${getDelegate?.render()}" }
require(getDelegate is IrCall) { "Unexpected initializer of the delegated property ${atomicProperty.render()}: " +
"expected invocation of the delegate atomic property getter, but found ${getDelegate?.render()}." + CONSTRAINTS_MESSAGE }
val delegateVolatileField = when {
getDelegate.isAtomicFactoryCall() -> {
/**
@@ -223,11 +253,13 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
val delegate = getDelegate.getCorrespondingProperty()
check(delegate.parent == atomicProperty.parent) {
"The delegated property [${atomicProperty.render()}] declared in [${atomicProperty.parent.render()}] should be declared in the same scope " +
"as the corresponding atomic property [${delegate.render()}] declared in [${delegate.parent.render()}]." + CONSTRAINTS_MESSAGE}
val volatileProperty = atomicPropertyToVolatile[delegate] ?: error("The delegate property was not transformed: ${delegate.render()}.")
volatileProperty.backingField ?: error("Transformed atomic field should have a non-null backingField.")
"as the corresponding atomic property [${delegate.render()}] declared in [${delegate.parent.render()}]" + CONSTRAINTS_MESSAGE}
val volatileProperty = atomicfuPropertyToVolatile[delegate]
?: error("No generated volatile property was found for the delegate atomic property ${delegate.render()}")
volatileProperty.backingField
?: error("Volatile property ${volatileProperty.render()} corresponding to the atomic property ${delegate.render()} should have a non-null backingField")
}
else -> error("Unexpected initializer of the delegated property ${this.render()}")
else -> error("Unexpected initializer of the delegated property ${getDelegate.render()}" + CONSTRAINTS_MESSAGE)
}
atomicProperty.getter?.transformAccessor(delegateVolatileField)
atomicProperty.setter?.transformAccessor(delegateVolatileField)
@@ -242,18 +274,20 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
dispatchReceiverParameter?.capture()
}
with(atomicSymbols.createBuilder(symbol)) {
body = irExprBody(
if (this@transformAccessor.isGetter) {
irGetField(dispatchReceiver, delegateVolatileField)
} else {
irSetField(dispatchReceiver, delegateVolatileField, this@transformAccessor.valueParameters[0].capture())
}
)
body = irBlockBody {
+irReturn(
if (this@transformAccessor.isGetter) {
irGetField(dispatchReceiver, delegateVolatileField)
} else {
irSetField(dispatchReceiver, delegateVolatileField, this@transformAccessor.valueParameters[0].capture())
}
)
}
}
}
/**
* Generates a private volatile field initialized with the initial value of the given atomic property:
* Builds a private volatile field initialized with the initial value of the given atomic property:
* private val a = atomic(0) --> private @Volatile a: Int = 0
*/
protected fun AbstractAtomicfuIrBuilder.buildVolatileBackingField(
@@ -261,13 +295,13 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
parentContainer: IrDeclarationContainer,
tweakBooleanToInt: Boolean
): IrField {
val atomicField = requireNotNull(atomicProperty.backingField) { "BackingField of atomic property $atomicProperty should not be null." }
val fieldType = atomicField.type.atomicToPrimitiveType()
val atomicField = requireNotNull(atomicProperty.backingField) { "The backing field of the atomic property ${atomicProperty.render()} declared in ${parentContainer.render()} should not be null." + CONSTRAINTS_MESSAGE }
val fieldType = (atomicField.type as IrSimpleType).atomicToPrimitiveType()
val initializer = atomicField.initializer?.expression
if (initializer == null) {
val initBlock = atomicField.getInitBlockForField(parentContainer)
val initExprWithIndex = initBlock.getInitExprWithIndexFromInitBlock(atomicField.symbol)
?: error("Expected property ${atomicProperty.render()} initialization in init block ${initBlock.render()}.")
?: error("The atomic property ${atomicProperty.render()} was not initialized neither at the declaration, nor in the init block." + CONSTRAINTS_MESSAGE)
val atomicFactoryCall = initExprWithIndex.value.value
val initExprIndex = initExprWithIndex.index
val initValue = atomicFactoryCall.getAtomicFactoryValueArgument()
@@ -296,6 +330,56 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
}
}
/**
* Builds an atomic array field initialized with the initial size of the given atomic array,
* the generated field has the same visibility as the original atomic array:
* internal val intArr = kotlinx.atomicfu.AtomicIntArray --> internal val intArr = j.u.c.a.AtomicIntegerArray // JVM
* internal val intArr = kotlin.concurrent.AtomicIntArray // Native
*/
private fun AbstractAtomicfuIrBuilder.buildAtomicArrayField(
atomicProperty: IrProperty,
parentContainer: IrDeclarationContainer
): IrField {
val atomicArrayField =
requireNotNull(atomicProperty.backingField) { "The backing field of the atomic array ${atomicProperty.render()} should not be null." + CONSTRAINTS_MESSAGE }
val initializer = atomicArrayField.initializer?.expression
if (initializer == null) {
// replace field initialization in the init block
val initBlock = atomicArrayField.getInitBlockForField(parentContainer)
// property initialization order in the init block matters -> transformed initializer should be placed at the same position
val initExprWithIndex = initBlock.getInitExprWithIndexFromInitBlock(atomicArrayField.symbol)
?: error("The atomic array ${atomicProperty.render()} was not initialized neither at the declaration, nor in the init block." + CONSTRAINTS_MESSAGE)
val atomicFactoryCall = initExprWithIndex.value.value
val initExprIndex = initExprWithIndex.index
val arraySize = atomicFactoryCall.getArraySizeArgument()
return irAtomicArrayField(
atomicArrayField.name,
atomicSymbols.getAtomicArrayClassByAtomicfuArrayType(atomicArrayField.type),
atomicArrayField.isStatic,
atomicArrayField.annotations,
arraySize,
(atomicFactoryCall as IrFunctionAccessExpression).dispatchReceiver,
parentContainer
).also {
val initExpr = it.initializer?.expression
?: error("The generated atomic array field ${it.render()} should've already be initialized." + CONSTRAINTS_MESSAGE)
it.initializer = null
initBlock.updateFieldInitialization(atomicArrayField.symbol, it.symbol, initExpr, initExprIndex)
}
} else {
val arraySize = initializer.getArraySizeArgument()
return irAtomicArrayField(
atomicArrayField.name,
atomicSymbols.getAtomicArrayClassByAtomicfuArrayType(atomicArrayField.type),
atomicArrayField.isStatic,
atomicArrayField.annotations,
arraySize,
(initializer as IrFunctionAccessExpression).dispatchReceiver,
parentContainer
)
}
}
/**
* In case if atomic property is initialized in init block it's declaration is replaced with the volatile property
* and initialization of the backing field is also performed in the init block:
@@ -306,7 +390,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
* _a = atomic(0) _a = 0
* } }
*/
protected fun IrAnonymousInitializer.getInitExprWithIndexFromInitBlock(
private fun IrAnonymousInitializer.getInitExprWithIndexFromInitBlock(
oldFieldSymbol: IrFieldSymbol
): IndexedValue<IrSetField>? =
body.statements.withIndex().singleOrNull { it.value is IrSetField && (it.value as IrSetField).symbol == oldFieldSymbol }?.let {
@@ -314,7 +398,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
it as IndexedValue<IrSetField>
}
protected fun IrAnonymousInitializer.updateFieldInitialization(
private fun IrAnonymousInitializer.updateFieldInitialization(
oldFieldSymbol: IrFieldSymbol,
volatileFieldSymbol: IrFieldSymbol,
initExpr: IrExpression,
@@ -331,7 +415,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
}
}
protected fun IrField.getInitBlockForField(parentContainer: IrDeclarationContainer): IrAnonymousInitializer {
private fun IrField.getInitBlockForField(parentContainer: IrDeclarationContainer): IrAnonymousInitializer {
for (declaration in parentContainer.declarations) {
if (declaration is IrAnonymousInitializer) {
if (declaration.body.statements.any { it is IrSetField && it.symbol == this.symbol }) {
@@ -362,22 +446,6 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
)
}
// atomic(value = 0) -> 0
private fun IrExpression.getAtomicFactoryValueArgument(): IrExpression {
require(this is IrCall) { "Expected atomic factory invocation but found: ${this.render()}." }
return getValueArgument(0)?.deepCopyWithSymbols()
?: error("Atomic factory should take at least one argument: ${this.render()}.")
}
// AtomicIntArray(size = 10) -> 10
protected fun IrExpression.getArraySizeArgument(): IrExpression {
require(this is IrFunctionAccessExpression) {
"Expected atomic array factory invocation, but found: ${this.render()}."
}
return getValueArgument(0)?.deepCopyWithSymbols()
?: error("Atomic array factory should take at least one argument: ${this.render()}.")
}
private fun IrProperty.checkVisibility() =
check((visibility == DescriptorVisibilities.PRIVATE || visibility == DescriptorVisibilities.INTERNAL) ||
(parent is IrClass &&
@@ -402,7 +470,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
}
}
protected abstract inner class AtomicExtensionTransformer : IrElementTransformerVoid() {
private inner class AtomicExtensionTransformer : IrElementTransformerVoid() {
override fun visitFile(declaration: IrFile): IrFile {
declaration.transformAllAtomicExtensions()
return super.visitFile(declaration)
@@ -413,7 +481,57 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
return super.visitClass(declaration)
}
abstract fun IrDeclarationContainer.transformAllAtomicExtensions()
private fun IrDeclarationContainer.transformAllAtomicExtensions() {
declarations.filter { it is IrFunction && it.isAtomicExtension() }.forEach { atomicExtension ->
atomicExtension as IrFunction
declarations.add(transformAtomicExtension(atomicExtension, this, false))
declarations.add(transformAtomicExtension(atomicExtension, this, true))
// the original atomic extension is removed
declarations.remove(atomicExtension)
}
}
private fun transformAtomicExtension(
atomicExtension: IrFunction,
parent: IrDeclarationContainer,
isArrayReceiver: Boolean
): IrFunction {
/**
* At this step, only signature of the atomic extension is changed,
* the body is just copied and will be transformed at the next step by AtomicFunctionCallTransformer.
*
* Two different signatures are generated for the case of atomic property receiver
* and for the case of atomic array element receiver, due to different atomic updaters.
*
* Generated signatures for JVM:
* inline fun AtomicInt.foo(arg: Int) --> inline fun foo$atomicfu(dispatchReceiver: Any?, atomicHandler: AtomicIntegerFieldUpdater, arg': Int)
* inline fun foo$atomicfu$array(dispatchReceiver: Any?, atomicHandler: AtomicIntegerArray, index: Int, arg': Int)
*
* Generated signatures for Native:
* inline fun AtomicInt.foo(arg: Int) --> inline fun foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, arg': Int)
* * inline fun foo$atomicfu$array(atomicArray: AtomicIntegerArray, index: Int, arg': Int)
*/
return buildTransformedAtomicExtensionSignature(atomicExtension, isArrayReceiver).apply {
body = atomicExtension.body?.deepCopyWithSymbols(this)
body?.transform(
object : IrElementTransformerVoid() {
override fun visitReturn(expression: IrReturn): IrExpression = super.visitReturn(
if (expression.returnTargetSymbol == atomicExtension.symbol) {
with(atomicSymbols.createBuilder(this@apply.symbol)) {
irReturn(expression.value)
}
} else {
expression
}
)
}, null
)
this.parent = parent
// all usages of the old type parameters should be remapped to the new type parameters.
val typeRemapper = IrTypeParameterRemapper(atomicExtension.typeParameters.associateWith { this.typeParameters[it.index] })
remapTypes(typeRemapper)
}
}
}
protected abstract inner class AtomicFunctionCallTransformer : IrElementTransformer<IrFunction?> {
@@ -431,9 +549,13 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
// then valueType is the type argument of Atomic* class `String`
(it.type as IrSimpleType).arguments[0] as IrSimpleType
} else {
propertyGetterCall.type.atomicToPrimitiveType()
(propertyGetterCall.type as IrSimpleType).atomicToPrimitiveType()
}
val isArrayReceiver = when {
propertyGetterCall is IrCall -> propertyGetterCall.isArrayElementReceiver(data)
propertyGetterCall.isThisReceiver() -> data != null && data.name.asString().isMangledAtomicArrayExtension()
else -> error("Unsupported atomic array receiver ${propertyGetterCall.render()}" + CONSTRAINTS_MESSAGE)
}
val isArrayReceiver = propertyGetterCall.isArrayElementReceiver(data)
if (expression.symbol.owner.isFromKotlinxAtomicfuPackage()) {
/**
* Transform invocations of functions from kotlinx.atomicfu on atomics properties or atomic array elements:
@@ -506,35 +628,215 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
parentFunction: IrFunction?
): IrExpression
abstract fun transformAtomicUpdateCallOnArrayElement(
private fun transformAtomicUpdateCallOnArrayElement(
expression: IrCall,
functionName: String,
valueType: IrType,
getPropertyReceiver: IrExpression,
parentFunction: IrFunction?
): IrExpression
): IrExpression {
with(atomicSymbols.createBuilder(expression.symbol)) {
/**
* Atomic update call on the atomic array element is replaced
* with the call to the j.u.c.a.Atomic*Array (for JVM) or kotlin.concurrent.AtomicIntArray (for Native).
* The API of these classes is consistent -> this transformation is commonized.
*
* 1. Function call receiver is atomic property getter call.
*
* The call is delegated to the corresponding atomic array:
*
* val intArr = kotlinx.atomicfu.AtomicIntArray(10) val intArr = AtomicIntArray(10)
* <get-intArr>()[5].compareAndSet(0, 5) ---> intArr.compareAndSet(5, 0, 5)
*
*
* 2. Function is called in the body of the transformed atomic extension,
* the call receiver is <this> receiver of the original atomic extension:
*
* inline fun AtomicInt.foo(new: Int) { inline fun foo$atomicfu$array(atomicArray: AtomicIntArray, index: Int, arg': Int)
* this.getAndSet(value, new) ---> atomicArray.getAndSet(index, new)
* } }
*/
val getAtomicArray = getAtomicHandler(getPropertyReceiver, parentFunction)
return callAtomicArray(
arrayClassSymbol = getAtomicArray.type.classOrNull!!,
functionName = functionName,
dispatchReceiver = getAtomicArray,
index = getPropertyReceiver.getArrayElementIndex(parentFunction),
valueArguments = expression.valueArguments,
isBooleanReceiver = valueType.isBoolean()
)
}
}
abstract fun transformedAtomicfuInlineFunctionCall(
private fun transformedAtomicfuInlineFunctionCall(
expression: IrCall,
functionName: String,
valueType: IrType,
getPropertyReceiver: IrExpression,
isArrayReceiver: Boolean,
parentFunction: IrFunction?
): IrCall
): IrCall {
with(atomicSymbols.createBuilder(expression.symbol)) {
/**
* a.loop { value -> a.compareAndSet(value, 777) } -->
*
* inline fun <T> atomicfu$loop(dispatchReceiver: Any?, fu: AtomicIntegerFieldUpdater, action: (Int) -> Unit) {
* while (true) {
* val cur = fu.get()
* action(cur)
* }
* }
*
* a.atomicfu$loop(dispatchReceiver, fu) { ... }
*/
requireNotNull(parentFunction) { "Expected containing function of the call ${expression.render()}, but found null." + CONSTRAINTS_MESSAGE }
val loopFunc = parentFunction.parentDeclarationContainer.getOrBuildInlineLoopFunction(
functionName = functionName,
valueType = if (valueType.isBoolean()) irBuiltIns.intType else valueType,
isArrayReceiver = isArrayReceiver
)
// We have to copy this lambda, because it is passed to both foo$atomicfu and foo$atomicfu$array transformations,
// and otherwise causes "lambda is already bound" error in K/N.
val action = (expression.getValueArgument(0) as IrFunctionExpression).apply {
function.body?.transform(this@AtomicFunctionCallTransformer, parentFunction)
if (function.valueParameters[0].type.isBoolean()) {
function.valueParameters[0].type = irBuiltIns.intType
function.returnType = irBuiltIns.intType
}
}.deepCopyWithSymbols(parentFunction)
val syntheticArguments = buildSyntheticValueArgsForTransformedAtomicExtensionCall(expression, getPropertyReceiver, isArrayReceiver, parentFunction)
return irCallWithArgs(
symbol = loopFunc.symbol,
dispatchReceiver = parentFunction.containingFunction.dispatchReceiverParameter?.capture(),
extensionReceiver = null,
valueArguments = syntheticArguments + action
)
}
}
abstract fun transformAtomicExtensionCall(
private fun transformAtomicExtensionCall(
expression: IrCall,
originalAtomicExtension: IrSimpleFunction,
getPropertyReceiver: IrExpression,
isArrayReceiver: Boolean,
parentFunction: IrFunction?
): IrCall
): IrCall {
with(atomicSymbols.createBuilder(expression.symbol)) {
/**
* Atomic extension call is replaced with the call to transformed atomic extension:
*
* 1. Function call receiver is atomic property getter call.
* Transformation variant of the atomic extension is chosen according to the type of receiver
* (atomic property -> foo$atomicfu, atomic array element -> foo$atomicfu$array)
*
* For JVM:
*
* inline fun AtomicInt.foo(arg: Int) {..} inline fun foo$atomicfu(dispatchReceiver: Any?, fu: j.u.c.a.AtomicIntegerFieldUpdater, arg: Int)
* aClass._a.foo(arg) --> foo$atomicfu(aClass, _a$volatile$FU, arg)
*
* For Native:
* inline fun AtomicInt.foo(arg: Int) {..} inline foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, arg: Int)
* aClass._a.foo(arg) --> foo$atomicfu({_ -> aClass::_a}, arg)
*
* 2. Function is called in the body of the transformed atomic extension,
* the call receiver is the old <this> receiver of the extension.
* In this case value parameters captured from the parent function are passed as arguments.
*
* For JVM:
*
* inline fun AtomicInt.bar(new: Int) inline fun bar$atomicfu(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': Int)
* inline fun AtomicInt.foo(new: Int) { inline fun foo$atomicfu(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': Int)
* bar(new) ---> bar$atomicfu(dispatchReceiver, handler, new)
* }
*/
requireNotNull(parentFunction) { "Expected containing function of the call ${expression.render()}, but found null." }
val parent = originalAtomicExtension.parent as IrDeclarationContainer
val transformedAtomicExtension = parent.getOrBuildTransformedAtomicExtension(originalAtomicExtension, isArrayReceiver)
val syntheticValueArguments = buildSyntheticValueArgsForTransformedAtomicExtensionCall(expression, getPropertyReceiver, isArrayReceiver, parentFunction)
return callAtomicExtension(
symbol = transformedAtomicExtension.symbol,
dispatchReceiver = expression.dispatchReceiver,
syntheticValueArguments = syntheticValueArguments,
valueArguments = expression.valueArguments
)
}
}
abstract fun IrDeclarationContainer.getTransformedAtomicExtension(
private fun IrDeclarationContainer.getOrBuildInlineLoopFunction(
functionName: String,
valueType: IrType,
isArrayReceiver: Boolean
): IrSimpleFunction {
val parent = this
val mangledName = mangleAtomicExtensionName(functionName, isArrayReceiver)
findDeclaration<IrSimpleFunction> {
// check the type of the receiver here
it.name.asString() == mangledName && it.checkSyntheticParameterTypes(isArrayReceiver, valueType)
}?.let { return it }
return pluginContext.irFactory.buildFun {
name = Name.identifier(mangledName)
isInline = true
visibility = DescriptorVisibilities.PRIVATE
origin = AbstractAtomicSymbols.ATOMICFU_GENERATED_FUNCTION
}.apply {
dispatchReceiverParameter = (parent as? IrClass)?.thisReceiver?.deepCopyWithSymbols(this)
if (functionName == LOOP) {
if (isArrayReceiver) generateAtomicfuArrayLoop(valueType) else generateAtomicfuLoop(valueType)
} else {
if (isArrayReceiver) generateAtomicfuArrayUpdate(functionName, valueType) else generateAtomicfuUpdate(functionName, valueType)
}
this.parent = parent
parent.declarations.add(this)
}
}
private fun IrDeclarationContainer.getOrBuildTransformedAtomicExtension(
declaration: IrSimpleFunction,
isArrayReceiver: Boolean
): IrSimpleFunction
): IrSimpleFunction {
// Try find the transformed atomic extension in the parent container
findDeclaration<IrSimpleFunction> {
it.name.asString() == mangleAtomicExtensionName(declaration.name.asString(), isArrayReceiver) &&
it.isTransformedAtomicExtension()
}?.let { return it }
/**
* NOTE: this comment is applicable to the JVM backend incremental compialtion:
* If the transformed declaration is not found then the call may be performed from another module
* which depends on the module where declarations are generated from untransformed metadata (real transformed declarations are not there).
* This happens if the call is performed from the test module or in case of incremental compilation.
*
* We build a fake declaration here: it's signature equals the one of the real transformed declaration,
* it doesn't have body and won't be generated. It is placed in the call site and
* during compilation this fake declaration will be resolved to the real transformed declaration.
*/
return buildTransformedAtomicExtensionSignature(declaration, isArrayReceiver)
}
protected fun getAtomicHandler(atomicCallReceiver: IrExpression, parentFunction: IrFunction?): IrExpression =
when {
atomicCallReceiver is IrCall -> {
val isArrayReceiver = atomicCallReceiver.isArrayElementGetter()
val getAtomicProperty = if (isArrayReceiver) atomicCallReceiver.dispatchReceiver as IrCall else atomicCallReceiver
val atomicProperty = getAtomicProperty.getCorrespondingProperty()
val atomicHandlerProperty = atomicfuPropertyToAtomicHandler[atomicProperty]
?: error("No atomic handler found for the atomic property ${atomicProperty.render()}, \n" +
"these properties were registered: ${
atomicfuPropertyToAtomicHandler.keys.toList().joinToString("\n") { it.render() }
}" + CONSTRAINTS_MESSAGE)
with(atomicSymbols.createBuilder(atomicCallReceiver.symbol)) {
// dispatchReceiver for get-a$FU() is null, because a$FU is a static property
// dispatchReceiver for get-arr'() is equal to the dispatchReceiver of the original getter
irGetProperty(atomicHandlerProperty, if (isArrayReceiver) getAtomicProperty.dispatchReceiver else null)
}
}
atomicCallReceiver.isThisReceiver() -> {
requireNotNull(parentFunction) { "Expected containing function of the call with receiver ${atomicCallReceiver.render()}, but found null." + CONSTRAINTS_MESSAGE}
require(parentFunction.isTransformedAtomicExtension())
val isArrayReceiver = parentFunction.name.asString().isMangledAtomicArrayExtension()
if (isArrayReceiver) parentFunction.valueParameters[0].capture() else parentFunction.valueParameters[1].capture()
}
else -> error("Unexpected type of atomic function call receiver: ${atomicCallReceiver.render()} \n" + CONSTRAINTS_MESSAGE)
}
override fun visitGetValue(expression: IrGetValue, data: IrFunction?): IrExpression {
/**
@@ -561,9 +863,26 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
return super.visitGetValue(expression, data)
}
// Generates value arguments for the transformed atomic extension invocation.
abstract fun buildSyntheticValueArgsForTransformedAtomicExtensionCall(
expression: IrCall,
getPropertyReceiver: IrExpression,
isArrayReceiver: Boolean,
parentFunction: IrFunction
): List<IrExpression?>
abstract fun IrValueParameter.remapValueParameter(transformedExtension: IrFunction): IrValueParameter?
abstract fun IrFunction.isTransformedAtomicExtension(): Boolean
protected fun IrFunction.isTransformedAtomicExtension(): Boolean {
val isArrayReceiver = name.asString().isMangledAtomicArrayExtension()
return if (isArrayReceiver) checkSyntheticArrayElementExtensionParameter() else checkSyntheticAtomicExtensionParameters()
}
abstract fun IrFunction.checkSyntheticAtomicExtensionParameters(): Boolean
abstract fun IrFunction.checkSyntheticArrayElementExtensionParameter(): Boolean
abstract fun IrFunction.checkSyntheticParameterTypes(isArrayReceiver: Boolean, receiverValueType: IrType): Boolean
abstract fun IrExpression.isArrayElementReceiver(
parentFunction: IrFunction?
@@ -589,6 +908,53 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
}
return super.visitContainerExpression(expression, data)
}
private fun IrSimpleFunction.generateAtomicfuLoop(valueType: IrType) {
addSyntheticValueParametersToTransformedAtomicExtension(false, valueType)
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, irBuiltIns.unitType))
body = with(atomicSymbols.createBuilder(symbol)) {
atomicfuLoopBody(valueType, valueParameters)
}
returnType = irBuiltIns.unitType
}
private fun IrSimpleFunction.generateAtomicfuArrayLoop(valueType: IrType) {
val atomicfuArrayClass = atomicSymbols.getAtomicArrayClassByValueType(valueType)
addSyntheticValueParametersToTransformedAtomicExtension(true, valueType)
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, irBuiltIns.unitType))
body = with(atomicSymbols.createBuilder(symbol)) {
atomicfuArrayLoopBody(atomicfuArrayClass, valueParameters)
}
returnType = irBuiltIns.unitType
}
private fun IrSimpleFunction.generateAtomicfuUpdate(functionName: String, valueType: IrType) {
addSyntheticValueParametersToTransformedAtomicExtension(false, valueType)
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, valueType))
body = with(atomicSymbols.createBuilder(symbol)) {
atomicfuUpdateBody(
functionName,
valueType,
valueParameters
)
}
returnType = if (functionName == UPDATE) irBuiltIns.unitType else valueType
}
private fun IrSimpleFunction.generateAtomicfuArrayUpdate(functionName: String, valueType: IrType) {
val atomicfuArrayClass = atomicSymbols.getAtomicArrayClassByValueType(valueType)
addSyntheticValueParametersToTransformedAtomicExtension(true, valueType)
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, valueType))
body = with(atomicSymbols.createBuilder(symbol)) {
atomicfuArrayUpdateBody(
functionName,
valueType,
atomicfuArrayClass,
valueParameters
)
}
returnType = if (functionName == UPDATE) irBuiltIns.unitType else valueType
}
}
private inner class FinalTransformationChecker: IrElementTransformer<IrFunction?> {
@@ -611,6 +977,11 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
}
}
// Builds the signature of the transformed atomic extension.
abstract fun buildTransformedAtomicExtensionSignature(atomicExtension: IrFunction, isArrayReceiver: Boolean): IrSimpleFunction
// Adds value parameters for transformed inline extension functions.
abstract fun IrFunction.addSyntheticValueParametersToTransformedAtomicExtension(isArrayReceiver: Boolean, valueType: IrType)
// Util transformer functions
protected fun getStaticVolatileWrapperInstance(volatileWrapperClass: IrClass): IrExpression {
@@ -671,7 +1042,7 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
it.type.isAtomicArrayType() && symbol.owner.name.asString() == GET
} ?: false
protected fun IrType.atomicToPrimitiveType(): IrType =
protected fun IrSimpleType.atomicToPrimitiveType(): IrType =
when(classFqName?.shortName()?.asString()) {
"AtomicInt" -> irBuiltIns.intType
"AtomicLong" -> irBuiltIns.longType
@@ -704,14 +1075,42 @@ abstract class AbstractAtomicfuTransformer(val pluginContext: IrPluginContext) {
protected val IrDeclaration.parentDeclarationContainer: IrDeclarationContainer
get() = parents.filterIsInstance<IrDeclarationContainer>().firstOrNull()
?: error("In the sequence of parents for ${this.render()} no IrDeclarationContainer was found")
?: error("In the sequence of parents for ${this.render()} no IrDeclarationContainer was found" + CONSTRAINTS_MESSAGE)
protected val IrFunction.containingFunction: IrFunction
get() {
if (this.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) return this
return parents.filterIsInstance<IrFunction>().firstOrNull {
it.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
} ?: error("In the sequence of parents for the local function ${this.render()} no containing function was found")
} ?: error("In the sequence of parents for the local function ${this.render()} no containing function was found" + CONSTRAINTS_MESSAGE)
}
// atomic(value = 0) -> 0
protected fun IrExpression.getAtomicFactoryValueArgument(): IrExpression {
require(this is IrCall) { "Expected atomic factory invocation but found: ${this.render()}" }
return getValueArgument(0)?.deepCopyWithSymbols()
?: error("Atomic factory should take at least one argument: ${this.render()}" + CONSTRAINTS_MESSAGE)
}
// AtomicIntArray(size = 10) -> 10
protected fun IrExpression.getArraySizeArgument(): IrExpression {
require(this is IrFunctionAccessExpression) {
"Expected atomic array factory invocation, but found: ${this.render()}."
}
return getValueArgument(0)?.deepCopyWithSymbols()
?: error("Atomic array factory should take at least one argument: ${this.render()}" + CONSTRAINTS_MESSAGE)
}
protected fun IrExpression.getArrayElementIndex(parentFunction: IrFunction?): IrExpression =
when {
this is IrCall -> getValueArgument(0)!!
this.isThisReceiver() -> {
require(parentFunction != null)
val index = parentFunction.valueParameters[1]
require(index.name.asString() == INDEX && index.type == irBuiltIns.intType)
index.capture()
}
else -> error("Unexpected type of atomic receiver expression: ${this.render()} \n" + CONSTRAINTS_MESSAGE)
}
// A.kt -> A$VolatileWrapper$atomicfu
@@ -6,15 +6,14 @@
package org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm
import org.jetbrains.kotlin.backend.common.extensions.*
import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicSymbols
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuTransformer
@@ -23,10 +22,7 @@ import kotlin.collections.set
private const val ATOMICFU = "atomicfu"
private const val DISPATCH_RECEIVER = "dispatchReceiver\$$ATOMICFU"
private const val ATOMIC_HANDLER = "handler\$$ATOMICFU"
private const val ACTION = "action\$$ATOMICFU"
private const val INDEX = "index\$$ATOMICFU"
private const val LOOP = "loop"
private const val UPDATE = "update"
class AtomicfuJvmIrTransformer(
pluginContext: IrPluginContext,
@@ -36,9 +32,6 @@ class AtomicfuJvmIrTransformer(
override val atomicPropertiesTransformer: AtomicPropertiesTransformer
get() = JvmAtomicPropertiesTransformer()
override val atomicExtensionsTransformer: AtomicExtensionTransformer
get() = JvmAtomicExtensionTransformer()
override val atomicFunctionsTransformer: AtomicFunctionCallTransformer
get() = JvmAtomicFunctionCallTransformer()
@@ -80,27 +73,6 @@ class AtomicfuJvmIrTransformer(
return wrapperClass.addVolatilePropertyWithAtomicUpdater(atomicProperty, index)
}
override fun IrDeclarationContainer.addTransformedAtomicArray(atomicProperty: IrProperty, index: Int): IrProperty {
/**
* Atomic arrays are replaced with corresponding java.util.concurrent.Atomic*Array:
*
* val intArr = kotlinx.atomicfu.AtomicIntArray(45) --> val intArr = java.util.concurrent.AtomicIntegerArray(45)
*/
val parentContainer = this
with(atomicSymbols.createBuilder(atomicProperty.symbol)) {
val javaAtomicArrayField = buildJavaAtomicArrayField(atomicProperty, parentContainer)
return parentContainer.replacePropertyAtIndex(
javaAtomicArrayField,
atomicProperty.visibility,
isVar = false,
isStatic = parentContainer is IrFile,
index
).also {
propertyToAtomicHandler[atomicProperty] = it
}
}
}
private fun IrClass.addVolatilePropertyWithAtomicUpdater(from: IrProperty, index: Int): IrProperty {
/**
* Generates a volatile property and an atomic updater for this property,
@@ -133,58 +105,15 @@ class AtomicfuJvmIrTransformer(
} else {
parentClass.addProperty(volatileField, DescriptorVisibilities.PRIVATE, isVar = true, isStatic = false)
}
atomicPropertyToVolatile[from] = volatileProperty
atomicfuPropertyToVolatile[from] = volatileProperty
val atomicUpdaterField = irJavaAtomicFieldUpdater(volatileField, parentClass)
parentClass.addProperty(atomicUpdaterField, from.getMinVisibility(), isVar = false, isStatic = true).also {
propertyToAtomicHandler[from] = it
atomicfuPropertyToAtomicHandler[from] = it
}
return volatileProperty
}
}
private fun JvmAtomicfuIrBuilder.buildJavaAtomicArrayField(
atomicProperty: IrProperty,
parentContainer: IrDeclarationContainer
): IrField {
val atomicArrayField =
requireNotNull(atomicProperty.backingField) { "BackingField of atomic array $atomicProperty should not be null" }
val initializer = atomicArrayField.initializer?.expression
if (initializer == null) {
// replace field initialization in the init block
val initBlock = atomicArrayField.getInitBlockForField(parentContainer)
// property initialization order in the init block matters -> transformed initializer should be placed at the same position
val initExprWithIndex = initBlock.getInitExprWithIndexFromInitBlock(atomicArrayField.symbol)
?: error("Expected atomic array ${atomicProperty.render()} initialization in init block ${initBlock.render()}" + CONSTRAINTS_MESSAGE)
val atomicFactoryCall = initExprWithIndex.value.value
val initExprIndex = initExprWithIndex.index
val arraySize = atomicFactoryCall.getArraySizeArgument()
return irJavaAtomicArrayField(
atomicArrayField.name,
atomicSymbols.getAtomicArrayClassByAtomicfuArrayType(atomicArrayField.type),
atomicArrayField.isStatic,
atomicArrayField.annotations,
arraySize,
(atomicFactoryCall as IrFunctionAccessExpression).dispatchReceiver,
parentContainer
).also {
val initExpr = it.initializer?.expression ?: error("Initializer of the generated field ${it.render()} can not be null" + CONSTRAINTS_MESSAGE)
it.initializer = null
initBlock.updateFieldInitialization(atomicArrayField.symbol, it.symbol, initExpr, initExprIndex)
}
} else {
val arraySize = initializer.getArraySizeArgument()
return irJavaAtomicArrayField(
atomicArrayField.name,
atomicSymbols.getAtomicArrayClassByAtomicfuArrayType(atomicArrayField.type),
atomicArrayField.isStatic,
atomicArrayField.annotations,
arraySize,
(initializer as IrFunctionAccessExpression).dispatchReceiver,
parentContainer
)
}
}
private fun IrDeclarationContainer.getOrBuildVolatileWrapper(atomicProperty: IrProperty): IrClass {
val visibility = atomicProperty.getMinVisibility()
findDeclaration<IrClass> { it.isVolatileWrapper(visibility) && it.visibility == visibility }?.let { return it }
@@ -202,50 +131,6 @@ class AtomicfuJvmIrTransformer(
}
}
private inner class JvmAtomicExtensionTransformer : AtomicExtensionTransformer() {
override fun IrDeclarationContainer.transformAllAtomicExtensions() {
declarations.filter { it is IrFunction && it.isAtomicExtension() }.forEach { atomicExtension ->
atomicExtension as IrFunction
declarations.add(transformAtomicExtension(atomicExtension, this, false))
declarations.add(transformAtomicExtension(atomicExtension, this, true))
// the original atomic extension is removed
declarations.remove(atomicExtension)
}
}
private fun transformAtomicExtension(atomicExtension: IrFunction, parent: IrDeclarationContainer, isArrayReceiver: Boolean): IrFunction {
/**
* At this step, only signature of the atomic extension is changed,
* the body is just copied and will be transformed at the next step by JvmAtomicFunctionCallTransformer.
*
* Two different signatures are generated for the case of atomic property receiver
* and for the case of atomic array element receiver, due to different atomic updaters.
*
* inline fun AtomicInt.foo(arg: Int) --> inline fun foo$atomicfu(dispatchReceiver: Any?, atomicHandler: AtomicIntegerFieldUpdater, arg': Int)
* inline fun foo$atomicfu$array(dispatchReceiver: Any?, atomicHandler: AtomicIntegerArray, index: Int, arg': Int)
*/
return buildTransformedAtomicExtensionDeclaration(atomicExtension, isArrayReceiver).apply {
// the body will be transformed later by `AtomicFUTransformer`
body = atomicExtension.body?.deepCopyWithSymbols(this)
body?.transform(
object : IrElementTransformerVoid() {
override fun visitReturn(expression: IrReturn): IrExpression = super.visitReturn(
if (expression.returnTargetSymbol == atomicExtension.symbol) {
with(atomicSymbols.createBuilder(this@apply.symbol)) {
irReturn(expression.value)
}
} else {
expression
}
)
}, null
)
this.parent = parent
}
}
}
private inner class JvmAtomicFunctionCallTransformer : AtomicFunctionCallTransformer() {
override fun transformAtomicUpdateCallOnProperty(
@@ -288,174 +173,32 @@ class AtomicfuJvmIrTransformer(
}
}
override fun transformAtomicUpdateCallOnArrayElement(
override fun buildSyntheticValueArgsForTransformedAtomicExtensionCall(
expression: IrCall,
functionName: String,
valueType: IrType,
getPropertyReceiver: IrExpression,
parentFunction: IrFunction?
): IrCall {
with(atomicSymbols.createBuilder(expression.symbol)) {
/**
* Atomic update call on the atomic array element is replaced
* with the call on the j.u.c.a.Atomic*Array field:
*
* 1. Function call receiver is atomic property getter call.
*
* The call is replaced with the call on the corresponding field updater:
*
* val intArr = AtomicIntArray(10) val intArr = AtomicIntegerArray(10)
* <get-intArr>()[5].compareAndSet(0, 5) ---> intArr.compareAndSet(5, 0, 5)
*
*
* 2. Function is called in the body of the transformed atomic extension,
* the call receiver is the old <this> receiver of the extension:
*
* inline fun AtomicInt.foo(new: Int) { inline fun foo$atomicfu$array(dispatchReceiver: Any?, atomicHandler: AtomicIntegerArray, index: Int, arg': Int)
* this.getAndSet(value, new) ---> atomicHandler.getAndSet(index, new)
* } }
*/
val getJavaAtomicArray = getAtomicHandler(getPropertyReceiver, parentFunction)
return callAtomicArray(
arrayClassSymbol = getJavaAtomicArray.type.classOrNull!!,
functionName = functionName,
dispatchReceiver = getJavaAtomicArray,
index = getPropertyReceiver.getArrayElementIndex(parentFunction),
valueArguments = expression.valueArguments,
isBooleanReceiver = valueType.isBoolean()
)
}
}
override fun transformedAtomicfuInlineFunctionCall(
expression: IrCall,
functionName: String,
valueType: IrType,
getPropertyReceiver: IrExpression,
isArrayReceiver: Boolean,
parentFunction: IrFunction?
): IrCall {
with(atomicSymbols.createBuilder(expression.symbol)) {
val dispatchReceiver = getDispatchReceiver(getPropertyReceiver, parentFunction)
val getAtomicHandler = getAtomicHandler(getPropertyReceiver, parentFunction)
/**
* a.loop { value -> a.compareAndSet(value, 777) } -->
*
* inline fun <T> atomicfu$loop(atomicfu$handler: AtomicIntegerFieldUpdater, atomicfu$action: (Int) -> Unit, dispatchReceiver: Any?) {
* while (true) {
* val cur = atomicfu$handler.get()
* atomicfu$action(cur)
* }
* }
*
* a.atomicfu$loop(dispatchReceiver, atomicHandler) { ... }
*/
requireNotNull(parentFunction) { "Parent function of this call ${expression.render()} is null" + CONSTRAINTS_MESSAGE }
val loopFunc = parentFunction.parentDeclarationContainer.getOrBuildInlineLoopFunction(
functionName = functionName,
valueType = if (valueType.isBoolean()) irBuiltIns.intType else valueType,
isArrayReceiver = isArrayReceiver
)
val action = (expression.getValueArgument(0) as IrFunctionExpression).apply {
function.body?.transform(this@JvmAtomicFunctionCallTransformer, parentFunction)
if (function.valueParameters[0].type.isBoolean()) {
function.valueParameters[0].type = irBuiltIns.intType
function.returnType = irBuiltIns.intType
}
}
return irCallWithArgs(
symbol = loopFunc.symbol,
dispatchReceiver = parentFunction.containingFunction.dispatchReceiverParameter?.capture(),
extensionReceiver = null,
valueArguments = if (isArrayReceiver) {
val index = getPropertyReceiver.getArrayElementIndex(parentFunction)
listOf(getAtomicHandler, index, action)
} else {
listOf(getAtomicHandler, action, dispatchReceiver)
}
)
parentFunction: IrFunction
): List<IrExpression?> {
val dispatchReceiver = getDispatchReceiver(getPropertyReceiver, parentFunction)
val getAtomicHandler = getAtomicHandler(getPropertyReceiver, parentFunction)
return if (isArrayReceiver) {
val index = getPropertyReceiver.getArrayElementIndex(parentFunction)
listOf(getAtomicHandler, index)
} else {
listOf(dispatchReceiver, getAtomicHandler)
}
}
override fun transformAtomicExtensionCall(
expression: IrCall,
originalAtomicExtension: IrSimpleFunction,
getPropertyReceiver: IrExpression,
isArrayReceiver: Boolean,
parentFunction: IrFunction?
): IrCall {
with(atomicSymbols.createBuilder(expression.symbol)) {
/**
* Atomic extension call is replaced with the call to transformed atomic extension:
*
* 1. Function call receiver is atomic property getter call.
* Transformation variant of the atomic extension is chosen accorfin to the type of receiver
* (atomic property -> foo$atomicfu, atomic array element -> foo$atomicfu$array)
*
* inline fun AtomicInt.foo(atg: Int) {..}
* aClass._a.foo(arg) --> foo$atomicfu(aClass, _a$volatile$FU, arg)
*
*
* 2. Function is called in the body of the transformed atomic extension,
* the call receiver is the old <this> receiver of the extension.
* In this case value parameters captured from the parent function are passed as arguments.
*
* inline fun AtomicInt.bar(new: Int) inline fun bar$atomicfu(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': Int)
* inline fun AtomicInt.foo(new: Int) { inline fun foo$atomicfu(dispatchReceiver: Any?, handler: j.u.c.a.AtomicIntegerFieldUpdater, arg': Int)
* bar(new) ---> bar$atomicfu(dispatchReceiver, handler, new)
* } }
*/
val parent = originalAtomicExtension.parent as IrDeclarationContainer
val transformedAtomicExtension = parent.getTransformedAtomicExtension(originalAtomicExtension, isArrayReceiver)
val dispatchReceiver = getDispatchReceiver(getPropertyReceiver, parentFunction)
val getAtomicHandler = getAtomicHandler(getPropertyReceiver, parentFunction)
return callAtomicExtension(
symbol = transformedAtomicExtension.symbol,
dispatchReceiver = expression.dispatchReceiver,
syntheticValueArguments = if (isArrayReceiver) {
listOf(dispatchReceiver, getAtomicHandler, getPropertyReceiver.getArrayElementIndex(parentFunction))
} else {
listOf(dispatchReceiver, getAtomicHandler)
},
valueArguments = expression.valueArguments
)
}
}
override fun IrDeclarationContainer.getTransformedAtomicExtension(
declaration: IrSimpleFunction,
isArrayReceiver: Boolean
): IrSimpleFunction {
// Try find the transformed atomic extension in the parent container
findDeclaration<IrSimpleFunction> {
it.name.asString() == mangleAtomicExtensionName(declaration.name.asString(), isArrayReceiver) &&
it.isTransformedAtomicExtension()
}?.let { return it }
/**
* If the transformed declaration is not found then the call may be performed from another module
* which depends on the module where declarations are generated from untransformed metadata (real transformed declarations are not there).
* This happens if the call is performed from the test module or in case of incremental compilation.
*
* We build a fake declaration here: it's signature equals the one of the real transformed declaration,
* it doesn't have body and won't be generated. It is placed in the call site and
* during compilation this fake declaration will be resolved to the real transformed declaration.
*/
return buildTransformedAtomicExtensionDeclaration(declaration, isArrayReceiver)
}
override fun IrFunction.isTransformedAtomicExtension(): Boolean {
val isArrayReceiver = name.asString().isMangledAtomicArrayExtension()
return if (isArrayReceiver) checkSyntheticArrayElementExtensionParameter() else checkSyntheticAtomicExtensionParameters()
}
override fun IrValueParameter.remapValueParameter(transformedExtension: IrFunction): IrValueParameter? {
if (index < 0 && !type.isAtomicValueType()) {
// data is a transformed function
// index == -1 for `this` parameter
return transformedExtension.dispatchReceiverParameter ?: error("Dispatch receiver of ${transformedExtension.render()} is null" + CONSTRAINTS_MESSAGE)
return transformedExtension.dispatchReceiverParameter
?: error("During remapping of value parameters from the original atomic extension ${this.parent.render()} to the transformed one ${transformedExtension}:" +
"dispatchReceiver parameter (index == -1) was not found at the transformed extension." + CONSTRAINTS_MESSAGE)
}
if (index >= 0) {
val shift = if (transformedExtension.name.asString().isMangledAtomicArrayExtension()) 3 else 2
val shift = 2 // 2 synthetic value parameters
return transformedExtension.valueParameters[index + shift]
}
return null
@@ -485,32 +228,7 @@ class AtomicfuJvmIrTransformer(
parentFunction.valueParameters[0].capture()
} else null
}
else -> error("Unexpected type of atomic function call receiver: ${atomicCallReceiver.render()}" + CONSTRAINTS_MESSAGE)
}
private fun getAtomicHandler(atomicCallReceiver: IrExpression, parentFunction: IrFunction?): IrExpression =
when {
atomicCallReceiver is IrCall -> {
val isArrayReceiver = atomicCallReceiver.isArrayElementGetter()
val getAtomicProperty = if (isArrayReceiver) atomicCallReceiver.dispatchReceiver as IrCall else atomicCallReceiver
val atomicProperty = getAtomicProperty.getCorrespondingProperty()
val atomicHandlerProperty = propertyToAtomicHandler[atomicProperty]
?: error("No atomic handler found for the atomic property ${atomicProperty.render()}, \n" +
"these properties were registered: ${
propertyToAtomicHandler.keys.toList().joinToString("\n") { it.render() }
}" + CONSTRAINTS_MESSAGE)
with(atomicSymbols.createBuilder(atomicCallReceiver.symbol)) {
// dispatchReceiver for get-a$FU() is null, because a$FU is a static property
// dispatchReceiver for get-arr'() is equal to the dispatchReceiver of the original getter
irGetProperty(atomicHandlerProperty, if (isArrayReceiver) getAtomicProperty.dispatchReceiver else null)
}
}
atomicCallReceiver.isThisReceiver() -> {
requireNotNull(parentFunction) { "Containing function of the atomic call with <this> receiver should not be null" + CONSTRAINTS_MESSAGE}
require(parentFunction.isTransformedAtomicExtension())
parentFunction.valueParameters[1].capture()
}
else -> error("Unexpected type of atomic function call receiver: ${atomicCallReceiver.render()} \n" + CONSTRAINTS_MESSAGE)
else -> error("Unexpected type of atomic function call receiver: ${atomicCallReceiver.render()}." + CONSTRAINTS_MESSAGE)
}
private fun IrCall.getDispatchReceiver(): IrExpression? {
@@ -520,110 +238,48 @@ class AtomicfuJvmIrTransformer(
val dispatchReceiver = getAtomicProperty.dispatchReceiver
// top-level atomics
if (!isArrayReceiver && (dispatchReceiver == null || dispatchReceiver.type.isObject())) {
val volatileProperty = atomicPropertyToVolatile[atomicProperty]!!
val volatileProperty = atomicfuPropertyToVolatile[atomicProperty]!!
return getStaticVolatileWrapperInstance(volatileProperty.parentAsClass)
}
return dispatchReceiver
}
private fun IrExpression.getArrayElementIndex(parentFunction: IrFunction?): IrExpression =
when {
this is IrCall -> getValueArgument(0)!!
this.isThisReceiver() -> {
require(parentFunction != null)
parentFunction.valueParameters[2].capture()
}
else -> error("Unexpected type of atomic receiver expression: ${this.render()} \n" + CONSTRAINTS_MESSAGE)
}
private fun IrFunction.checkSyntheticArrayElementExtensionParameter(): Boolean {
if (valueParameters.size < 3) return false
return valueParameters[0].name.asString() == DISPATCH_RECEIVER && valueParameters[0].type == irBuiltIns.anyNType &&
valueParameters[1].name.asString() == ATOMIC_HANDLER && atomicSymbols.isAtomicArrayHandlerType(valueParameters[1].type) &&
valueParameters[2].name.asString() == INDEX && valueParameters[2].type == irBuiltIns.intType
override fun IrFunction.checkSyntheticArrayElementExtensionParameter(): Boolean {
if (valueParameters.size < 2) return false
return valueParameters[0].name.asString() == ATOMIC_HANDLER && atomicSymbols.isAtomicArrayHandlerType(valueParameters[0].type) &&
valueParameters[1].name.asString() == INDEX && valueParameters[1].type == irBuiltIns.intType
}
private fun IrFunction.checkSyntheticAtomicExtensionParameters(): Boolean {
override fun IrFunction.checkSyntheticAtomicExtensionParameters(): Boolean {
if (valueParameters.size < 2) return false
return valueParameters[0].name.asString() == DISPATCH_RECEIVER && valueParameters[0].type == irBuiltIns.anyNType &&
valueParameters[1].name.asString() == ATOMIC_HANDLER && atomicSymbols.isAtomicFieldUpdaterType(valueParameters[1].type)
}
private fun IrDeclarationContainer.getOrBuildInlineLoopFunction(
functionName: String,
valueType: IrType,
isArrayReceiver: Boolean
): IrSimpleFunction {
val parent = this
val mangledName = mangleAtomicExtensionName(functionName, isArrayReceiver)
val updaterType =
if (isArrayReceiver) atomicSymbols.getAtomicArrayType(valueType) else atomicSymbols.getFieldUpdaterType(valueType)
findDeclaration<IrSimpleFunction> {
it.name.asString() == mangledName && it.valueParameters[0].type == updaterType
}?.let { return it }
return pluginContext.irFactory.buildFun {
name = Name.identifier(mangledName)
isInline = true
visibility = DescriptorVisibilities.PRIVATE
origin = AbstractAtomicSymbols.ATOMICFU_GENERATED_FUNCTION
}.apply {
dispatchReceiverParameter = (parent as? IrClass)?.thisReceiver?.deepCopyWithSymbols(this)
if (functionName == LOOP) {
if (isArrayReceiver) generateAtomicfuArrayLoop(valueType) else generateAtomicfuLoop(valueType)
} else {
if (isArrayReceiver) generateAtomicfuArrayUpdate(functionName, valueType) else generateAtomicfuUpdate(functionName, valueType)
}
this.parent = parent
parent.declarations.add(this)
override fun IrFunction.checkSyntheticParameterTypes(isArrayReceiver: Boolean, receiverValueType: IrType): Boolean {
if (isArrayReceiver) {
if (valueParameters.size < 2) return false
val atomicArrayType = atomicSymbols.getAtomicArrayClassByValueType(receiverValueType).defaultType
return valueParameters[0].name.asString() == ATOMIC_HANDLER && valueParameters[0].type == atomicArrayType &&
valueParameters[1].name.asString() == INDEX && valueParameters[1].type == irBuiltIns.intType
} else {
if (valueParameters.size < 2) return false
val atomicUpdaterType = atomicSymbols.getFieldUpdaterType(receiverValueType)
return valueParameters[0].name.asString() == DISPATCH_RECEIVER && valueParameters[0].type == irBuiltIns.anyNType &&
valueParameters[1].name.asString() == ATOMIC_HANDLER && valueParameters[1].type == atomicUpdaterType
}
}
private fun IrSimpleFunction.generateAtomicfuLoop(valueType: IrType) {
addValueParameter(ATOMIC_HANDLER, atomicSymbols.getFieldUpdaterType(valueType))
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, irBuiltIns.unitType))
addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType)
body = with(atomicSymbols.createBuilder(symbol)) {
atomicfuLoopBody(valueType, valueParameters)
}
returnType = irBuiltIns.unitType
}
private fun IrSimpleFunction.generateAtomicfuArrayLoop(valueType: IrType) {
val atomicfuArrayClass = atomicSymbols.getAtomicArrayClassByValueType(valueType)
addValueParameter(ATOMIC_HANDLER, atomicfuArrayClass.defaultType)
addValueParameter(INDEX, irBuiltIns.intType)
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, irBuiltIns.unitType))
body = with(atomicSymbols.createBuilder(symbol)) {
atomicfuArrayLoopBody(atomicfuArrayClass, valueParameters)
}
returnType = irBuiltIns.unitType
}
private fun IrSimpleFunction.generateAtomicfuUpdate(functionName: String, valueType: IrType) {
addValueParameter(ATOMIC_HANDLER, atomicSymbols.getFieldUpdaterType(valueType))
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, valueType))
addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType)
body = with(atomicSymbols.createBuilder(symbol)) {
atomicfuUpdateBody(functionName, valueParameters, valueType)
}
returnType = if (functionName == UPDATE) irBuiltIns.unitType else valueType
}
private fun IrSimpleFunction.generateAtomicfuArrayUpdate(functionName: String, valueType: IrType) {
val atomicfuArrayClass = atomicSymbols.getAtomicArrayClassByValueType(valueType)
addValueParameter(ATOMIC_HANDLER, atomicfuArrayClass.defaultType)
addValueParameter(INDEX, irBuiltIns.intType)
addValueParameter(ACTION, atomicSymbols.function1Type(valueType, valueType))
body = with(atomicSymbols.createBuilder(symbol)) {
atomicfuArrayUpdateBody(functionName, atomicfuArrayClass, valueParameters)
}
returnType = if (functionName == UPDATE) irBuiltIns.unitType else valueType
}
}
private fun buildTransformedAtomicExtensionDeclaration(atomicExtension: IrFunction, isArrayReceiver: Boolean): IrSimpleFunction {
/**
* Builds the signature of the transformed atomic extension:
*
* inline fun AtomicInt.foo(arg: Int) --> inline fun foo$atomicfu(dispatchReceiver: Any?, atomicHandler: AtomicIntegerFieldUpdater, arg': Int)
* inline fun foo$atomicfu$array(atomicArray: AtomicIntegerArray, index: Int, arg': Int)
*/
override fun buildTransformedAtomicExtensionSignature(atomicExtension: IrFunction, isArrayReceiver: Boolean): IrSimpleFunction {
val mangledName = mangleAtomicExtensionName(atomicExtension.name.asString(), isArrayReceiver)
val valueType = atomicExtension.extensionReceiverParameter!!.type.atomicToPrimitiveType()
val valueType = (atomicExtension.extensionReceiverParameter!!.type as IrSimpleType).atomicToPrimitiveType()
return pluginContext.irFactory.buildFun {
name = Name.identifier(mangledName)
isInline = true
@@ -632,17 +288,24 @@ class AtomicfuJvmIrTransformer(
}.apply {
extensionReceiverParameter = null
dispatchReceiverParameter = atomicExtension.dispatchReceiverParameter?.deepCopyWithSymbols(this)
if (isArrayReceiver) {
addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType)
addValueParameter(ATOMIC_HANDLER, atomicSymbols.getAtomicArrayClassByValueType(valueType).defaultType)
addValueParameter(INDEX, irBuiltIns.intType)
} else {
addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType)
addValueParameter(ATOMIC_HANDLER, atomicSymbols.getFieldUpdaterType(valueType))
}
atomicExtension.typeParameters.forEach { addTypeParameter(it.name.asString(), it.representativeUpperBound) }
addSyntheticValueParametersToTransformedAtomicExtension(isArrayReceiver, valueType)
atomicExtension.valueParameters.forEach { addValueParameter(it.name, it.type) }
returnType = atomicExtension.returnType
this.parent = atomicExtension.parent
}
}
/**
* Adds synthetic value parameters to the transformed atomic extension (custom atomic extension or atomicfu inline update functions).
*/
override fun IrFunction.addSyntheticValueParametersToTransformedAtomicExtension(isArrayReceiver: Boolean, valueType: IrType) {
if (isArrayReceiver) {
addValueParameter(ATOMIC_HANDLER, atomicSymbols.getAtomicArrayClassByValueType(valueType).defaultType)
addValueParameter(INDEX, irBuiltIns.intType)
} else {
addValueParameter(DISPATCH_RECEIVER, irBuiltIns.anyNType)
addValueParameter(ATOMIC_HANDLER, atomicSymbols.getFieldUpdaterType(valueType))
}
}
}
@@ -28,7 +28,7 @@ class JvmAtomicSymbols(
private val kotlinJvm: IrPackageFragment = createPackage("kotlin.jvm")
private val javaLangClass: IrClassSymbol = createClass(javaLang, "Class", ClassKind.CLASS, Modality.FINAL)
// AtomicIntegerFieldUpdater
// java.util.concurrent.AtomicIntegerFieldUpdater
val atomicIntFieldUpdaterClass: IrClassSymbol =
createClass(javaUtilConcurrent, "AtomicIntegerFieldUpdater", ClassKind.CLASS, Modality.FINAL)
@@ -104,7 +104,7 @@ class JvmAtomicSymbols(
addValueParameter("newValue", irBuiltIns.intType)
}.symbol
// AtomicLongFieldUpdater
// java.util.concurrent.AtomicLongFieldUpdater
val atomicLongFieldUpdaterClass: IrClassSymbol =
createClass(javaUtilConcurrent, "AtomicLongFieldUpdater", ClassKind.CLASS, Modality.FINAL)
@@ -180,7 +180,7 @@ class JvmAtomicSymbols(
addValueParameter("newValue", irBuiltIns.longType)
}.symbol
// AtomicReferenceFieldUpdater
// java.util.concurrent.AtomicReferenceFieldUpdater
val atomicRefFieldUpdaterClass: IrClassSymbol =
createClass(javaUtilConcurrent, "AtomicReferenceFieldUpdater", ClassKind.CLASS, Modality.FINAL)
@@ -232,170 +232,170 @@ class JvmAtomicSymbols(
returnType = valueType.defaultType
}.symbol
// AtomicIntegerArray
val atomicIntArrayClass: IrClassSymbol =
// java.util.concurrent.AtomicIntegerArray
override val atomicIntArrayClassSymbol: IrClassSymbol =
createClass(javaUtilConcurrent, "AtomicIntegerArray", ClassKind.CLASS, Modality.FINAL)
val atomicIntArrayConstructor: IrConstructorSymbol = atomicIntArrayClass.owner.addConstructor().apply {
val atomicIntArrayConstructor: IrConstructorSymbol = atomicIntArrayClassSymbol.owner.addConstructor().apply {
addValueParameter("length", irBuiltIns.intType)
}.symbol
val atomicIntArrayGet: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "get", returnType = irBuiltIns.intType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "get", returnType = irBuiltIns.intType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicIntArraySet: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", irBuiltIns.intType)
}.symbol
val atomicIntArrayCompareAndSet: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("expect", irBuiltIns.intType)
addValueParameter("update", irBuiltIns.intType)
}.symbol
val atomicIntArrayAddAndGet: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "addAndGet", returnType = irBuiltIns.intType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "addAndGet", returnType = irBuiltIns.intType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("delta", irBuiltIns.intType)
}.symbol
val atomicIntArrayGetAndAdd: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "getAndAdd", returnType = irBuiltIns.intType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "getAndAdd", returnType = irBuiltIns.intType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("delta", irBuiltIns.intType)
}.symbol
val atomicIntArrayIncrementAndGet: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "incrementAndGet", returnType = irBuiltIns.intType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "incrementAndGet", returnType = irBuiltIns.intType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicIntArrayGetAndIncrement: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "getAndIncrement", returnType = irBuiltIns.intType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "getAndIncrement", returnType = irBuiltIns.intType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicIntArrayDecrementAndGet: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "decrementAndGet", returnType = irBuiltIns.intType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "decrementAndGet", returnType = irBuiltIns.intType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicIntArrayGetAndDecrement: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "getAndDecrement", returnType = irBuiltIns.intType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "getAndDecrement", returnType = irBuiltIns.intType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicIntArrayLazySet: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", irBuiltIns.intType)
}.symbol
val atomicIntArrayGetAndSet: IrSimpleFunctionSymbol =
atomicIntArrayClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.intType).apply {
atomicIntArrayClassSymbol.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.intType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", irBuiltIns.intType)
}.symbol
// AtomicLongArray
val atomicLongArrayClass: IrClassSymbol =
// java.util.concurrent.AtomicLongArray
override val atomicLongArrayClassSymbol: IrClassSymbol =
createClass(javaUtilConcurrent, "AtomicLongArray", ClassKind.CLASS, Modality.FINAL)
val atomicLongArrayConstructor: IrConstructorSymbol = atomicLongArrayClass.owner.addConstructor().apply {
val atomicLongArrayConstructor: IrConstructorSymbol = atomicLongArrayClassSymbol.owner.addConstructor().apply {
addValueParameter("length", irBuiltIns.intType)
}.symbol
val atomicLongArrayGet: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "get", returnType = irBuiltIns.longType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "get", returnType = irBuiltIns.longType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicLongArraySet: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", irBuiltIns.longType)
}.symbol
val atomicLongArrayCompareAndSet: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("expect", irBuiltIns.longType)
addValueParameter("update", irBuiltIns.longType)
}.symbol
val atomicLongArrayAddAndGet: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "addAndGet", returnType = irBuiltIns.longType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "addAndGet", returnType = irBuiltIns.longType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("delta", irBuiltIns.longType)
}.symbol
val atomicLongArrayGetAndAdd: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "getAndAdd", returnType = irBuiltIns.longType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "getAndAdd", returnType = irBuiltIns.longType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("delta", irBuiltIns.longType)
}.symbol
val atomicLongArrayIncrementAndGet: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "incrementAndGet", returnType = irBuiltIns.longType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "incrementAndGet", returnType = irBuiltIns.longType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicLongArrayGetAndIncrement: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "getAndIncrement", returnType = irBuiltIns.longType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "getAndIncrement", returnType = irBuiltIns.longType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicLongArrayDecrementAndGet: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "decrementAndGet", returnType = irBuiltIns.longType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "decrementAndGet", returnType = irBuiltIns.longType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicLongArrayGetAndDecrement: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "getAndDecrement", returnType = irBuiltIns.longType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "getAndDecrement", returnType = irBuiltIns.longType).apply {
addValueParameter("i", irBuiltIns.intType)
}.symbol
val atomicLongArrayLazySet: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", irBuiltIns.longType)
}.symbol
val atomicLongArrayGetAndSet: IrSimpleFunctionSymbol =
atomicLongArrayClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.longType).apply {
atomicLongArrayClassSymbol.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.longType).apply {
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", irBuiltIns.longType)
}.symbol
// AtomicReferenceArray
val atomicRefArrayClass: IrClassSymbol =
// java.util.concurrent.AtomicReferenceArray
override val atomicRefArrayClassSymbol: IrClassSymbol =
createClass(javaUtilConcurrent, "AtomicReferenceArray", ClassKind.CLASS, Modality.FINAL)
val atomicRefArrayConstructor: IrConstructorSymbol = atomicRefArrayClass.owner.addConstructor().apply {
val atomicRefArrayConstructor: IrConstructorSymbol = atomicRefArrayClassSymbol.owner.addConstructor().apply {
addValueParameter("length", irBuiltIns.intType)
}.symbol
val atomicRefArrayGet: IrSimpleFunctionSymbol =
atomicRefArrayClass.owner.addFunction(name = "get", returnType = irBuiltIns.anyNType).apply {
atomicRefArrayClassSymbol.owner.addFunction(name = "get", returnType = irBuiltIns.anyNType).apply {
val valueType = addTypeParameter("T", irBuiltIns.anyNType)
addValueParameter("i", irBuiltIns.intType)
returnType = valueType.defaultType
}.symbol
val atomicRefArraySet: IrSimpleFunctionSymbol =
atomicRefArrayClass.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply {
atomicRefArrayClassSymbol.owner.addFunction(name = "set", returnType = irBuiltIns.unitType).apply {
val valueType = addTypeParameter("T", irBuiltIns.anyNType)
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", valueType.defaultType)
}.symbol
val atomicRefArrayCompareAndSet: IrSimpleFunctionSymbol =
atomicRefArrayClass.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply {
atomicRefArrayClassSymbol.owner.addFunction(name = "compareAndSet", returnType = irBuiltIns.booleanType).apply {
val valueType = addTypeParameter("T", irBuiltIns.anyNType)
addValueParameter("i", irBuiltIns.intType)
addValueParameter("expect", valueType.defaultType)
@@ -403,39 +403,30 @@ class JvmAtomicSymbols(
}.symbol
val atomicRefArrayLazySet: IrSimpleFunctionSymbol =
atomicRefArrayClass.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply {
atomicRefArrayClassSymbol.owner.addFunction(name = "lazySet", returnType = irBuiltIns.unitType).apply {
val valueType = addTypeParameter("T", irBuiltIns.anyNType)
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", valueType.defaultType)
}.symbol
val atomicRefArrayGetAndSet: IrSimpleFunctionSymbol =
atomicRefArrayClass.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.anyNType).apply {
atomicRefArrayClassSymbol.owner.addFunction(name = "getAndSet", returnType = irBuiltIns.anyNType).apply {
val valueType = addTypeParameter("T", irBuiltIns.anyNType)
addValueParameter("i", irBuiltIns.intType)
addValueParameter("newValue", valueType.defaultType)
returnType = valueType.defaultType
}.symbol
private val VALUE_TYPE_TO_ATOMIC_ARRAY_CLASS: Map<IrType, IrClassSymbol> = mapOf(
irBuiltIns.intType to atomicIntArrayClass,
irBuiltIns.booleanType to atomicIntArrayClass,
irBuiltIns.longType to atomicLongArrayClass,
irBuiltIns.anyNType to atomicRefArrayClass
)
private val ATOMIC_ARRAY_TYPES: Set<IrClassSymbol> = setOf(
atomicIntArrayClass,
atomicLongArrayClass,
atomicRefArrayClass
)
private val ATOMIC_FIELD_UPDATER_TYPES: Set<IrClassSymbol> = setOf(
atomicIntFieldUpdaterClass,
atomicLongFieldUpdaterClass,
atomicRefFieldUpdaterClass
)
// only one constructor is imported for java array types (see values atomicIntArrayConstructor, atomicRefArrayConstructor)
override fun getAtomicArrayConstructor(atomicArrayClassSymbol: IrClassSymbol): IrFunctionSymbol =
atomicArrayClassSymbol.constructors.firstOrNull() ?: error("No constructors found for ${atomicArrayClassSymbol.owner.render()}")
override fun createBuilder(symbol: IrSymbol, startOffset: Int, endOffset: Int) =
JvmAtomicfuIrBuilder(this, symbol, startOffset, endOffset)
@@ -449,38 +440,11 @@ class JvmAtomicSymbols(
fun getFieldUpdaterType(valueType: IrType) = getJucaAFUClass(valueType).defaultType
fun getAtomicArrayClassByAtomicfuArrayType(atomicfuArrayType: IrType): IrClassSymbol =
when (atomicfuArrayType.classFqName?.shortName()?.asString()) {
"AtomicIntArray" -> atomicIntArrayClass
"AtomicLongArray" -> atomicLongArrayClass
"AtomicBooleanArray" -> atomicIntArrayClass
"AtomicArray" -> atomicRefArrayClass
else -> error("Unexpected atomicfu array type ${atomicfuArrayType.render()}")
}
fun getAtomicArrayClassByValueType(valueType: IrType): IrClassSymbol =
VALUE_TYPE_TO_ATOMIC_ARRAY_CLASS[valueType]
?: error("No corresponding atomic array class found for this value type ${valueType.render()} ")
fun getAtomicArrayType(valueType: IrType) = getAtomicArrayClassByValueType(valueType).defaultType
fun isAtomicArrayHandlerType(valueType: IrType) = valueType.classOrNull in ATOMIC_ARRAY_TYPES
fun isAtomicFieldUpdaterType(valueType: IrType) = valueType.classOrNull in ATOMIC_FIELD_UPDATER_TYPES
fun getNewUpdater(atomicUpdaterClassSymbol: IrClassSymbol): IrSimpleFunctionSymbol =
atomicUpdaterClassSymbol.getSimpleFunction("newUpdater") ?: error("No newUpdater function was found for ${atomicUpdaterClassSymbol.owner.render()} ")
fun getAtomicArrayConstructor(atomicArrayClassSymbol: IrClassSymbol): IrConstructorSymbol =
atomicArrayClassSymbol.constructors.firstOrNull() ?: error("No constructors declared for ${atomicArrayClassSymbol.owner.render()} ")
fun getAtomicHandlerFunctionSymbol(atomicHandlerClass: IrClassSymbol, name: String): IrSimpleFunctionSymbol =
when (name) {
"<get-value>", "getValue" -> atomicHandlerClass.getSimpleFunction("get")
"<set-value>", "setValue" -> atomicHandlerClass.getSimpleFunction("set")
else -> atomicHandlerClass.getSimpleFunction(name)
} ?: error("No $name function found in $name")
val kotlinKClassJava: IrPropertySymbol = irFactory.buildProperty {
name = Name.identifier("java")
}.apply {
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicSymbols
import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuIrBuilder
@@ -32,37 +33,6 @@ class JvmAtomicfuIrBuilder internal constructor(
putValueArgument(0, obj)
}
// atomicArr.get(index)
fun atomicGetArrayElement(atomicArrayClass: IrClassSymbol, receiver: IrExpression, index: IrExpression) =
irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicArrayClass, "get")).apply {
dispatchReceiver = receiver
putValueArgument(0, index)
}
fun irJavaAtomicArrayField(
name: Name,
arrayClass: IrClassSymbol,
isStatic: Boolean,
annotations: List<IrConstructorCall>,
size: IrExpression,
dispatchReceiver: IrExpression?,
parentContainer: IrDeclarationContainer
): IrField =
context.irFactory.buildField {
this.name = name
type = arrayClass.defaultType
this.isFinal = true
this.isStatic = isStatic
visibility = DescriptorVisibilities.PRIVATE
origin = AbstractAtomicSymbols.ATOMICFU_GENERATED_FIELD
}.apply {
this.initializer = IrExpressionBodyImpl(
newJavaAtomicArray(arrayClass, size, dispatchReceiver)
)
this.annotations = annotations
this.parent = parentContainer
}
fun irJavaAtomicFieldUpdater(volatileField: IrField, parentClass: IrClass): IrField {
// Generate an atomic field updater for the volatile backing field of the given property:
// val a = atomic(0)
@@ -83,23 +53,13 @@ class JvmAtomicfuIrBuilder internal constructor(
}
}
// atomicArr.compareAndSet(index, expect, update)
fun callAtomicArray(
arrayClassSymbol: IrClassSymbol,
functionName: String,
dispatchReceiver: IrExpression?,
index: IrExpression,
valueArguments: List<IrExpression?>,
isBooleanReceiver: Boolean
): IrCall {
val irCall = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(arrayClassSymbol, functionName)).apply {
this.dispatchReceiver = dispatchReceiver
putValueArgument(0, index) // array element index
valueArguments.forEachIndexed { index, arg ->
putValueArgument(index + 1, arg) // function arguments
}
}
return if (isBooleanReceiver && irCall.type.isInt()) irCall.toBoolean() else irCall
override fun newAtomicArray(
atomicArrayClass: IrClassSymbol,
size: IrExpression,
dispatchReceiver: IrExpression?
): IrFunctionAccessExpression = irCall(atomicSymbols.getAtomicArrayConstructor(atomicArrayClass)).apply {
putValueArgument(0, size) // size
this.dispatchReceiver = dispatchReceiver
}
// a$FU.compareAndSet(obj, expect, update)
@@ -127,13 +87,6 @@ class JvmAtomicfuIrBuilder internal constructor(
return if (isBooleanReceiver && irCall.type.isInt()) irCall.toBoolean() else irCall
}
fun callAtomicExtension(
symbol: IrSimpleFunctionSymbol,
dispatchReceiver: IrExpression?,
syntheticValueArguments: List<IrExpression?>,
valueArguments: List<IrExpression?>
) = irCallWithArgs(symbol, dispatchReceiver, null, syntheticValueArguments + valueArguments)
// val a$FU = j.u.c.a.AtomicIntegerFieldUpdater.newUpdater(A::class, "a")
private fun newJavaAtomicFieldUpdater(
fieldUpdaterClass: IrClassSymbol,
@@ -150,35 +103,30 @@ class JvmAtomicfuIrBuilder internal constructor(
}
}
// val atomicArr = j.u.c.a.AtomicIntegerArray(size)
fun newJavaAtomicArray(
atomicArrayClass: IrClassSymbol,
size: IrExpression,
dispatchReceiver: IrExpression?
) = irCall(atomicSymbols.getAtomicArrayConstructor(atomicArrayClass)).apply {
putValueArgument(0, size) // size
this.dispatchReceiver = dispatchReceiver
}
/*
inline fun <T> atomicfu$loop(atomicfu$handler: AtomicIntegerFieldUpdater, atomicfu$action: (Int) -> Unit, dispatchReceiver: Any?) {
inline fun <T> atomicfu$loop(dispatchReceiver: Any?, atomicfu$handler: AtomicIntegerFieldUpdater, atomicfu$action: (Int) -> Unit) {
while (true) {
val cur = atomicfu$handler.get()
atomicfu$action(cur)
}
}
*/
fun atomicfuLoopBody(valueType: IrType, valueParameters: List<IrValueParameter>) =
// dispatchReceiver: IrValueParameter, atomicHandler: IrValueParameter, action: IrValueParameter
// todo this should also be abstracted out
override fun atomicfuLoopBody(valueType: IrType, valueParameters: List<IrValueParameter>) =
irBlockBody {
val dispatchReceiver = valueParameters[0]
val atomicHandler = valueParameters[1]
val action = valueParameters[2]
+irWhile().apply {
condition = irTrue()
body = irBlock {
val cur = createTmpVariable(
atomicGetValue(valueType, irGet(valueParameters[0]), irGet(valueParameters[2])),
atomicGetValue(valueType, irGet(atomicHandler), irGet(dispatchReceiver)),
"atomicfu\$cur", false
)
+irCall(atomicSymbols.invoke1Symbol).apply {
dispatchReceiver = irGet(valueParameters[1])
this.dispatchReceiver = irGet(action)
putValueArgument(0, irGet(cur))
}
}
@@ -193,17 +141,20 @@ class JvmAtomicfuIrBuilder internal constructor(
}
}
*/
fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>) =
override fun atomicfuArrayLoopBody(atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>) =
irBlockBody {
val atomicHandler = valueParameters[0]
val index = valueParameters[1]
val action = valueParameters[2]
+irWhile().apply {
condition = irTrue()
body = irBlock {
val cur = createTmpVariable(
atomicGetArrayElement(atomicArrayClass, irGet(valueParameters[0]), irGet(valueParameters[1])),
atomicGetArrayElement(atomicArrayClass, irGet(atomicHandler), irGet(index)),
"atomicfu\$cur", false
)
+irCall(atomicSymbols.invoke1Symbol).apply {
dispatchReceiver = irGet(valueParameters[2])
dispatchReceiver = irGet(action)
putValueArgument(0, irGet(cur))
}
}
@@ -239,29 +190,35 @@ class JvmAtomicfuIrBuilder internal constructor(
}
}
*/
fun atomicfuUpdateBody(functionName: String, valueParameters: List<IrValueParameter>, valueType: IrType) =
override fun atomicfuUpdateBody(functionName: String, valueType: IrType, valueParameters: List<IrValueParameter>) =
irBlockBody {
val dispatchReceiver = valueParameters[0]
val atomicHandler = valueParameters[1]
val action = valueParameters[2]
+irWhile().apply {
condition = irTrue()
body = irBlock {
val cur = createTmpVariable(
atomicGetValue(valueType, irGet(valueParameters[0]), irGet(valueParameters[2])),
atomicGetValue(valueType, irGet(atomicHandler), irGet(dispatchReceiver)),
"atomicfu\$cur", false
)
val upd = createTmpVariable(
irCall(atomicSymbols.invoke1Symbol).apply {
dispatchReceiver = irGet(valueParameters[1])
this.dispatchReceiver = irGet(action)
putValueArgument(0, irGet(cur))
}, "atomicfu\$upd", false
)
+irIfThen(
type = atomicSymbols.irBuiltIns.unitType,
condition = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicSymbols.getJucaAFUClass(valueType), "compareAndSet")).apply {
putValueArgument(0, irGet(valueParameters[2]))
putValueArgument(1, irGet(cur))
putValueArgument(2, irGet(upd))
dispatchReceiver = irGet(valueParameters[0])
},
condition = callFieldUpdater(
fieldUpdaterSymbol = atomicSymbols.getJucaAFUClass(valueType),
functionName = "compareAndSet",
getAtomicHandler = irGet(atomicHandler),
classInstanceContainingField = irGet(dispatchReceiver),
valueArguments = listOf(irGet(cur), irGet(upd)),
castType = null,
isBooleanReceiver = valueType.isBoolean()
),
thenPart = when (functionName) {
"update" -> irReturnUnit()
"getAndUpdate" -> irReturn(irGet(cur))
@@ -302,29 +259,36 @@ class JvmAtomicfuIrBuilder internal constructor(
}
}
*/
fun atomicfuArrayUpdateBody(functionName: String, atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>) =
override fun atomicfuArrayUpdateBody(functionName: String, valueType: IrType, atomicArrayClass: IrClassSymbol, valueParameters: List<IrValueParameter>) =
irBlockBody {
val atomicHandler = valueParameters[0]
val index = valueParameters[1]
val action = valueParameters[2]
+irWhile().apply {
val atomicArrayClassSymbol = (atomicHandler.type as IrSimpleType).classOrNull
?: error("Failed to obtain the class corresponding to the array type ${atomicHandler.render()}.")
condition = irTrue()
body = irBlock {
val cur = createTmpVariable(
atomicGetArrayElement(atomicArrayClass, irGet(valueParameters[0]), irGet(valueParameters[1])),
atomicGetArrayElement(atomicArrayClass, irGet(atomicHandler), irGet(index)),
"atomicfu\$cur", false
)
val upd = createTmpVariable(
irCall(atomicSymbols.invoke1Symbol).apply {
dispatchReceiver = irGet(valueParameters[2])
dispatchReceiver = irGet(action)
putValueArgument(0, irGet(cur))
}, "atomicfu\$upd", false
)
+irIfThen(
type = atomicSymbols.irBuiltIns.unitType,
condition = irCall(atomicSymbols.getAtomicHandlerFunctionSymbol(atomicArrayClass, "compareAndSet")).apply {
putValueArgument(0, irGet(valueParameters[1])) // index
putValueArgument(1, irGet(cur))
putValueArgument(2, irGet(upd))
dispatchReceiver = irGet(valueParameters[0])
},
condition = callAtomicArray(
arrayClassSymbol = atomicArrayClassSymbol,
functionName = "compareAndSet",
dispatchReceiver = irGet(atomicHandler),
index = irGet(index),
valueArguments = listOf(irGet(cur), irGet(upd)),
isBooleanReceiver = valueType.isBoolean()
),
thenPart = when (functionName) {
"update" -> irReturnUnit()
"getAndUpdate" -> irReturn(irGet(cur))
@@ -17,19 +17,19 @@ public final class ArrayInlineExtensionTest {
private synthetic final field refArr: java.util.concurrent.atomic.AtomicReferenceArray
static method <clinit>(): void
public method <init>(): void
private synthetic final method bar$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method bar$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method bar$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private final method casLoop(p0: int): int
private final method casLoopExpression(p0: long): long
private synthetic final method extensionLoop$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method extensionLoop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method extensionLoop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final method extensionLoopExpression$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method extensionLoopExpression$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method extensionLoopExpression$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final method extensionLoopMixedReceivers$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int, p4: int, p5: int): int
private synthetic final method extensionLoopMixedReceivers$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int, p3: int, p4: int): int
private synthetic final method extensionLoopMixedReceivers$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int, p3: int, p4: int): int
private synthetic final method extensionLoopRecursive$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method extensionLoopRecursive$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method extensionLoopRecursive$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final method foo$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method foo$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method foo$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final static method getA$volatile$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater
private synthetic final method getA$volatile(): int
@@ -38,7 +38,7 @@ public final class ArrayInlineExtensionTest {
private synthetic final method getRefArr(): java.util.concurrent.atomic.AtomicReferenceArray
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method setA$volatile(p0: int): void
public final method testIntExtensionLoops(): void
public final inner class ArrayInlineExtensionTest$A
@@ -20,9 +20,9 @@ public final class LoopTest {
public method <init>(): void
private final method embeddedLoops(p0: int): int
private final method embeddedUpdate(p0: int): int
private synthetic final method extensionEmbeddedLoops$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method extensionEmbeddedLoops$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method extensionEmbeddedLoops$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final method extesntionEmbeddedRefUpdate$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, p3: java.lang.String): java.lang.String
private synthetic final method extesntionEmbeddedRefUpdate$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: java.lang.String): java.lang.String
private synthetic final method extesntionEmbeddedRefUpdate$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.String): java.lang.String
private synthetic final static method getA$volatile$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater
private synthetic final method getA$volatile(): int
@@ -35,14 +35,14 @@ public final class LoopTest {
private synthetic final method getR$volatile(): java.lang.Object
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method setA$volatile(p0: int): void
private synthetic final method setB$volatile(p0: int): void
private synthetic final method setC$volatile(p0: int): void
private synthetic final method setR$volatile(p0: java.lang.Object): void
public final method test(): void
private synthetic final method updateAndGet$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: kotlin.jvm.functions.Function1): java.lang.Object
private synthetic final method updateAndGet$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): int
private synthetic final method updateAndGet$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): java.lang.Object
private synthetic final method updateAndGet$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): int
private synthetic final method updateAndGet$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): java.lang.Object
}
@@ -20,9 +20,9 @@ public final class ExtensionLoopTestKt {
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
private synthetic final static method getExtensionLoopTest$VolatileWrapper$atomicfu$private(): ExtensionLoopTest$VolatileWrapper$atomicfu$private
private synthetic final static method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: kotlin.jvm.functions.Function1): void
private synthetic final static method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final static method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
public final static method testTopLevelExtensionLoop(): void
private synthetic final static method topLevelExtensionLoop$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, p3: java.lang.String): java.lang.String
private synthetic final static method topLevelExtensionLoop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: java.lang.String): java.lang.String
private synthetic final static method topLevelExtensionLoop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.String): java.lang.String
}
@@ -52,19 +52,19 @@ public final class LoopTest {
private synthetic volatile field rs$volatile: java.lang.Object
static method <clinit>(): void
public method <init>(): void
private synthetic final method bar$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method bar$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method bar$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private final method casLoop(p0: int): int
private final method casLoopExpression(p0: int): int
private synthetic final method extensionLoop$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method extensionLoop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method extensionLoop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final method extensionLoopExpression$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method extensionLoopExpression$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method extensionLoopExpression$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final method extensionLoopMixedReceivers$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int, p4: int): int
private synthetic final method extensionLoopMixedReceivers$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int, p3: int): int
private synthetic final method extensionLoopMixedReceivers$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int, p3: int): int
private synthetic final method extensionLoopRecursive$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method extensionLoopRecursive$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method extensionLoopRecursive$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final method foo$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int, p3: int): int
private synthetic final method foo$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: int): int
private synthetic final method foo$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: int): int
private synthetic final static method getA$volatile$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater
private synthetic final method getA$volatile(): int
@@ -79,7 +79,7 @@ public final class LoopTest {
private synthetic final static method getRs$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
private synthetic final method getRs$volatile(): java.lang.Object
private synthetic final method loop$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method setA$volatile(p0: int): void
private synthetic final method setA1$volatile(p0: int): void
private synthetic final method setB$volatile(p0: int): void
@@ -11,7 +11,7 @@ public final class ExtensionsTest {
private synthetic volatile field s$volatile: java.lang.Object
static method <clinit>(): void
public method <init>(): void
private synthetic final method booleanExtensionArithmetic$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int): void
private synthetic final method booleanExtensionArithmetic$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int): void
private synthetic final method booleanExtensionArithmetic$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater): void
private synthetic final static method getA$volatile$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater
private synthetic final method getA$volatile(): int
@@ -21,11 +21,11 @@ public final class ExtensionsTest {
private synthetic final method getL$volatile(): long
private synthetic final static method getS$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
private synthetic final method getS$volatile(): java.lang.Object
private synthetic final method intExtensionArithmetic$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int): void
private synthetic final method intExtensionArithmetic$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int): void
private synthetic final method intExtensionArithmetic$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater): void
private synthetic final method longExtensionArithmetic$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongArray, p2: int): void
private synthetic final method longExtensionArithmetic$atomicfu$array(p0: java.util.concurrent.atomic.AtomicLongArray, p1: int): void
private synthetic final method longExtensionArithmetic$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater): void
private synthetic final method refExtension$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int): void
private synthetic final method refExtension$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int): void
private synthetic final method refExtension$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater): void
private synthetic final method setA$volatile(p0: int): void
private synthetic final method setB$volatile(p0: int): void
@@ -22,7 +22,7 @@ public final class InlineExtensionWithTypeParameterTest {
private synthetic volatile field sref$volatile: java.lang.Object
static method <clinit>(): void
public method <init>(): void
private synthetic final method foo$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, p3: int, p4: InlineExtensionWithTypeParameterTest$Segment): int
private synthetic final method foo$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: int, p3: InlineExtensionWithTypeParameterTest$Segment): int
private synthetic final method foo$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: int, p3: InlineExtensionWithTypeParameterTest$Segment): int
private final method getSegmentId(p0: InlineExtensionWithTypeParameterTest$Segment): int
private synthetic final static method getSref$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
@@ -14,8 +14,8 @@ public final class LambdaTest {
private synthetic final static method getRs$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
private synthetic final method getRs$volatile(): java.lang.Object
private final method inlineLambda(p0: java.lang.Object, p1: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
public final method loopInLambda1(p0: int): void
public final method loopInLambda2(p0: int): void
public final method loopInLambda2Ref(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
@@ -13,7 +13,7 @@ public final class LockFreeIntBits {
private synthetic final method getBits$volatile(): int
private final method mask(p0: int): int
private synthetic final method setBits$volatile(p0: int): void
private synthetic final method update$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method update$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
}
@kotlin.Metadata
@@ -16,6 +16,6 @@ public final class LockTestKt {
// source: 'LockTest.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static @org.jetbrains.annotations.NotNull method reflectionTest(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: java.util.Map): java.util.List
private synthetic final static method tryAcquire$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerArray, p2: int): boolean
private synthetic final static method tryAcquire$atomicfu$array(p0: java.util.concurrent.atomic.AtomicIntegerArray, p1: int): boolean
private synthetic final static method tryAcquire$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater): boolean
}
@@ -5,9 +5,9 @@ public final class ParameterizedInlineFunExtensionTest {
private synthetic volatile field tail$volatile: java.lang.Object
static method <clinit>(): void
public method <init>(): void
private synthetic final method bar$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, p3: java.lang.Object, p4: java.lang.Object): java.lang.Object
private synthetic final method bar$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: java.lang.Object, p3: java.lang.Object): java.lang.Object
private synthetic final method bar$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.Object, p3: java.lang.Object): java.lang.Object
private synthetic final method foo$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int, p3: java.lang.Object, p4: java.lang.Object, p5: kotlin.jvm.functions.Function1): java.lang.Object
private synthetic final method foo$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int, p2: java.lang.Object, p3: java.lang.Object, p4: kotlin.jvm.functions.Function1): java.lang.Object
private synthetic final method foo$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: java.lang.Object, p3: java.lang.Object, p4: kotlin.jvm.functions.Function1): java.lang.Object
private synthetic final static method getTail$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
private synthetic final method getTail$volatile(): java.lang.Object
@@ -29,7 +29,7 @@ public final class LockFreeQueue {
private synthetic final method getHead$volatile(): java.lang.Object
private synthetic final static method getTail$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
private synthetic final method getTail$volatile(): java.lang.Object
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method setHead$volatile(p0: java.lang.Object): void
private synthetic final method setTail$volatile(p0: java.lang.Object): void
private final inner class LockFreeQueue$Node
@@ -17,17 +17,17 @@ public final class LockFreeStack {
static method <clinit>(): void
public method <init>(): void
public final method clear(): void
private synthetic final method getAndUpdate$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): java.lang.Object
private synthetic final method getAndUpdate$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): java.lang.Object
private synthetic final static method getTop$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
private synthetic final method getTop$volatile(): java.lang.Object
public final method isEmpty(): boolean
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
public final @org.jetbrains.annotations.Nullable method popLoop(): java.lang.Object
public final @org.jetbrains.annotations.Nullable method popUpdate(): java.lang.Object
public final method pushLoop(p0: java.lang.Object): void
public final method pushUpdate(p0: java.lang.Object): void
private synthetic final method setTop$volatile(p0: java.lang.Object): void
private synthetic final method update$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method update$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private final inner class LockFreeStack$Node
}
@@ -36,9 +36,9 @@ public final class LoopTest {
private synthetic final method getA$volatile(): int
private synthetic final static method getA1$volatile$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater
private synthetic final method getA1$volatile(): int
private synthetic final method getAndUpdate$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): int
private synthetic final method getAndUpdate$atomicfu(p0: java.util.concurrent.atomic.AtomicLongFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): long
private synthetic final method getAndUpdate$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): java.lang.Object
private synthetic final method getAndUpdate$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): int
private synthetic final method getAndUpdate$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: kotlin.jvm.functions.Function1): long
private synthetic final method getAndUpdate$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): java.lang.Object
private synthetic final static method getB$volatile$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater
private synthetic final method getB$volatile(): int
private synthetic final static method getL$volatile$FU(): java.util.concurrent.atomic.AtomicLongFieldUpdater
@@ -47,21 +47,21 @@ public final class LoopTest {
private synthetic final method getR$volatile(): java.lang.Object
private synthetic final static method getRs$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
private synthetic final method getRs$volatile(): java.lang.Object
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicLongFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method setA$volatile(p0: int): void
private synthetic final method setA1$volatile(p0: int): void
private synthetic final method setB$volatile(p0: int): void
private synthetic final method setL$volatile(p0: long): void
private synthetic final method setR$volatile(p0: java.lang.Object): void
private synthetic final method setRs$volatile(p0: java.lang.Object): void
private synthetic final method update$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method update$atomicfu(p0: java.util.concurrent.atomic.AtomicLongFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method update$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method updateAndGet$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): int
private synthetic final method updateAndGet$atomicfu(p0: java.util.concurrent.atomic.AtomicLongFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): long
private synthetic final method updateAndGet$atomicfu(p0: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): java.lang.Object
private synthetic final method update$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method update$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method update$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method updateAndGet$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): int
private synthetic final method updateAndGet$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicLongFieldUpdater, p2: kotlin.jvm.functions.Function1): long
private synthetic final method updateAndGet$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater, p2: kotlin.jvm.functions.Function1): java.lang.Object
public final inner class LoopTest$A
}
@@ -7,7 +7,7 @@ public final class SimpleLock {
public method <init>(): void
private synthetic final static method get_locked$volatile$FU(): java.util.concurrent.atomic.AtomicIntegerFieldUpdater
private synthetic final method get_locked$volatile(): int
private synthetic final method loop$atomicfu(p0: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p1: kotlin.jvm.functions.Function1, p2: java.lang.Object): void
private synthetic final method loop$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicIntegerFieldUpdater, p2: kotlin.jvm.functions.Function1): void
private synthetic final method set_locked$volatile(p0: int): void
public final method withLock(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function0): java.lang.Object
}
@@ -42,7 +42,7 @@ public final class UncheckedCastTest {
private synthetic final method getBs$volatile(): java.lang.Object
private synthetic final static method getS$volatile$FU(): java.util.concurrent.atomic.AtomicReferenceFieldUpdater
private synthetic final method getS$volatile(): java.lang.Object
private synthetic final method getString$atomicfu$array(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceArray, p2: int): java.lang.String
private synthetic final method getString$atomicfu$array(p0: java.util.concurrent.atomic.AtomicReferenceArray, p1: int): java.lang.String
private synthetic final method getString$atomicfu(p0: java.lang.Object, p1: java.util.concurrent.atomic.AtomicReferenceFieldUpdater): java.lang.String
private synthetic final method setBs$volatile(p0: java.lang.Object): void
private synthetic final method setS$volatile(p0: java.lang.Object): void