[atomicfu-K/N] Introduction of K/N transformation
diff --git a/libraries/tools/atomicfu/src/common/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuKotlinGradleSubplugin.kt b/libraries/tools/atomicfu/src/common/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuKotlinGradleSubplugin.kt
index afcdc9a..e1a66db 100644
--- a/libraries/tools/atomicfu/src/common/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuKotlinGradleSubplugin.kt
+++ b/libraries/tools/atomicfu/src/common/kotlin/org/jetbrains/kotlinx/atomicfu/gradle/AtomicfuKotlinGradleSubplugin.kt
@@ -37,7 +37,8 @@
val project = kotlinCompilation.target.project
val config = project.extensions.getByType(AtomicfuKotlinGradleExtension::class.java)
return (config.isJsIrTransformationEnabled && kotlinCompilation.target.isJs()) ||
- (config.isJvmIrTransformationEnabled && kotlinCompilation.target.isJvm())
+ (config.isJvmIrTransformationEnabled && kotlinCompilation.target.isJvm()) ||
+ (config.isNativeIrTransformationEnabled && kotlinCompilation.target.isNative())
}
override fun applyToCompilation(
@@ -48,6 +49,7 @@
open class AtomicfuKotlinGradleExtension {
var isJsIrTransformationEnabled = false
var isJvmIrTransformationEnabled = false
+ var isNativeIrTransformationEnabled = false
}
override fun getPluginArtifact(): SubpluginArtifact =
@@ -58,4 +60,6 @@
private fun KotlinTarget.isJs() = platformType == KotlinPlatformType.js
private fun KotlinTarget.isJvm() = platformType == KotlinPlatformType.jvm || platformType == KotlinPlatformType.androidJvm
+
+ private fun KotlinTarget.isNative() = platformType == KotlinPlatformType.native // todo wasm?
}
diff --git a/native/native.tests/build.gradle.kts b/native/native.tests/build.gradle.kts
index 215c0ea..95f12a7 100644
--- a/native/native.tests/build.gradle.kts
+++ b/native/native.tests/build.gradle.kts
@@ -38,6 +38,8 @@
}
}
+testsJar {}
+
if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
apply(plugin = "idea")
idea {
diff --git a/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeTests.kt b/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeTests.kt
index cefd90c..0056786 100644
--- a/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeTests.kt
+++ b/native/native.tests/tests/org/jetbrains/kotlin/generators/tests/GenerateNativeTests.kt
@@ -241,6 +241,16 @@
model("diagnostics/nativeTests", excludedPattern = CUSTOM_TEST_DATA_EXTENSION_PATTERN)
}
}
+
+ // Atomicfu compiler plugin native tests. // TODO: to delete
+ testGroup("plugins/atomicfu/atomicfu-compiler/test", "plugins/atomicfu/atomicfu-compiler/testData") {
+ testClass<AbstractNativeBlackBoxTest>(
+ suiteTestClassName = "AtomicfuNativeTestGenerated",
+ annotations = listOf(atomicfu(), provider<UseStandardTestCaseGroupProvider>())
+ ) {
+ model("nativeBox")
+ }
+ }
}
}
@@ -280,3 +290,6 @@
private fun debugger() = annotation(Tag::class.java, "debugger")
private fun infrastructure() = annotation(Tag::class.java, "infrastructure")
+private fun k1libContents() = annotation(Tag::class.java, "k1libContents")
+private fun k2libContents() = annotation(Tag::class.java, "k2libContents")
+private fun atomicfu() = annotation(Tag::class.java, "atomicfu")
diff --git a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
index 90c6cbe..db31341 100644
--- a/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
+++ b/plugins/atomicfu/atomicfu-compiler/build.gradle.kts
@@ -1,6 +1,9 @@
-import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
+import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
+import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin
+import org.jetbrains.kotlin.konan.target.HostManager
description = "Atomicfu Compiler Plugin"
@@ -9,6 +12,11 @@
id("jps-compatible")
}
+project.configureJvmToolchain(JdkMajorVersion.JDK_11_0)
+
+// WARNING: Native target is host-dependent. Re-running the same build on another host OS may bring to a different result.
+val nativeTargetName = HostManager.host.name
+
val antLauncherJar by configurations.creating
val testJsRuntime by configurations.creating {
attributes {
@@ -26,6 +34,18 @@
val atomicfuJvmClasspath by configurations.creating
+val atomicfuNativeKlib by configurations.creating {
+ attributes {
+ attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
+ // WARNING: Native target is host-dependent. Re-running the same build on another host OS may bring to a different result.
+ attribute(KotlinNativeTarget.konanTargetAttribute, nativeTargetName)
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
+ attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
+ // todo: don't add platform specific attribute
+ attribute(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget.konanTargetAttribute, org.jetbrains.kotlin.konan.target.KonanTarget.MACOS_X64.toString())
+ }
+}
+
val atomicfuJsIrRuntimeForTests by configurations.creating {
attributes {
attribute(KotlinPlatformType.attribute, KotlinPlatformType.js)
@@ -39,6 +59,8 @@
}
dependencies {
+ testImplementation(project(mapOf("path" to ":native:native.tests")))
+ testImplementation(project(mapOf("path" to ":native:native.tests")))
compileOnly(intellijCore())
compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all"))
@@ -68,14 +90,31 @@
testApi(commonDependency("junit:junit"))
testApi(project(":kotlin-test:kotlin-test-jvm"))
+ // Dependencies for Kotlin/Native test infra:
+ testImplementation(projectTests(":native:native.tests"))
+ testImplementation(project(":native:kotlin-native-utils"))
+ testImplementation(commonDependency("org.jetbrains.teamcity:serviceMessages"))
+
+ // todo: remove unnecessary dependencies
+ testImplementation(project(":kotlin-compiler-runner-unshaded"))
+
+ testImplementation(commonDependency("commons-lang:commons-lang"))
+ testImplementation(projectTests(":compiler:tests-common"))
+ testImplementation(projectTests(":compiler:tests-common-new"))
+ testImplementation(projectTests(":compiler:test-infrastructure"))
+ testCompileOnly("org.jetbrains.kotlinx:atomicfu:0.17.1") // todo: do not hardcode atomicfu version
+
+ testApiJUnit5()
+
testRuntimeOnly(kotlinStdlib())
testRuntimeOnly(project(":kotlin-preloader")) // it's required for ant tests
testRuntimeOnly(project(":compiler:backend-common"))
testRuntimeOnly(commonDependency("org.fusesource.jansi", "jansi"))
atomicfuJsClasspath("org.jetbrains.kotlinx:atomicfu-js:0.17.1") { isTransitive = false }
- atomicfuJvmClasspath("org.jetbrains.kotlinx:atomicfu:0.17.1") { isTransitive = false }
atomicfuJsIrRuntimeForTests(project(":kotlinx-atomicfu-runtime")) { isTransitive = false }
+ atomicfuJvmClasspath("org.jetbrains.kotlinx:atomicfu:0.20.2") { isTransitive = false }
+ atomicfuNativeKlib("org.jetbrains.kotlinx:atomicfu:0.20.2") { isTransitive = false }
embedded(project(":kotlinx-atomicfu-runtime")) {
attributes {
@@ -125,3 +164,13 @@
publish()
standardPublicJars()
+
+// val myInfrastructureTest = nativeTest("myInfrastructureTest", "infrastructure")
+
+val nativeBoxTest = nativeTest(
+ taskName = "nativeBoxTest",
+ tag = "atomicfu",
+ requirePlatformLibs = true,
+ customDependencies = listOf(atomicfuJvmClasspath),
+ customKlibDependencies = listOf(atomicfuNativeKlib)
+)
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt
new file mode 100644
index 0000000..b80104f
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/AtomicfuNativeIrTransformer.kt
@@ -0,0 +1,434 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlinx.atomicfu.compiler.backend.native
+
+import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
+import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
+import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
+import org.jetbrains.kotlin.ir.builders.declarations.*
+import org.jetbrains.kotlin.ir.builders.irBlockBody
+import org.jetbrains.kotlin.ir.builders.irCall
+import org.jetbrains.kotlin.ir.builders.irReturn
+import org.jetbrains.kotlin.ir.declarations.*
+import org.jetbrains.kotlin.ir.expressions.*
+import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
+import org.jetbrains.kotlin.ir.types.*
+import org.jetbrains.kotlin.ir.util.*
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuTransformer
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.js.buildSimpleType
+
+
+private const val ATOMICFU = "atomicfu"
+private const val LOOP = "loop"
+private const val UPDATE = "update"
+private const val ACTION = "action\$$ATOMICFU"
+private const val REF_GETTER = "refGetter\$$ATOMICFU"
+
+class AtomicfuNativeIrTransformer(
+ pluginContext: IrPluginContext,
+ override val atomicSymbols: NativeAtomicSymbols
+) : AbstractAtomicfuTransformer(pluginContext) {
+
+ override val atomicPropertiesTransformer: AtomicPropertiesTransformer
+ get() = NativeAtomicPropertiesTransformer()
+
+ override val atomicExtensionsTransformer: AtomicExtensionTransformer
+ get() = NativeAtomicExtensionTransformer()
+
+ override val atomicFunctionsTransformer: AtomicFunctionCallTransformer
+ get() = NativeAtomicFunctionCallTransformer()
+
+ private inner class NativeAtomicPropertiesTransformer : AtomicPropertiesTransformer() {
+
+ override fun IrClass.addTransformedInClassAtomic(atomicProperty: IrProperty): IrProperty =
+ addVolatileProperty(atomicProperty)
+
+ override fun IrDeclarationContainer.addTransformedStaticAtomic(atomicProperty: IrProperty): IrProperty =
+ addVolatileProperty(atomicProperty)
+
+ override fun IrDeclarationContainer.addTransformedAtomicArray(atomicProperty: IrProperty): IrProperty? {
+ // todo: just skip them (as a box) and do not transform any subsequent calls on this array
+ // todo: design API for atomic array intrinsics
+ return null
+ }
+
+ private fun IrDeclarationContainer.addVolatileProperty(from: IrProperty): IrProperty {
+ val parentContainer = this
+ with(atomicSymbols.createBuilder(from.symbol)) {
+ val volatileField = buildVolatileBackingField(from, parentContainer, false)
+ return parentContainer.addProperty(volatileField, from.visibility, isVar = true, isStatic = from.getter!!.dispatchReceiverParameter == null).also {
+ atomicPropertyToVolatile[from] = it
+ }
+ }
+ }
+ }
+
+ private inner class NativeAtomicExtensionTransformer : AtomicExtensionTransformer() {
+
+ override fun IrDeclarationContainer.transformAllAtomicExtensions() {
+ declarations.filter { it is IrSimpleFunction && it.isAtomicExtension() }.forEach {
+ declarations.add((it as IrSimpleFunction).deepCopyWithSymbols(this).transformAtomicExtension())
+ // TODO: while arrays are not supported, non-transformed atomic extensions are not removed
+ //declarations.remove(it)
+ }
+ }
+
+ // inline fun AtomicInt.foo(arg: Int) -> foo(crossinline refGetter: () -> KMutableProperty0<Int>, arg: Int)
+ private fun IrSimpleFunction.transformAtomicExtension(): IrFunction {
+ val mangledName = mangleAtomicExtensionName(this.name.asString(), false)
+ val valueType = extensionReceiverParameter!!.type.atomicToPrimitiveType()
+ this.name = Name.identifier(mangledName)
+ val refGetterLambda = buildValueParameter(this) {
+ name = Name.identifier("refGetter\$atomicfu")
+ index = 0
+ type = atomicSymbols.kMutableProperty0GetterType(valueType)
+ isCrossInline = true
+ }
+ valueParameters = listOf(refGetterLambda) + valueParameters
+ extensionReceiverParameter = null
+ return this
+ }
+ }
+
+ private inner class NativeAtomicFunctionCallTransformer : AtomicFunctionCallTransformer() {
+
+ override fun transformAtomicUpdateCallOnProperty(
+ expression: IrCall,
+ functionName: String,
+ valueType: IrType,
+ castType: IrType?,
+ getPropertyReceiver: IrExpression,
+ parentFunction: IrFunction?
+ ): IrExpression =
+ with(atomicSymbols.createBuilder(expression.symbol)) {
+ // Transformation of the original atomic extension body should be skipped,
+ // because invocations on atomic array elements are left untransformed.
+ val containingFunction =
+ (if (parentFunction?.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) parentFunction.parent else parentFunction) as? IrSimpleFunction
+ if (containingFunction != null && containingFunction.isAtomicExtension()) {
+ return expression
+ }
+ /**
+ * Atomic update call on the atomic property is replaced
+ * with the atomic intrinsic call on the corresponding volatile property reference:
+ *
+ * 1. Function call receiver is atomic property getter call.
+ *
+ * The call is replaced with atomic intrinsic call and the new receiver is
+ * the reference to the corresponding volatile property:
+ *
+ * val a = atomic(0) @Volatile var a$volatile = 0
+ * <get-a>().compareAndSet(0, 5) ---> (this::a$volatile).compareAndSetField(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.
+ *
+ * The call is replaced with atomic intrinsic call and the new receiver is
+ * the invoked getter of the property reference
+ * that is the first parameter of the parent function:
+ *
+ * fun AtomicInt.foo(new: Int) { fun foo$atomicfu(crossinline refGetter: () -> KMutableProperty0<Int>, new: Int) {
+ * this.compareAndSet(value, new) ---> refGetter().compareAndSetField(refGetter().get(), new)
+ * } }
+ */
+ requireNotNull(parentFunction) { "Parent function of the call ${expression.render()} is null" }
+ val getPropertyReference = buildVolatilePropertyReference(getPropertyReceiver, parentFunction)
+ return irCallAtomicNativeIntrinsic(
+ functionName = functionName,
+ propertyRef = getPropertyReference,
+ valueType = valueType,
+ valueArguments = expression.valueArguments
+ )
+ }
+
+ override fun transformAtomicUpdateCallOnArrayElement(
+ expression: IrCall,
+ functionName: String,
+ valueType: IrType,
+ getPropertyReceiver: IrExpression,
+ parentFunction: IrFunction?
+ ): IrExpression {
+ // invocations with array element receiver are left untransformed
+ return expression
+ }
+
+ override fun transformAtomicExtensionCall(
+ expression: IrCall,
+ originalAtomicExtension: IrSimpleFunction,
+ getPropertyReceiver: IrExpression,
+ isArrayReceiver: Boolean,
+ parentFunction: IrFunction?
+ ): IrCall =
+ with(atomicSymbols.createBuilder(expression.symbol)) {
+ // Transformation of the original atomic extension body should be skipped,
+ // because invocations on atomic array elements are left untransformed.
+ val containingFunction =
+ (if (parentFunction?.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) parentFunction.parent else parentFunction) as? IrSimpleFunction
+ if (isArrayReceiver || (containingFunction != null && containingFunction.isAtomicExtension())) {
+ return expression
+ }
+ /**
+ * Call of the atomic extension on the atomic property is replaced
+ * with the transformed atomic extension call, passing getter of the corresponding volatile property as an argument:
+ *
+ *
+ * 1. Receiver of the atomic extension call is atomic property getter call.
+ *
+ * Atomic extension call is replaced with the transformed extension invocation and
+ * inline getter of the corresponding volatile property is passed as an argument:
+ *
+ * val a = atomic(0) @Volatile var a$volatile = 0
+ * fun AtomicInt.foo(new: Int) {...} fun foo$atomicfu(crossinline refGetter: () -> KMutableProperty0<Int>, new: Int)
+ *
+ * <get-a>().foo(5) ---> foo$atomicfu({_ -> this::a$volatile}, 5)
+ *
+ *
+ * 2. Atomic extension is invoked in the body of the transformed atomic extension,
+ * the call receiver is the old <this> receiver of the extension.
+ *
+ * This call is replaced with the transformed extension call
+ * and takes the refGetter value parameter of the parent function as an argument.
+ *
+ * fun AtomicInt.bar() {..} fun bar$atomicfu(refGetter: () -> KMutableProperty0<Int>) { .. }
+ *
+ * fun AtomicInt.foo(new: Int) { fun foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, new: Int) {
+ * this.bar() ---> bar$atomicfu(refGetter)
+ * } }
+ */
+ requireNotNull(parentFunction) { "Parent function of the call ${expression.render()} is null" }
+ val parent = originalAtomicExtension.parent as IrDeclarationContainer
+ val transformedAtomicExtension = parent.getTransformedAtomicExtension(originalAtomicExtension, isArrayReceiver)
+ val volatilePropertyGetter = buildVolatilePropertyGetter(getPropertyReceiver, parentFunction) ?: return expression // 13107 // 13215
+ return irCallWithArgs(
+ symbol = transformedAtomicExtension.symbol,
+ dispatchReceiver = expression.dispatchReceiver,
+ extensionReceiver = null,
+ valueArguments = listOf(volatilePropertyGetter) + expression.valueArguments
+ )
+ }
+
+ override fun transformedAtomicfuInlineFunctionCall(
+ expression: IrCall,
+ functionName: String,
+ valueType: IrType,
+ getPropertyReceiver: IrExpression,
+ isArrayReceiver: Boolean,
+ parentFunction: IrFunction?
+ ): IrCall {
+ with(atomicSymbols.createBuilder(expression.symbol)) {
+ // Transformation of the original atomic extension body should be skipped,
+ // because invocations on atomic array elements are left untransformed.
+ val containingFunction =
+ (if (parentFunction?.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) parentFunction.parent else parentFunction) as? IrSimpleFunction
+ if (isArrayReceiver || (containingFunction != null && containingFunction.isAtomicExtension())) {
+ return expression
+ }
+ requireNotNull(parentFunction) { "Parent function of the call ${expression.render()} is null" }
+ 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@NativeAtomicFunctionCallTransformer, parentFunction)
+ // todo check for type in extension receiver here
+ // todo: we don't have to do this here, that's only for JVM
+ if (function.valueParameters[0].type.isBoolean()) {
+ function.valueParameters[0].type = irBuiltIns.intType
+ function.returnType = irBuiltIns.intType
+ }
+ }
+ val volatilePropertyGetter = buildVolatilePropertyGetter(getPropertyReceiver, parentFunction) ?: return expression
+ return irCallWithArgs(
+ symbol = loopFunc.symbol,
+ dispatchReceiver = parentFunction.containingFunction.dispatchReceiverParameter?.capture(),
+ extensionReceiver = null,
+ valueArguments = listOf(volatilePropertyGetter, action)
+ )
+ }
+ }
+
+ override fun IrDeclarationContainer.getTransformedAtomicExtension(
+ declaration: IrSimpleFunction,
+ isArrayReceiver: Boolean
+ ): IrSimpleFunction = findDeclaration {
+ it.name.asString() == mangleAtomicExtensionName(declaration.name.asString(), isArrayReceiver) &&
+ it.isTransformedAtomicExtension()
+ }
+ ?: error("Could not find corresponding transformed declaration for the atomic extension ${declaration.render()} ${if (isArrayReceiver) "for array element receiver" else ""}")
+
+ override fun IrValueParameter.remapValueParameter(transformedExtension: IrFunction): IrValueParameter? {
+ if (index < 0 && !type.isAtomicValueType()) {
+ // index == -1 for `this` parameter
+ return transformedExtension.dispatchReceiverParameter
+ ?: error { "Dispatch receiver of ${transformedExtension.render()} is null" }
+ }
+ if (index >= 0) {
+ return transformedExtension.valueParameters[index]
+ }
+ return null
+ }
+
+ private fun IrDeclarationContainer.getOrBuildInlineLoopFunction(
+ functionName: String,
+ valueType: IrType,
+ isArrayReceiver: Boolean
+ ): IrSimpleFunction {
+ val parent = this
+ val mangledName = mangleAtomicExtensionName(functionName, isArrayReceiver)
+ findDeclaration<IrSimpleFunction> {
+ it.name.asString() == mangledName
+ // todo put back all the checks
+// it.valueParameters.isNotEmpty() &&
+// it.valueParameters[0].type == irBuiltIns.functionN(0) &&
+// (it.valueParameters[0].type as IrSimpleType).arguments.firstOrNull() == irBuiltIns.kMutableProperty0Class
+ }?.let { return it }
+ println(valueType)
+ /**
+ * inline fun update$atomicfu(refGetter: () -> KMutableProperty0<Int>, action: (Int) -> Int) {
+ * while (true) {
+ * val cur = refGetter().get()
+ * val upd = action(cur)
+ * if (refGetter().compareAndSetField(cur, upd)) return
+ * }
+ * }
+ */
+ return pluginContext.irFactory.buildFun {
+ name = Name.identifier(mangledName)
+ isInline = true
+ visibility = DescriptorVisibilities.PRIVATE
+ }.apply {
+ with(atomicSymbols.createBuilder(symbol)) {
+ dispatchReceiverParameter = (parent as? IrClass)?.thisReceiver?.deepCopyWithSymbols(this@apply)
+ addTypeParameter("T", irBuiltIns.anyNType)
+ val type = buildSimpleType(typeParameters[0].symbol, emptyList())
+ addValueParameter(REF_GETTER, atomicSymbols.kMutableProperty0GetterType(type))
+ if (functionName == LOOP) {
+ addValueParameter(ACTION, atomicSymbols.function1Type(type, irBuiltIns.unitType))
+ body = atomicfuLoopBody(valueParameters[0], valueParameters[1])
+ returnType = irBuiltIns.unitType
+ } else {
+ addValueParameter(ACTION, atomicSymbols.function1Type(type, type))
+ body = atomicfuUpdateBody(functionName, type, valueParameters[0], valueParameters[1])
+ returnType = if (functionName == UPDATE) irBuiltIns.unitType else type
+ }
+ }
+ this.parent = parent
+ parent.declarations.add(this)
+ }
+ }
+
+ private fun IrFunction.isAtomicExtension(): Boolean =
+ extensionReceiverParameter != null && isInline && extensionReceiverParameter!!.type.isAtomicValueType()
+
+ override fun IrFunction.isTransformedAtomicExtension(): Boolean =
+ extensionReceiverParameter == null && valueParameters.isNotEmpty() && valueParameters[0].name.asString() == "refGetter\$atomicfu"
+
+ private fun NativeAtomicfuIrBuilder.buildVolatilePropertyReference(
+ getPropertyReceiver: IrExpression,
+ parentFunction: IrFunction
+ ): IrExpression = when {
+ getPropertyReceiver is IrCall -> {
+ /**
+ * Function call receiver is atomic property getter call.
+ * The new receiver is the reference to the corresponding volatile property:
+ *
+ * <get-a>().compareAndSet(0, 5) ---> (this::a$volatile).compareAndSetField(0, 5)
+ */
+ val atomicProperty = getPropertyReceiver.getCorrespondingProperty()
+ val volatileProperty = atomicPropertyToVolatile[atomicProperty]
+ ?: error("No volatile property was generated for the atomic property ${atomicProperty.render()}")
+ irPropertyReference(
+ property = volatileProperty,
+ classReceiver = getPropertyReceiver.dispatchReceiver
+ )
+ }
+ getPropertyReceiver.isThisReceiver() -> {
+ /**
+ * Function call receiver is the old <this> receiver of the extension.
+ * The new receiver is the invoked getter of the property reference.
+ * that is the first parameter of the parent function:
+ *
+ * fun AtomicInt.foo(new: Int) { fun foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, new: Int) {
+ * this.compareAndSet(value, new) ---> refGetter().compareAndSetField(refGetter().get(), new)
+ * } }
+ */
+ require(parentFunction.isTransformedAtomicExtension())
+ val refGetter = parentFunction.valueParameters[0].capture()
+ irCall(atomicSymbols.invoke0Symbol).apply { dispatchReceiver = refGetter }
+ }
+ else -> error("Unsupported type of atomic receiver expression: ${getPropertyReceiver.render()}")
+ }
+
+ private fun NativeAtomicfuIrBuilder.buildVolatilePropertyGetter(
+ getPropertyReceiver: IrExpression,
+ parentFunction: IrFunction
+ ): IrExpression? {
+ when {
+ getPropertyReceiver is IrCall -> {
+ /**
+ * Receiver of the atomic extension call is atomic property getter call.
+ * Generate inline getter lambda of the corresponding volatile property
+ * to pass as an argument to the transformed extension call:
+ *
+ * <get-a>().foo(5) ---> foo$atomicfu({_ -> this::a$volatile}, 5)
+ */
+ val isArrayReceiver = getPropertyReceiver.isArrayElementGetter()
+ // leave invocations on array elements untransformed for now
+ if (isArrayReceiver) return null
+ val atomicProperty = getPropertyReceiver.getCorrespondingProperty()
+ val volatileProperty = atomicPropertyToVolatile[atomicProperty]
+ ?: error("No volatile property was generated for the atomic property ${atomicProperty.render()}")
+ val valueType = volatileProperty.backingField!!.type
+ return IrFunctionExpressionImpl(
+ UNDEFINED_OFFSET, UNDEFINED_OFFSET,
+ type = atomicSymbols.kMutableProperty0GetterType(valueType),
+ function = irBuiltIns.irFactory.buildFun {
+ name = Name.identifier("<${volatileProperty.name.asString()}-getter>")
+ origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
+ returnType = atomicSymbols.kMutableProperty0Type(valueType)
+ isInline = true
+ visibility = DescriptorVisibilities.LOCAL
+ }.apply {
+ val lambda = this
+ body = irBlockBody {
+ +irReturn(
+ irPropertyReference(volatileProperty, getPropertyReceiver.dispatchReceiver)
+ ).apply {
+ type = irBuiltIns.nothingType
+ returnTargetSymbol = lambda.symbol
+ }
+ }
+ parent = parentFunction
+ },
+ origin = IrStatementOrigin.LAMBDA
+ )
+ }
+ getPropertyReceiver.isThisReceiver() -> {
+ /**
+ * Atomic extension is invoked in the body of the transformed atomic extension,
+ * the call receiver is the old <this> receiver of the extension.
+ * Pass the parameter of parent extension as an argument:
+ *
+ * fun AtomicInt.foo(new: Int) { fun foo$atomicfu(refGetter: () -> KMutableProperty0<Int>, new: Int) {
+ * this.bar() ---> bar$atomicfu(refGetter)
+ * } }
+ */
+ require(parentFunction.isTransformedAtomicExtension())
+ return parentFunction.valueParameters[0].capture()
+ }
+ else -> error("Unsupported type of atomic receiver expression: ${getPropertyReceiver.render()}")
+ }
+ }
+
+ override fun IrExpression.isArrayElementReceiver(parentFunction: IrFunction?): Boolean {
+ return if (this is IrCall) this.isArrayElementGetter() else false
+ }
+ }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicSymbols.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicSymbols.kt
new file mode 100644
index 0000000..2c04e80
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicSymbols.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlinx.atomicfu.compiler.backend.native
+
+import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.backend.jvm.functionByName
+import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.ir.declarations.IrClass
+import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
+import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
+import org.jetbrains.kotlin.ir.symbols.IrSymbol
+import org.jetbrains.kotlin.ir.types.*
+import org.jetbrains.kotlin.ir.util.getPropertyGetter
+import org.jetbrains.kotlin.name.*
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicSymbols
+
+class NativeAtomicSymbols(
+ context: IrPluginContext,
+ moduleFragment: IrModuleFragment
+) : AbstractAtomicSymbols(context, moduleFragment) {
+ override val volatileAnnotationClass: IrClass
+ get() = context.referenceClass(ClassId(FqName("kotlin.concurrent"), Name.identifier("Volatile")))?.owner
+ ?: error("kotlin.concurrent.Volatile class is not found")
+
+ // AtomicInt class
+
+ val compareAndSetFieldIntrinsic =
+ context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("compareAndSetField"))).single()
+
+ val getAndSetFieldIntrinsic =
+ context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("getAndSetField"))).single()
+
+ val getAndAddIntFieldIntrinsic =
+ context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("getAndAddField")))
+ .single { it.owner.returnType.isInt() }
+
+ val getAndAddLongFieldIntrinsic =
+ context.referenceFunctions(CallableId(FqName("kotlin.concurrent"), Name.identifier("getAndAddField")))
+ .single { it.owner.returnType.isLong() }
+
+ val kMutableProperty0Get = irBuiltIns.kMutableProperty0Class.functionByName("get")
+
+ val kMutableProperty0Set = irBuiltIns.kMutableProperty0Class.functionByName("set")
+
+ val intPlusOperator = context.referenceFunctions(CallableId(StandardClassIds.Int, Name.identifier("plus")))
+ .single { it.owner.valueParameters[0].type.isInt() }
+
+ val longPlusOperator = context.referenceFunctions(CallableId(StandardClassIds.Long, Name.identifier("plus")))
+ .single { it.owner.valueParameters[0].type.isLong() }
+
+ // KMutableProperty0<T>
+ fun kMutableProperty0Type(typeArg: IrType): IrType =
+ irSimpleType(irBuiltIns.kMutableProperty0Class, listOf(typeArg))
+
+ // () -> KMutableProperty0<T>
+ fun kMutableProperty0GetterType(typeArg: IrType): IrType = function0Type(kMutableProperty0Type(typeArg))
+
+ override fun createBuilder(symbol: IrSymbol, startOffset: Int, endOffset: Int) =
+ NativeAtomicfuIrBuilder(this, symbol, startOffset, endOffset)
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicfuIrBuilder.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicfuIrBuilder.kt
new file mode 100644
index 0000000..31e248a
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/backend/native/NativeAtomicfuIrBuilder.kt
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlinx.atomicfu.compiler.backend.native
+
+import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
+import org.jetbrains.kotlin.ir.builders.*
+import org.jetbrains.kotlin.ir.declarations.IrProperty
+import org.jetbrains.kotlin.ir.declarations.IrValueParameter
+import org.jetbrains.kotlin.ir.expressions.IrCall
+import org.jetbrains.kotlin.ir.expressions.IrExpression
+import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl
+import org.jetbrains.kotlin.ir.expressions.implicitCastTo
+import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
+import org.jetbrains.kotlin.ir.symbols.IrSymbol
+import org.jetbrains.kotlin.ir.types.*
+import org.jetbrains.kotlin.ir.util.dump
+import org.jetbrains.kotlin.ir.util.dumpKotlinLike
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.common.AbstractAtomicfuIrBuilder
+
+class NativeAtomicfuIrBuilder(
+ override val atomicSymbols: NativeAtomicSymbols,
+ symbol: IrSymbol,
+ startOffset: Int,
+ endOffset: Int
+): AbstractAtomicfuIrBuilder(atomicSymbols.irBuiltIns, symbol, startOffset, endOffset) {
+
+ internal fun irCallAtomicNativeIntrinsic(
+ functionName: String,
+ propertyRef: IrExpression,
+ valueType: IrType,
+ valueArguments: List<IrExpression?>
+ ): IrCall = when (functionName) {
+ "<get-value>" -> callGetter(atomicSymbols.kMutableProperty0Get, propertyRef)
+ "<set-value>", "lazySet" -> callSetter(atomicSymbols.kMutableProperty0Set, propertyRef, valueArguments[0])
+ "compareAndSet" -> compareAndSetField(propertyRef, valueType, valueArguments[0], valueArguments[1])
+ "getAndSet" -> getAndSetField(propertyRef, valueType, valueArguments[0])
+ "getAndAdd" -> getAndAddField(propertyRef, valueType, valueArguments[0])
+ "getAndIncrement" -> getAndIncrementField(propertyRef, valueType)
+ "getAndDecrement" -> getAndDecrementField(propertyRef, valueType)
+ "addAndGet" -> addAndGetField(propertyRef, valueType, valueArguments[0])
+ "incrementAndGet" -> incrementAndGetField(propertyRef, valueType)
+ "decrementAndGet" -> decrementAndGetField(propertyRef, valueType)
+ else -> error("Unsupported atomic function name $functionName")
+ }.let {
+ if (valueType.isBoolean() && it.type.isInt()) it.toBoolean() else it
+ }
+
+ private fun callGetter(getterSymbol: IrSimpleFunctionSymbol, receiver: IrExpression?): IrCall =
+ irCall(getterSymbol).apply {
+ dispatchReceiver = receiver
+ }
+// .let {
+// if (valueType.isBoolean() && it.type.isInt()) it.toBoolean() else it
+// }
+
+ private fun callSetter(setterSymbol: IrSimpleFunctionSymbol, receiver: IrExpression?, value: IrExpression?): IrCall =
+ irCall(setterSymbol).apply {
+ dispatchReceiver = receiver
+ putValueArgument(0, value)
+ }
+
+ private fun invokeRefGetter(refGetter: IrExpression) = irCall(atomicSymbols.invoke0Symbol).apply { dispatchReceiver = refGetter }
+
+ /*
+ inline fun <T> loop$atomicfu(refGetter: () -> KMutableProperty0<T>, action: (T) -> Unit) {
+ while (true) {
+ val cur = refGetter().get()
+ action(cur)
+ }
+ }
+ */
+ fun atomicfuLoopBody(refGetter: IrValueParameter, action: IrValueParameter) =
+ irBlockBody {
+ +irWhile().apply {
+ condition = irTrue()
+ body = irBlock {
+ // todo: replace with the get intrinsic call
+ val cur = createTmpVariable(
+ callGetter(atomicSymbols.kMutableProperty0Get, invokeRefGetter(irGet(refGetter))),
+ "atomicfu\$cur", false
+ )
+ +irCall(atomicSymbols.invoke1Symbol).apply {
+ dispatchReceiver = irGet(action)
+ putValueArgument(0, irGet(cur))
+ }
+ }
+ }
+ }
+
+ /*
+ inline fun update$atomicfu(refGetter: () -> KMutableProperty0<Int>, action: (Int) -> Int) {
+ while (true) {
+ val cur = refGetter().get()
+ val upd = action(cur)
+ if (refGetter().compareAndSetField(cur, upd)) return
+ }
+ }
+
+
+ inline fun getAndUpdate$atomicfu(refGetter: () -> KMutableProperty0<Int>, action: (Int) -> Int): Int {
+ while (true) {
+ val cur = refGetter().get()
+ val upd = action(cur)
+ if (refGetter().compareAndSetField(cur, upd)) return cur
+ }
+ }
+
+ inline fun getAndUpdate$atomicfu(refGetter: () -> KMutableProperty0<Int>, action: (Int) -> Int): Int {
+ while (true) {
+ val cur = refGetter().get()
+ val upd = action(cur)
+ if (refGetter().compareAndSetField(cur, upd)) return upd
+ }
+ }
+ */
+ fun atomicfuUpdateBody(
+ functionName: String,
+ valueType: IrType,
+ refGetter: IrValueParameter,
+ action: IrValueParameter
+ ) =
+ irBlockBody {
+ +irWhile().apply {
+ condition = irTrue()
+ body = irBlock {
+ val cur = createTmpVariable(
+ callGetter(atomicSymbols.kMutableProperty0Get, invokeRefGetter(irGet(refGetter))),
+ "atomicfu\$cur", false
+ )
+ val upd = createTmpVariable(
+ irCall(atomicSymbols.invoke1Symbol).apply {
+ dispatchReceiver = irGet(action)
+ putValueArgument(0, irGet(cur))
+ }, "atomicfu\$upd", false
+ )
+ +irIfThen(
+ type = atomicSymbols.irBuiltIns.unitType,
+ condition = irCallAtomicNativeIntrinsic(
+ functionName = "compareAndSet",
+ propertyRef = invokeRefGetter(irGet(refGetter)),
+ valueType = valueType,
+ valueArguments = listOf(irGet(cur), irGet(upd))
+ ),
+ thenPart = when (functionName) {
+ "update" -> irReturnUnit()
+ "getAndUpdate" -> irReturn(irGet(cur))
+ "updateAndGet" -> irReturn(irGet(upd))
+ else -> error("Unsupported atomicfu inline loop function name: $functionName")
+ }
+ )
+ }
+ }
+ }
+
+ private fun compareAndSetField(propertyRef: IrExpression, valueType: IrType, expected: IrExpression?, updated: IrExpression?) =
+ callNativeAtomicIntrinsic(propertyRef, atomicSymbols.compareAndSetFieldIntrinsic, valueType, expected, updated)
+
+ private fun getAndSetField(propertyRef: IrExpression, valueType: IrType, value: IrExpression?) =
+ callNativeAtomicIntrinsic(propertyRef, atomicSymbols.getAndSetFieldIntrinsic, valueType, value)
+
+ private fun getAndAddField(propertyRef: IrExpression, valueType: IrType, delta: IrExpression?): IrCall =
+ when {
+ valueType.isInt() ->
+ callNativeAtomicIntrinsic(propertyRef, atomicSymbols.getAndAddIntFieldIntrinsic, null, delta)
+ valueType.isLong() ->
+ callNativeAtomicIntrinsic(
+ propertyRef,
+ atomicSymbols.getAndAddLongFieldIntrinsic,
+ null,
+ delta?.implicitCastTo(context.irBuiltIns.longType)
+ )
+ else -> error("kotlin.native.internal/getAndAddField intrinsic is not supported for values of type ${valueType.dumpKotlinLike()}")
+ }
+
+ private fun addAndGetField(propertyRef: IrExpression, valueType: IrType, delta: IrExpression?): IrCall =
+ getAndAddField(propertyRef, valueType, delta).plus(delta)
+
+ private fun getAndIncrementField(propertyRef: IrExpression, valueType: IrType): IrCall {
+ val delta = if (valueType.isInt()) irInt(1) else irLong(1)
+ return getAndAddField(propertyRef, valueType, delta)
+ }
+
+ private fun getAndDecrementField(propertyRef: IrExpression, valueType: IrType): IrCall {
+ val delta = if (valueType.isInt()) irInt(-1) else irLong(-1)
+ return getAndAddField(propertyRef, valueType, delta)
+ }
+
+ private fun incrementAndGetField(propertyRef: IrExpression, valueType: IrType): IrCall {
+ val delta = if (valueType.isInt()) irInt(1) else irLong(1)
+ return addAndGetField(propertyRef, valueType, delta)
+ }
+
+ private fun decrementAndGetField(propertyRef: IrExpression, valueType: IrType): IrCall {
+ val delta = if (valueType.isInt()) irInt(-1) else irLong(-1)
+ return addAndGetField(propertyRef, valueType, delta)
+ }
+
+ private fun callNativeAtomicIntrinsic(
+ propertyRef: IrExpression,
+ symbol: IrSimpleFunctionSymbol,
+ typeArgument: IrType?,
+ vararg valueArguments: IrExpression?
+ ): IrCall =
+ irCall(symbol).apply {
+ extensionReceiver = propertyRef
+ typeArgument?.let { putTypeArgument(0, it) }
+ valueArguments.forEachIndexed { index, arg ->
+ putValueArgument(index, arg)
+ }
+ }
+
+ private fun IrCall.plus(other: IrExpression?): IrCall {
+ val returnType = this.symbol.owner.returnType
+ val plusOperatorSymbol = when {
+ returnType.isInt() -> atomicSymbols.intPlusOperator
+ returnType.isLong() -> atomicSymbols.longPlusOperator
+ else -> error("Return type of the function ${this.symbol.owner.dump()} is expected to be Int or Long, but found $returnType")
+ }
+ return irCall(plusOperatorSymbol).apply {
+ dispatchReceiver = this@plus
+ putValueArgument(0, other)
+ }
+ }
+
+ fun irPropertyReference(property: IrProperty, classReceiver: IrExpression?): IrPropertyReferenceImpl {
+ val backingField = requireNotNull(property.backingField) { "Backing field of the property $property should not be null" }
+ return IrPropertyReferenceImpl(
+ UNDEFINED_OFFSET, UNDEFINED_OFFSET,
+ type = atomicSymbols.irSimpleType(context.irBuiltIns.kMutableProperty0Class, listOf(backingField.type)),
+ symbol = property.symbol,
+ typeArgumentsCount = 0,
+ field = backingField.symbol,
+ getter = property.getter?.symbol,
+ setter = property.setter?.symbol
+ ).apply {
+ dispatchReceiver = classReceiver
+ }
+ }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt
index cdb0a158..725f55e 100644
--- a/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt
+++ b/plugins/atomicfu/atomicfu-compiler/src/org/jetbrains/kotlinx/atomicfu/compiler/extensions/AtomicfuLoweringExtension.kt
@@ -17,22 +17,32 @@
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.platform.isJs
import org.jetbrains.kotlin.platform.jvm.isJvm
-import org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm.AtomicSymbols
+import org.jetbrains.kotlin.platform.konan.isNative
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm.JvmAtomicSymbols
import org.jetbrains.kotlinx.atomicfu.compiler.backend.js.AtomicfuJsIrTransformer
import org.jetbrains.kotlinx.atomicfu.compiler.backend.jvm.AtomicfuJvmIrTransformer
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.native.AtomicfuNativeIrTransformer
+import org.jetbrains.kotlinx.atomicfu.compiler.backend.native.NativeAtomicSymbols
public open class AtomicfuLoweringExtension : IrGenerationExtension {
override fun generate(
moduleFragment: IrModuleFragment,
pluginContext: IrPluginContext
) {
- if (pluginContext.platform.isJvm()) {
- val atomicSymbols = AtomicSymbols(pluginContext.irBuiltIns, moduleFragment)
- AtomicfuJvmIrTransformer(pluginContext, atomicSymbols).transform(moduleFragment)
- }
- if (pluginContext.platform.isJs()) {
- for (file in moduleFragment.files) {
- AtomicfuClassLowering(pluginContext).runOnFileInOrder(file)
+ val platform = pluginContext.platform
+ when {
+ platform.isJvm() -> {
+ val atomicSymbols = JvmAtomicSymbols(pluginContext, moduleFragment)
+ AtomicfuJvmIrTransformer(pluginContext, atomicSymbols).transform(moduleFragment)
+ }
+ platform.isNative() -> {
+ val atomicSymbols = NativeAtomicSymbols(pluginContext, moduleFragment)
+ AtomicfuNativeIrTransformer(pluginContext, atomicSymbols).transform(moduleFragment)
+ }
+ platform.isJs() -> {
+ for (file in moduleFragment.files) {
+ AtomicfuClassLowering(pluginContext).runOnFileInOrder(file)
+ }
}
}
}
@@ -62,4 +72,4 @@
declaration.acceptChildrenVoid(this)
}
})
-}
\ No newline at end of file
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlin/konan/blackboxtest/AtomicfuNativeTestGenerated.java b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlin/konan/blackboxtest/AtomicfuNativeTestGenerated.java
new file mode 100644
index 0000000..4a5c764
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/test/org/jetbrains/kotlin/konan/blackboxtest/AtomicfuNativeTestGenerated.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.konan.blackboxtest;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseStandardTestCaseGroupProvider;
+import org.jetbrains.kotlin.test.util.KtTestUtil;
+import org.junit.jupiter.api.Tag;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateNativeTestsKt}. DO NOT MODIFY MANUALLY */
+@SuppressWarnings("all")
+@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox")
+@TestDataPath("$PROJECT_ROOT")
+@Tag("atomicfu")
+@UseStandardTestCaseGroupProvider()
+public class AtomicfuNativeTestGenerated extends AbstractNativeBlackBoxTest {
+ @Test
+ public void testAllFilesPresentInNativeBox() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox"), Pattern.compile("^(.+)\\.kt$"), null, true);
+ }
+
+ @Test
+ @TestMetadata("ArithmeticTest.kt")
+ public void testArithmeticTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArithmeticTest.kt");
+ }
+
+ @Test
+ @TestMetadata("ComplexLoopTest.kt")
+ public void testComplexLoopTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ComplexLoopTest.kt");
+ }
+
+ @Test
+ @TestMetadata("EasyAtomicExtensionTest.kt")
+ public void testEasyAtomicExtensionTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/EasyAtomicExtensionTest.kt");
+ }
+
+ @Test
+ @TestMetadata("ExtensionLoopTest.kt")
+ public void testExtensionLoopTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ExtensionLoopTest.kt");
+ }
+
+ @Test
+ @TestMetadata("InitBlockInitializationTest.kt")
+ public void testInitBlockInitializationTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/InitBlockInitializationTest.kt");
+ }
+
+ @Test
+ @TestMetadata("ParameterizedInlineFunExtensionTest.kt")
+ public void testParameterizedInlineFunExtensionTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ParameterizedInlineFunExtensionTest.kt");
+ }
+
+ @Test
+ @TestMetadata("TopLevelTest.kt")
+ public void testTopLevelTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/TopLevelTest.kt");
+ }
+
+ @Test
+ @TestMetadata("ArrayInlineExtensionTest.kt")
+ public void testArrayInlineExtensionTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArrayInlineExtensionTest.kt");
+ }
+
+ @Test
+ @TestMetadata("FieldInObjectTest.kt")
+ public void testFieldInObjectTest() throws Exception {
+ runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/FieldInObjectTest.kt");
+ }
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArithmeticTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArithmeticTest.kt
new file mode 100644
index 0000000..7c531cb
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArithmeticTest.kt
@@ -0,0 +1,147 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class IntArithmetic {
+ val _x = atomic(0)
+ val x get() = _x.value
+}
+
+class LongArithmetic {
+ val _x = atomic(4294967296)
+ val x get() = _x.value
+ val y = atomic(5000000000)
+ val z = atomic(2424920024888888848)
+ val max = atomic(9223372036854775807)
+}
+
+class BooleanArithmetic {
+ val _x = atomic(false)
+ val x get() = _x.value
+}
+
+class ReferenceArithmetic {
+ val _x = atomic<String?>(null)
+
+ private val prev = "djvndkjvnkdjvndr"
+
+ private val _next = atomic<Any?>(null)
+ private val _prev = atomic(prev)
+}
+
+class VisibilitiesTest {
+ val a = atomic(0)
+ public val b = atomic(1)
+ private val c = atomic(2)
+ internal val d = atomic(3)
+
+ fun test() {
+ a.lazySet(45)
+ b.lazySet(56)
+ c.lazySet(46)
+ d.lazySet(67)
+ }
+}
+
+@Test
+fun testGetValue() {
+ val a = IntArithmetic()
+ a._x.value = 5
+ assertEquals(5, a._x.value)
+ var aValue = a._x.value
+ assertEquals(5, aValue)
+ assertEquals(5, a.x)
+}
+
+@Test
+fun testAtomicCallPlaces() {
+ val a = IntArithmetic()
+ a._x.value = 5
+ a._x.compareAndSet(5, 42)
+ val res = a._x.compareAndSet(42, 45)
+ assertTrue(res)
+ assertTrue(a._x.compareAndSet(45, 77))
+ assertFalse(a._x.compareAndSet(95, 77))
+ a._x.compareAndSet(77, 88)
+ assertEquals(88, a._x.value)
+}
+
+@Test
+fun testInt() {
+ val a = IntArithmetic()
+ assertEquals(0, a.x)
+ val update = 3
+ assertEquals(0, a._x.getAndSet(update))
+ assertTrue(a._x.compareAndSet(update, 8))
+ a._x.lazySet(1)
+ assertEquals(1, a.x)
+ assertEquals(1, a._x.getAndSet(2))
+ assertEquals(2, a.x)
+ assertEquals(2, a._x.getAndIncrement())
+ assertEquals(3, a.x)
+ assertEquals(3, a._x.getAndDecrement())
+ assertEquals(2, a.x)
+ assertEquals(2, a._x.getAndAdd(2))
+ assertEquals(4, a.x)
+ assertEquals(7, a._x.addAndGet(3))
+ assertEquals(7, a.x)
+ assertEquals(8, a._x.incrementAndGet())
+ assertEquals(8, a.x)
+ assertEquals(7, a._x.decrementAndGet())
+ assertEquals(7, a.x)
+ assertTrue(a._x.compareAndSet(7, 10))
+}
+
+
+@Test
+fun testLong() {
+ val a = LongArithmetic()
+ assertEquals(2424920024888888848, a.z.value)
+ a.z.lazySet(8424920024888888848)
+ assertEquals(8424920024888888848, a.z.value)
+ assertEquals(8424920024888888848, a.z.getAndSet(8924920024888888848))
+ assertEquals(8924920024888888848, a.z.value)
+ assertEquals(8924920024888888849, a.z.incrementAndGet())
+ assertEquals(8924920024888888849, a.z.value)
+ assertEquals(8924920024888888849, a.z.getAndDecrement())
+ assertEquals(8924920024888888848, a.z.value)
+ assertEquals(8924920024888888848, a.z.getAndAdd(100000000000000000))
+ assertEquals(9024920024888888848, a.z.value)
+ assertEquals(-198452011965886959, a.z.addAndGet(-9223372036854775807))
+ assertEquals(-198452011965886959, a.z.value)
+ assertEquals(-198452011965886958, a.z.incrementAndGet())
+ assertEquals(-198452011965886958, a.z.value)
+ assertEquals(-198452011965886959, a.z.decrementAndGet())
+ assertEquals(-198452011965886959, a.z.value)
+}
+
+@Test
+fun testBoolean() {
+ val a = BooleanArithmetic()
+ a._x.value
+ assertEquals(false, a._x.value)
+ assertFalse(a.x)
+ a._x.lazySet(true)
+ assertTrue(a.x)
+ assertTrue(a._x.getAndSet(true))
+ assertTrue(a._x.compareAndSet(true, false))
+ assertFalse(a.x)
+}
+
+@Test
+fun testReference() {
+ val a = ReferenceArithmetic()
+ a._x.value = "aaa"
+ assertEquals("aaa", a._x.value)
+ a._x.lazySet("bb")
+ assertEquals("bb", a._x.value)
+ assertEquals("bb", a._x.getAndSet("ccc"))
+ assertEquals("ccc", a._x.value)
+}
+
+@Test
+fun visibiltiesTest() {
+ val a = VisibilitiesTest()
+ a.test()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArrayInlineExtensionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArrayInlineExtensionTest.kt
new file mode 100644
index 0000000..a9bb2ff
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ArrayInlineExtensionTest.kt
@@ -0,0 +1,83 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ArrayInlineExtensionTest {
+ val intArr = AtomicIntArray(10)
+ val a = atomic(100)
+ val longArr = AtomicLongArray(10)
+ val refArr = atomicArrayOfNulls<Any?>(5)
+
+ class A(val s: String)
+
+ private inline fun casLoop(to: Int): Int {
+ intArr[0].loop { cur ->
+ if (intArr[0].compareAndSet(cur, to)) return intArr[0].value
+ return 777
+ }
+ }
+
+ private inline fun casLoopExpression(to: Long): Long = longArr[3].loop { cur ->
+ if (longArr[3].compareAndSet(cur, to)) return longArr[3].value
+ return 777
+ }
+
+ private inline fun AtomicInt.extensionLoop(to: Int): Int {
+ loop { cur ->
+ if (compareAndSet(cur, to)) return value
+ return 777
+ }
+ }
+
+ private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
+ lazySet(cur + 10)
+ return if (compareAndSet(cur, to)) value else incrementAndGet()
+ }
+
+ private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int, index: Int): Int {
+ loop { cur ->
+ compareAndSet(cur, first)
+ intArr[index].compareAndSet(first, second)
+ return value
+ }
+ }
+
+ private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
+ loop { cur ->
+ compareAndSet(cur, to)
+ // TODO: revert this comment when atomic arrays are supported and original atomic extensions are removed completely.
+ // a.extensionLoop(5)
+ extensionLoop(to + 100)
+ return value
+ }
+ }
+
+ private inline fun AtomicInt.foo(to: Int): Int {
+ loop { cur ->
+ if (compareAndSet(cur, to)) return 777
+ else return value
+ }
+ }
+
+ private inline fun AtomicInt.bar(delta: Int): Int {
+ return foo(value + delta)
+ }
+
+ fun testIntExtensionLoops() {
+ assertEquals(5, casLoop(5))
+ assertEquals(6, casLoopExpression(6))
+ assertEquals(66, intArr[1].extensionLoop(66))
+ assertEquals(66, intArr[2].extensionLoop(66))
+ assertEquals(77, intArr[1].extensionLoopExpression(777))
+ assertEquals(99, intArr[1].extensionLoopMixedReceivers(88, 99, 1))
+ assertEquals(200, intArr[1].extensionLoopRecursive(100))
+ assertEquals(777, intArr[1].bar(100))
+ }
+}
+
+@Test
+fun testIntExtensionLoops() {
+ val testClass = ArrayInlineExtensionTest()
+ testClass.testIntExtensionLoops()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ComplexLoopTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ComplexLoopTest.kt
new file mode 100644
index 0000000..28f3009
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ComplexLoopTest.kt
@@ -0,0 +1,77 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+private val topLevelA = atomic(0)
+
+class ComplexLoopTest {
+ val a = atomic(10)
+ val long = atomic(6757L)
+ val b = atomic(11)
+ val c = atomic(12)
+ val r = atomic<String>("aaa")
+ val intArr = AtomicIntArray(10)
+
+ private inline fun AtomicInt.fooInt() {
+ loop { cur ->
+ if (compareAndSet(cur, 67)) return
+ }
+ }
+
+ private inline fun AtomicLong.fooLong() {
+ loop { cur ->
+ if (compareAndSet(cur, 67L)) return
+ }
+ }
+
+ private inline fun embeddedLoops(to: Int): Int =
+ a.loop { aValue ->
+ if (!a.compareAndSet(aValue, to)) return 666
+ b.loop { bValue ->
+ return if (b.compareAndSet(bValue, to)) a.value + b.value else 777
+ }
+ }
+
+ private inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int =
+ loop { cur1 ->
+ compareAndSet(cur1, to)
+ loop { cur2 ->
+ return cur2
+ }
+ }
+
+ private inline fun embeddedUpdate(to: Int): Int =
+ a.loop { aValue ->
+ a.compareAndSet(aValue, to)
+ return a.updateAndGet { cur -> cur + 100 }
+ }
+
+ private inline fun AtomicRef<String>.extesntionEmbeddedRefUpdate(to: String): String =
+ loop { value ->
+ compareAndSet(value, to)
+ return updateAndGet { cur -> "${cur}AAA" }
+ }
+
+ fun test() {
+ a.fooInt()
+ assertEquals(67, a.value)
+ b.fooInt()
+ assertEquals(67, b.value)
+ c.fooInt()
+ assertEquals(67, c.value)
+ long.fooLong()
+ assertEquals(67L, long.value)
+ assertEquals(24, embeddedLoops(12))
+ assertEquals(77, c.extensionEmbeddedLoops(77))
+ assertEquals(66, intArr[0].extensionEmbeddedLoops(66))
+ assertEquals(166, embeddedUpdate(66))
+ assertEquals("bbbAAA", r.extesntionEmbeddedRefUpdate("bbb"))
+ }
+}
+
+@Test
+fun testComplexLoopTest() {
+ val testClass = ComplexLoopTest()
+ testClass.test()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/EasyAtomicExtensionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/EasyAtomicExtensionTest.kt
new file mode 100644
index 0000000..e9285e4
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/EasyAtomicExtensionTest.kt
@@ -0,0 +1,30 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+import kotlin.reflect.*
+
+private class AAA {
+ val _a = atomic(67)
+ var intVal: Int = 77
+}
+
+inline fun AtomicInt.foo() {
+ val intialValue = value
+ assertEquals(intialValue, value)
+ value = 56
+ assertEquals(56, getAndSet(77))
+ assertEquals(77, value)
+ innerFoo(value)
+ assertEquals(1000, value)
+}
+
+inline fun AtomicInt.innerFoo(currentValue: Int) {
+ compareAndSet(currentValue, 1000)
+}
+
+@Test
+fun testAtomicExtension() {
+ val aClass = AAA()
+ aClass._a.foo()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ExtensionLoopTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ExtensionLoopTest.kt
new file mode 100644
index 0000000..cda1411
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ExtensionLoopTest.kt
@@ -0,0 +1,109 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ExtensionLoopTest {
+ val a = atomic(0)
+ val a1 = atomic(1)
+ val b = atomic(true)
+ val l = atomic(5000000000)
+ val r = atomic<A>(A("aaaa"))
+ val rs = atomic<String>("bbbb")
+
+ class A(val s: String)
+
+ private inline fun casLoop(to: Int): Int {
+ a.loop { cur ->
+ if (a.compareAndSet(cur, to)) return a.value
+ return 777
+ }
+ }
+
+ private inline fun casLoopExpression(to: Int): Int = a.loop { cur ->
+ if (a.compareAndSet(cur, to)) return a.value
+ return 777
+ }
+
+ private inline fun AtomicInt.extensionLoop(to: Int): Int {
+ loop { cur ->
+ if (compareAndSet(cur, to)) return value
+ return 777
+ }
+ }
+
+ private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
+ lazySet(cur + 10)
+ return if (compareAndSet(cur, to)) value + 1 else incrementAndGet()
+ }
+
+ // TODO: revert this when atomic arrays are supported.
+ // Atomic extensions should not contain invocations on receivers other than <this> extension receiver.
+ // Atomic arrays and invocations on atomic array elements are not supported for now
+ // -> original untransformed atomic extensions are not removed
+ // (they should not contain invocations on atomic properties that are already replaced with volatile properties)
+// private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int): Int {
+// loop { cur ->
+// compareAndSet(cur, first)
+// a.compareAndSet(first, second)
+// return value
+// }
+ //}
+
+ private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
+ loop { cur ->
+ compareAndSet(cur, to)
+ extensionLoop(5)
+ return value
+ }
+ }
+
+ private inline fun AtomicInt.foo(to: Int): Int {
+ loop { cur ->
+ if (compareAndSet(cur, to)) return 777
+ else return value
+ }
+ }
+
+ private inline fun AtomicInt.bar(delta: Int): Int {
+ return foo(value + delta)
+ }
+
+ inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int =
+ loop { cur1 ->
+ compareAndSet(value, to)
+ loop { cur2 ->
+ return cur2
+ }
+ }
+
+ fun testIntExtensionLoops() {
+ assertEquals(5, casLoop(5))
+ assertEquals(45, a.extensionEmbeddedLoops(45))
+ assertEquals(6, casLoopExpression(6))
+ assertEquals(17, a.extensionLoopExpression(777))
+ assertEquals(66, a.extensionLoop(66))
+ //assertEquals(99, a.extensionLoopMixedReceivers(88, 99))
+ assertEquals(5, a.extensionLoopRecursive(100))
+ assertEquals(777, a.bar(100))
+ }
+}
+
+
+private val ref = atomic<String>("aaa")
+
+private inline fun AtomicRef<String>.topLevelExtensionLoop(to: String): String = loop { cur ->
+ lazySet(cur + to)
+ return value
+}
+
+fun testTopLevelExtensionLoop() {
+ assertEquals("aaattt", ref.topLevelExtensionLoop("ttt"))
+}
+
+@Test
+fun testExtensionLoop() {
+ val testClass = ExtensionLoopTest()
+ testClass.testIntExtensionLoops()
+ testTopLevelExtensionLoop()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/FieldInObjectTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/FieldInObjectTest.kt
new file mode 100644
index 0000000..dd75936
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/FieldInObjectTest.kt
@@ -0,0 +1,65 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+import kotlin.random.*
+
+object Provider {
+ private val port = atomic(Random.nextInt(20, 90) * 100)
+ fun next(): Int = port.incrementAndGet()
+
+ private val _l = atomic(2424920024888888848)
+ fun getL() = _l.incrementAndGet()
+
+ val _ref = atomic<String?>(null)
+
+ val _x = atomic(false)
+
+ val intArr = AtomicIntArray(10)
+ val longArr = AtomicLongArray(10)
+ val refArr = atomicArrayOfNulls<Any?>(5)
+}
+
+//object DelegatedProvider {
+// val _a = atomic(42)
+// var a: Int by _a
+//
+// var vInt by atomic(77)
+//}
+
+@Test
+fun testFieldInObject() {
+ val port = Provider.next()
+ assertEquals(port + 1, Provider.next())
+
+ assertEquals(2424920024888888849, Provider.getL())
+
+ Provider._ref.compareAndSet(null, "abc")
+ assertEquals("abc", Provider._ref.value)
+
+ assertFalse(Provider._x.value)
+
+ Provider.intArr[8].value = 454
+ assertEquals(455, Provider.intArr[8].incrementAndGet())
+
+ Provider.longArr[8].value = 4544096409680468
+ assertEquals(4544096409680470, Provider.longArr[8].addAndGet(2))
+
+ Provider.refArr[1].value = Provider._ref.value
+ assertEquals("abc", Provider.refArr[1].value)
+}
+
+//@Test
+//fun testDelegatedPropertiesInObject() {
+// assertEquals(42, DelegatedProvider.a)
+// DelegatedProvider._a.compareAndSet(42, 56)
+// assertEquals(56, DelegatedProvider.a)
+// DelegatedProvider.a = 77
+// DelegatedProvider._a.compareAndSet(77, 66)
+// assertEquals(66, DelegatedProvider._a.value)
+// assertEquals(66, DelegatedProvider.a)
+//
+// assertEquals(77, DelegatedProvider.vInt)
+// DelegatedProvider.vInt = 55
+// assertEquals(110, DelegatedProvider.vInt * 2)
+//}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/InitBlockInitializationTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/InitBlockInitializationTest.kt
new file mode 100644
index 0000000..98a15fc
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/InitBlockInitializationTest.kt
@@ -0,0 +1,115 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class InitBlockInitializationTest {
+ val a: AtomicInt
+ val a1: AtomicInt
+ val b: AtomicBoolean
+ val l: AtomicLong
+ val r: AtomicRef<A>
+ val rs: AtomicRef<String>
+
+ init {
+ a = atomic(0)
+ a1 = atomic(1)
+ b = atomic(true)
+ l = atomic(5000000000)
+ r = atomic<A>(A("aaaa"))
+ rs = atomic<String>("bbbb")
+ }
+
+ class A(val s: String)
+
+ private inline fun casLoop(to: Int): Int {
+ a.loop { cur ->
+ if (a.compareAndSet(cur, to)) return a.value
+ return 777
+ }
+ }
+
+ private inline fun casLoopExpression(to: Int): Int = a.loop { cur ->
+ if (a.compareAndSet(cur, to)) return a.value
+ return 777
+ }
+
+ private inline fun AtomicInt.extensionLoop(to: Int): Int {
+ loop { cur ->
+ if (compareAndSet(cur, to)) return value
+ return 777
+ }
+ }
+
+ private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
+ lazySet(cur + 10)
+ return if (compareAndSet(cur, to)) value + 1 else incrementAndGet()
+ }
+
+ private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int): Int {
+ loop { cur ->
+ compareAndSet(cur, first)
+ compareAndSet(first, second)
+ return value
+ }
+ }
+
+ private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
+ loop { cur ->
+ compareAndSet(cur, to)
+ extensionLoop(5)
+ return value
+ }
+ }
+
+ private inline fun AtomicInt.foo(to: Int): Int {
+ loop { cur ->
+ if (compareAndSet(cur, to)) return 777
+ else return value
+ }
+ }
+
+ private inline fun AtomicInt.bar(delta: Int): Int {
+ return foo(value + delta)
+ }
+
+ inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int =
+ loop { cur1 ->
+ compareAndSet(value, to)
+ loop { cur2 ->
+ return cur2
+ }
+ }
+
+ fun testIntExtensionLoops() {
+ a.compareAndSet(0, 56)
+ assertEquals(56, a.value)
+ assertEquals(5, casLoop(5))
+ assertEquals(45, a.extensionEmbeddedLoops(45))
+ assertEquals(6, casLoopExpression(6))
+ assertEquals(17, a.extensionLoopExpression(777))
+ assertEquals(66, a.extensionLoop(66))
+ assertEquals(99, a.extensionLoopMixedReceivers(88, 99))
+ assertEquals(5, a.extensionLoopRecursive(100))
+ assertEquals(777, a.bar(100))
+ }
+}
+
+
+private val ref = atomic<String>("aaa")
+
+private inline fun AtomicRef<String>.topLevelExtensionLoop(to: String): String = loop { cur ->
+ lazySet(cur + to)
+ return value
+}
+
+fun testTopLevelExtensionLoop() {
+ assertEquals("aaattt", ref.topLevelExtensionLoop("ttt"))
+}
+
+@Test
+fun testInitBlockInitialization() {
+ val testClass = InitBlockInitializationTest()
+ testClass.testIntExtensionLoops()
+ testTopLevelExtensionLoop()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ParameterizedInlineFunExtensionTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ParameterizedInlineFunExtensionTest.kt
new file mode 100644
index 0000000..30fdb28
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/ParameterizedInlineFunExtensionTest.kt
@@ -0,0 +1,29 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class ParameterizedInlineFunExtensionTest {
+
+ private inline fun <S> AtomicRef<S>.foo(res1: S, res2: S, foo: (S) -> S): S {
+ val res = bar(res1, res2)
+ return res
+ }
+
+ private inline fun <S> AtomicRef<S>.bar(res1: S, res2: S): S {
+ return res2
+ }
+
+ private val tail = atomic("aaa")
+
+ fun testClose() {
+ val res = tail.foo("bbb", "ccc") { s -> s }
+ assertEquals("ccc", res)
+ }
+}
+
+@Test
+fun testParameterizedInlineFunExtensionTest() {
+ val testClass = ParameterizedInlineFunExtensionTest()
+ testClass.testClose()
+}
diff --git a/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/TopLevelTest.kt b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/TopLevelTest.kt
new file mode 100644
index 0000000..ca156ca
--- /dev/null
+++ b/plugins/atomicfu/atomicfu-compiler/testData/nativeBox/TopLevelTest.kt
@@ -0,0 +1,161 @@
+// FREE_COMPILER_ARGS: -Xplugin=/Users/Maria.Sokolova/IdeaProjects/kotlin/plugins/atomicfu/atomicfu-compiler/build/libs/kotlin-atomicfu-compiler-plugin-1.9.255-SNAPSHOT-atomicfu-1.jar
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+private val a = atomic(0)
+private val b = atomic(2424920024888888848)
+private val c = atomic(true)
+private val abcNode = atomic(ANode(BNode(CNode(8))))
+private val any = atomic<Any?>(null)
+
+private val intArr = AtomicIntArray(3)
+private val longArr = AtomicLongArray(5)
+private val booleanArr = AtomicBooleanArray(4)
+private val refArr = atomicArrayOfNulls<ANode<BNode<CNode>>>(5)
+private val anyRefArr = atomicArrayOfNulls<Any>(10)
+
+private val stringAtomicNullArr = atomicArrayOfNulls<String>(10)
+
+data class ANode<T>(val b: T)
+data class BNode<T>(val c: T)
+data class CNode(val d: Int)
+
+@Test
+fun testTopLevelInt() {
+ assertEquals(0, a.value)
+ assertEquals(0, a.getAndSet(3))
+ assertTrue(a.compareAndSet(3, 8))
+ a.lazySet(1)
+ assertEquals(1, a.value)
+ assertEquals(1, a.getAndSet(2))
+ assertEquals(2, a.value)
+ assertEquals(2, a.getAndIncrement())
+ assertEquals(3, a.value)
+ assertEquals(3, a.getAndDecrement())
+ assertEquals(2, a.value)
+ assertEquals(2, a.getAndAdd(2))
+ assertEquals(4, a.value)
+ assertEquals(7, a.addAndGet(3))
+ assertEquals(7, a.value)
+ assertEquals(8, a.incrementAndGet())
+ assertEquals(8, a.value)
+ assertEquals(7, a.decrementAndGet())
+ assertEquals(7, a.value)
+ assertTrue(a.compareAndSet(7, 10))
+}
+
+@Test
+fun testTopLevelLong() {
+ assertEquals(2424920024888888848, b.value)
+ b.lazySet(8424920024888888848)
+ assertEquals(8424920024888888848, b.value)
+ assertEquals(8424920024888888848, b.getAndSet(8924920024888888848))
+ assertEquals(8924920024888888848, b.value)
+ assertEquals(8924920024888888849, b.incrementAndGet())
+ assertEquals(8924920024888888849, b.value)
+ assertEquals(8924920024888888849, b.getAndDecrement())
+ assertEquals(8924920024888888848, b.value)
+ assertEquals(8924920024888888848, b.getAndAdd(100000000000000000))
+ assertEquals(9024920024888888848, b.value)
+ assertEquals(-198452011965886959, b.addAndGet(-9223372036854775807))
+ assertEquals(-198452011965886959, b.value)
+ assertEquals(-198452011965886958, b.incrementAndGet())
+ assertEquals(-198452011965886958, b.value)
+ assertEquals(-198452011965886959, b.decrementAndGet())
+ assertEquals(-198452011965886959, b.value)
+}
+
+@Test
+fun testTopLevelBoolean() {
+ assertTrue(c.value)
+ c.lazySet(false)
+ assertFalse(c.value)
+ assertTrue(!c.getAndSet(true))
+ assertTrue(c.compareAndSet(true, false))
+ assertFalse(c.value)
+}
+
+@Test
+fun testTopLevelRef() {
+ assertEquals(8, abcNode.value.b.c.d)
+ val newNode = ANode(BNode(CNode(76)))
+ assertEquals(8, abcNode.getAndSet(newNode).b.c.d)
+ assertEquals(76, abcNode.value.b.c.d)
+ val l = IntArray(4){i -> i}
+ any.lazySet(l)
+ assertEquals(2, (any.value as IntArray)[2])
+}
+
+@Test
+fun testTopLevelArrayOfNulls() {
+ assertEquals(null, stringAtomicNullArr[0].value)
+ assertTrue(stringAtomicNullArr[0].compareAndSet(null, "aa"))
+ stringAtomicNullArr[1].lazySet("aa")
+ assertTrue(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value)
+}
+
+fun testIntArray() {
+ assertTrue(intArr[0].compareAndSet(0, 3))
+ assertEquals(0, intArr[1].value)
+ intArr[0].lazySet(5)
+ assertEquals(5, intArr[0].value + intArr[1].value + intArr[2].value)
+ assertTrue(intArr[0].compareAndSet(5, 10))
+ assertEquals(10, intArr[0].getAndDecrement())
+ assertEquals(9, intArr[0].value)
+ intArr[2].value = 2
+ assertEquals(2, intArr[2].value)
+ assertTrue(intArr[2].compareAndSet(2, 34))
+ assertEquals(34, intArr[2].value)
+}
+
+fun testLongArray() {
+ longArr[0].value = 2424920024888888848
+ assertEquals(2424920024888888848, longArr[0].value)
+ longArr[0].lazySet(8424920024888888848)
+ assertEquals(8424920024888888848, longArr[0].value)
+ val ac = longArr[0].value
+ longArr[3].value = ac
+ assertEquals(8424920024888888848, longArr[3].getAndSet(8924920024888888848))
+ assertEquals(8924920024888888848, longArr[3].value)
+ val ac1 = longArr[3].value
+ longArr[4].value = ac1
+ assertEquals(8924920024888888849, longArr[4].incrementAndGet())
+ assertEquals(8924920024888888849, longArr[4].value)
+ assertEquals(8924920024888888849, longArr[4].getAndDecrement())
+ assertEquals(8924920024888888848, longArr[4].value)
+ longArr[4].value = 8924920024888888848
+ assertEquals(8924920024888888848, longArr[4].getAndAdd(100000000000000000))
+ val ac2 = longArr[4].value
+ longArr[1].value = ac2
+ assertEquals(9024920024888888848, longArr[1].value)
+ assertEquals(-198452011965886959, longArr[1].addAndGet(-9223372036854775807))
+ assertEquals(-198452011965886959, longArr[1].value)
+ assertEquals(-198452011965886958, longArr[1].incrementAndGet())
+ assertEquals(-198452011965886958, longArr[1].value)
+ assertEquals(-198452011965886959, longArr[1].decrementAndGet())
+ assertEquals(-198452011965886959, longArr[1].value)
+}
+
+fun testBooleanArray() {
+ assertFalse(booleanArr[1].value)
+ booleanArr[1].compareAndSet(false, true)
+ booleanArr[0].lazySet(true)
+ assertFalse(booleanArr[2].getAndSet(true))
+ assertTrue(booleanArr[0].value && booleanArr[1].value && booleanArr[2].value)
+}
+
+@Suppress("UNCHECKED_CAST")
+fun testRefArray() {
+ val a2 = ANode(BNode(CNode(2)))
+ val a3 = ANode(BNode(CNode(3)))
+ refArr[0].value = a2
+ assertEquals(2, refArr[0].value!!.b.c.d)
+ assertTrue(refArr[0].compareAndSet(a2, a3))
+ assertEquals(3, refArr[0].value!!.b.c.d)
+ val r0 = refArr[0].value
+ refArr[3].value = r0
+ assertEquals(3, refArr[3].value!!.b.c.d)
+ val a = abcNode.value
+ assertTrue(refArr[3].compareAndSet(a3, a))
+}