Snap for 8562061 from 8f4851d32831c91ff4f83e8c31242d5671e70305 to mainline-media-release

Change-Id: I0b4e4ef43796b40c290380493da1a9c8ad560c62
diff --git a/Android.bp b/Android.bp
index ce7ad79..23d101a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,23 +32,8 @@
         "-Xmulti-platform",
     ],
     visibility: ["//external/kotlinx.coroutines"],
-}
-
-java_binary_host {
-    name: "kotlinx_atomicfu_transformer",
-    srcs: [
-        "atomicfu-transformer/src/main/**/*.kt",
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
     ],
-    exclude_srcs: [
-        "atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt",
-    ],
-    static_libs: [
-        "kotlinx_metadata_jvm",
-        "asm-6.0",
-        "asm-commons-6.0",
-        "asm-tree-6.0",
-        "asm-util-6.0",
-        "slf4j-jdk14",
-    ],
-    main_class: "kotlinx.atomicfu.transformer.AtomicFUTransformerKT",
 }
diff --git a/CHANGES.md b/CHANGES.md
index fe0edc4..1ad8836 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,30 @@
 # Change log for kotlinx.atomicfu
 
+# Version 0.16.0
+
+* Update Kotlin to 1.5.0.
+* Supported x86_64-based watchOS simulator target. (#177).
+
+# Version 0.15.2
+
+* Update kotlinx-metadata to 0.2.0.
+* Update Kotlin to 1.4.30.
+* Added kotlin space repository.
+
+# Version 0.15.1
+
+* Maven central publication (#173).
+* Binary compatibility with IR (#170).
+* Supported garbage-free multi-append in debug trace (#172).
+
+# Version 0.15.0
+
+* Tracing atomic operations (#20).
+* Supported delegated properties (#83).
+* Fixed visibility modifiers of synthetic fields and classes (#144).
+* Introduced `size` method for atomic arrays (#149).
+* Update Kotlin to 1.4.10.
+
 # Version 0.14.4
 
 * Fixed bug when Maven plugin wasn't published
diff --git a/README.md b/README.md
index 04b1e61..3f86a27 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@
   * [User-defined extensions on atomics](#user-defined-extensions-on-atomics)
   * [Locks](#locks)
   * [Testing of lock-free data structures](#testing-lock-free-data-structures-on-jvm).
+  * [Tracing operations](#tracing-operations)
 
 ## Example
 
@@ -90,9 +91,7 @@
 
 ```kotlin
 private val _foo = atomic<T>(initial) // private atomic, convention is to name it with leading underscore
-public var foo: T                     // public val/var
-    get() = _foo.value
-    set(value) { _foo.value = value }
+public var foo: T by _foo            // public delegated property (val/var)
 ```  
 
 ## Gradle build setup
@@ -108,7 +107,7 @@
 
 ```groovy
 buildscript {
-    ext.atomicfu_version = '0.14.3'
+    ext.atomicfu_version = '0.16.0'
 
     dependencies {
         classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version"
@@ -156,7 +155,7 @@
 
 ```groovy
 atomicfu {
-  dependenciesVersion = '0.14.3' // set to null to turn-off auto dependencies
+  dependenciesVersion = '0.16.0' // set to null to turn-off auto dependencies
   transformJvm = true // set to false to turn off JVM transformation
   transformJs = true // set to false to turn off JS transformation
   variant = "FU" // JVM transformation variant: FU,VH, or BOTH 
@@ -170,7 +169,7 @@
 
 ```xml
 <properties>
-     <atomicfu.version>0.14.3</atomicfu.version>
+     <atomicfu.version>0.16.0</atomicfu.version>
 </properties> 
 ```
 
@@ -338,3 +337,35 @@
 ```
 
 For Gradle there is nothing else to add. Tests are always run using original (non-transformed) classes.
+
+### Tracing operations
+
+You can debug your tests tracing atomic operations with a special trace object:
+
+```kotlin
+private val trace = Trace()
+private val current = atomic(0, trace)
+
+fun update(x: Int): Int {           
+    // custom trace message
+    trace { "calling update($x)" }
+    // automatic tracing of modification operations 
+    return current.getAndAdd(x)
+}
+```      
+
+All trace messages are stored in a cyclic array inside `trace`. 
+ 
+You can optionally set the size of trace's message array and format function. For example, 
+you can add a current thread name to the traced messages:
+
+```kotlin
+private val trace = Trace(size = 64) {   
+    index, // index of a trace message 
+    text   // text passed when invoking trace { text }
+    -> "$index: [${Thread.currentThread().name}] $text" 
+} 
+```                           
+
+`trace` is only seen before transformation and completely erased after on Kotlin/JVM and Kotlin/JS.
+
diff --git a/RELEASE.md b/RELEASE.md
index 8c5b129..6f5e71d 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -51,10 +51,9 @@
     * Create a release named `<version>`. 
     * Cut & paste lines from [`CHANGES.md`](CHANGES.md) into description.
    
-13. In [Bintray](https://bintray.com/kotlin/kotlinx/kotlinx.atomicfu#) admin interface:
-    * Publish artifacts of the new version.
-    * Wait until newly published version becomes the most recent.
-    * Sync to Maven Central.
+13. In [Sonatype](oss.sonatype.org/#stagingRepositories) admin interface:
+    * Close the repository and wait for it to verify.
+    * Release it.
 
 14. Switch into `develop` branch:<br>
    `git checkout develop`
@@ -66,4 +65,4 @@
    `git merge origin/master`
    
 17. Push updates to `develop`:<br>
-   `git push`
\ No newline at end of file
+   `git push`
diff --git a/atomicfu-gradle-plugin/build.gradle b/atomicfu-gradle-plugin/build.gradle
index 864e88e..28091b6 100644
--- a/atomicfu-gradle-plugin/build.gradle
+++ b/atomicfu-gradle-plugin/build.gradle
@@ -5,9 +5,19 @@
 apply plugin: 'kotlin'
 apply plugin: 'java-gradle-plugin'
 
-apply from: rootProject.file('gradle/compile-options.gradle')
+if (rootProject.ext.jvm_ir_enabled) {
+    kotlin.target.compilations.all {
+        kotlinOptions.useIR = true
+    }
+}
 
-ext.configureKotlin(org.jetbrains.kotlin.gradle.tasks.KotlinCompile)
+// Gradle plugin must be compiled targeting the same Kotlin version as used by Gradle
+kotlin.sourceSets.all {
+    languageSettings {
+        apiVersion = "1.3"
+        languageVersion = "1.3"
+    }
+}
 
 dependencies {
     compile gradleApi()
@@ -39,13 +49,13 @@
         : atomicfu.kotlin.targets.js
 def atomicfuJsJarTask = atomicfu.tasks.getByName(jsLegacy.artifactsTaskName)
 
-def atomicfuMetadataJarTask = atomicfu.tasks.getByName(atomicfu.kotlin.targets.metadata.artifactsTaskName)
+def atomicfuMetadataOutput = atomicfu.kotlin.targets.metadata.compilations["main"].output.allOutputs
 
 // Write the plugin's classpath to a file to share with the tests
 task createClasspathManifest {
     dependsOn(atomicfuJvmJarTask)
     dependsOn(atomicfuJsJarTask)
-    dependsOn(atomicfuMetadataJarTask)
+    dependsOn(atomicfuMetadataOutput)
 
     def outputDir = file("$buildDir/$name")
     outputs.dir outputDir
@@ -55,7 +65,7 @@
         file("$outputDir/plugin-classpath.txt").text = (sourceSets.main.runtimeClasspath + configurations.testPluginClasspath).join("\n")
         file("$outputDir/atomicfu-jvm.txt").text = atomicfuJvmJarTask.archivePath
         file("$outputDir/atomicfu-js.txt").text = atomicfuJsJarTask.archivePath
-        file("$outputDir/atomicfu-metadata.txt").text = atomicfuMetadataJarTask.archivePath
+        file("$outputDir/atomicfu-metadata.txt").text = atomicfuMetadataOutput.join("\n")
     }
 }
 
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/Project.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/Project.kt
index 14b9274..f3f49e1 100644
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/Project.kt
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/Project.kt
@@ -83,8 +83,10 @@
                 repositories {
                     jcenter()
                     mavenCentral()
+                    maven { url 'https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev' }
                     maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
                     maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
+                    maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev' }
                     maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
                 }
 
@@ -94,4 +96,4 @@
             """.trimIndent()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/build.gradle b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/build.gradle
index 6010e28..7e21215 100644
--- a/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/build.gradle
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/build.gradle
@@ -5,6 +5,11 @@
 apply plugin: 'kotlinx-atomicfu'
 apply plugin: 'kotlin'
 
+// This flag is enabled to be able using JVM IR compiled dependencies (when build is ran with -Penable_jvm_ir)
+kotlin.target.compilations.all {
+    kotlinOptions.freeCompilerArgs += '-Xallow-jvm-ir-dependencies'
+}
+
 dependencies {
     compileOnly atomicfuJvm
     testRuntime atomicfuJvm
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/build.gradle b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/build.gradle
index a06e523..fc95366 100644
--- a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/build.gradle
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/build.gradle
@@ -6,7 +6,12 @@
 apply plugin: 'kotlin-multiplatform'
 
 kotlin {
-    jvm()
+    // This flag is enabled to be able using JVM IR compiled dependencies (when build is ran with -Penable_jvm_ir)
+    jvm() {
+        compilations.all {
+            kotlinOptions.freeCompilerArgs += '-Xallow-jvm-ir-dependencies'
+        }
+    }
     js()
 
     sourceSets {
diff --git a/atomicfu-maven-plugin/build.gradle b/atomicfu-maven-plugin/build.gradle
index a7ab2fc..8929be9 100644
--- a/atomicfu-maven-plugin/build.gradle
+++ b/atomicfu-maven-plugin/build.gradle
@@ -7,7 +7,7 @@
 
 apply from: rootProject.file('gradle/compile-options.gradle')
 
-ext.configureKotlin(org.jetbrains.kotlin.gradle.tasks.KotlinCompile)
+ext.configureKotlin()
 
 dependencies {
     compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
@@ -54,6 +54,11 @@
                         }
 
                         appendNode('repository').with {
+                            appendNode('id', 'dev')
+                            appendNode('url', 'https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev')
+                        }
+
+                        appendNode('repository').with {
                             appendNode('id', 'kotlinx')
                             appendNode('url', 'https://kotlin.bintray.com/kotlinx')
                         }
diff --git a/atomicfu-native/build.gradle b/atomicfu-native/build.gradle
index 025f68e..ecf6ddd 100644
--- a/atomicfu-native/build.gradle
+++ b/atomicfu-native/build.gradle
@@ -4,4 +4,4 @@
 
 // this is a kludge so that existing YouTrack config works, todo: remove
 // Deploy configurations run :atomicfu-native:bintrayUpload
-task bintrayUpload(dependsOn: ':atomicfu:publish')
\ No newline at end of file
+task publish(dependsOn: ':atomicfu:publish')
diff --git a/atomicfu-transformer/build.gradle b/atomicfu-transformer/build.gradle
index 9fb3e8e..c6304f0 100644
--- a/atomicfu-transformer/build.gradle
+++ b/atomicfu-transformer/build.gradle
@@ -6,7 +6,7 @@
 
 apply from: rootProject.file('gradle/compile-options.gradle')
 
-ext.configureKotlin(org.jetbrains.kotlin.gradle.tasks.KotlinCompile)
+ext.configureKotlin()
 
 dependencies {
     compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
@@ -17,5 +17,5 @@
     compile "org.slf4j:slf4j-api:$slf4j_version"
     runtime "org.slf4j:slf4j-simple:$slf4j_version"
     compile "org.mozilla:rhino:1.7.10"
-    compile "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0"
+    compile "org.jetbrains.kotlinx:kotlinx-metadata-jvm:$kotlinx_metadata_version"
 }
\ No newline at end of file
diff --git a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AsmUtil.kt b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AsmUtil.kt
index 6cdd9b4..ba08fbc 100644
--- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AsmUtil.kt
+++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AsmUtil.kt
@@ -42,6 +42,12 @@
         return cur
     }
 
+fun getInsnOrNull(from: AbstractInsnNode?, to: AbstractInsnNode?, predicate: (AbstractInsnNode) -> Boolean): AbstractInsnNode? {
+    var cur: AbstractInsnNode? = from?.next
+    while (cur != null && cur != to && !predicate(cur)) cur = cur.next
+    return cur
+}
+
 private fun AbstractInsnNode?.isUseless() = this is LabelNode || this is LineNumberNode || this is FrameNode
 
 fun InsnList.listUseful(limit: Int = Int.MAX_VALUE): List<AbstractInsnNode> {
@@ -69,6 +75,9 @@
 fun AbstractInsnNode.isReturn() =
     this.opcode == RETURN
 
+fun AbstractInsnNode.isInvokeVirtual() =
+        this.opcode == INVOKEVIRTUAL
+
 @Suppress("UNCHECKED_CAST")
 fun MethodNode.localVar(v: Int, node: AbstractInsnNode): LocalVariableNode? =
     (localVariables as List<LocalVariableNode>).firstOrNull { it.index == v && verifyLocalVarScopeStart(v, node, it.start)}
diff --git a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformer.kt b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformer.kt
index 750065c..c7c262c 100644
--- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformer.kt
+++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformer.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 @file:Suppress("EXPERIMENTAL_FEATURE_WARNING")
@@ -24,6 +24,10 @@
 private const val JLI_PKG = "java/lang/invoke"
 private const val ATOMIC = "atomic"
 
+private const val TRACE = "Trace"
+private const val TRACE_BASE = "TraceBase"
+private const val TRACE_FORMAT = "TraceFormat"
+
 private val INT_ARRAY_TYPE = getType("[I")
 private val LONG_ARRAY_TYPE = getType("[J")
 private val BOOLEAN_ARRAY_TYPE = getType("[Z")
@@ -73,8 +77,24 @@
 
 private const val GET_VALUE = "getValue"
 private const val SET_VALUE = "setValue"
+private const val GET_SIZE = "getSize"
 
 private const val AFU_CLS = "$AFU_PKG/AtomicFU"
+private const val TRACE_KT = "$AFU_PKG/TraceKt"
+private const val TRACE_BASE_CLS = "$AFU_PKG/$TRACE_BASE"
+
+private val TRACE_BASE_TYPE = getObjectType(TRACE_BASE_CLS)
+
+private val TRACE_APPEND = MethodId(TRACE_BASE_CLS, "append", getMethodDescriptor(VOID_TYPE, OBJECT_TYPE), INVOKEVIRTUAL)
+private val TRACE_APPEND_2 = MethodId(TRACE_BASE_CLS, "append", getMethodDescriptor(VOID_TYPE, OBJECT_TYPE, OBJECT_TYPE), INVOKEVIRTUAL)
+private val TRACE_APPEND_3 = MethodId(TRACE_BASE_CLS, "append", getMethodDescriptor(VOID_TYPE, OBJECT_TYPE, OBJECT_TYPE, OBJECT_TYPE), INVOKEVIRTUAL)
+private val TRACE_APPEND_4 = MethodId(TRACE_BASE_CLS, "append", getMethodDescriptor(VOID_TYPE, OBJECT_TYPE, OBJECT_TYPE, OBJECT_TYPE, OBJECT_TYPE), INVOKEVIRTUAL)
+private val TRACE_DEFAULT_ARGS = "I${OBJECT_TYPE.descriptor}"
+private const val DEFAULT = "\$default"
+private const val DELEGATE = "\$delegate"
+
+private val TRACE_FACTORY = MethodId(TRACE_KT, TRACE, "(IL$AFU_PKG/$TRACE_FORMAT;)L$AFU_PKG/$TRACE_BASE;", INVOKESTATIC)
+private val TRACE_PARTIAL_ARGS_FACTORY = MethodId(TRACE_KT, "$TRACE$DEFAULT", "(IL$AFU_PKG/$TRACE_FORMAT;$TRACE_DEFAULT_ARGS)L$AFU_PKG/$TRACE_BASE;", INVOKESTATIC)
 
 private val FACTORIES: Set<MethodId> = setOf(
     MethodId(AFU_CLS, ATOMIC, "(Ljava/lang/Object;)L$AFU_PKG/AtomicRef;", INVOKESTATIC),
@@ -86,7 +106,17 @@
     MethodId("$AFU_PKG/AtomicLongArray", "<init>", "(I)V", INVOKESPECIAL),
     MethodId("$AFU_PKG/AtomicBooleanArray", "<init>", "(I)V", INVOKESPECIAL),
     MethodId("$AFU_PKG/AtomicArray", "<init>", "(I)V", INVOKESPECIAL),
-    MethodId("$AFU_PKG/AtomicFU_commonKt", "atomicArrayOfNulls", "(I)L$AFU_PKG/AtomicArray;", INVOKESTATIC)
+    MethodId("$AFU_PKG/AtomicFU_commonKt", "atomicArrayOfNulls", "(I)L$AFU_PKG/AtomicArray;", INVOKESTATIC),
+
+    MethodId(AFU_CLS, ATOMIC, "(Ljava/lang/Object;L$TRACE_BASE_CLS;)L$AFU_PKG/AtomicRef;", INVOKESTATIC),
+    MethodId(AFU_CLS, ATOMIC, "(IL$TRACE_BASE_CLS;)L$AFU_PKG/AtomicInt;", INVOKESTATIC),
+    MethodId(AFU_CLS, ATOMIC, "(JL$TRACE_BASE_CLS;)L$AFU_PKG/AtomicLong;", INVOKESTATIC),
+    MethodId(AFU_CLS, ATOMIC, "(ZL$TRACE_BASE_CLS;)L$AFU_PKG/AtomicBoolean;", INVOKESTATIC),
+
+    MethodId(AFU_CLS, ATOMIC + DEFAULT, "(Ljava/lang/Object;L$TRACE_BASE_CLS;$TRACE_DEFAULT_ARGS)L$AFU_PKG/AtomicRef;", INVOKESTATIC),
+    MethodId(AFU_CLS, ATOMIC + DEFAULT, "(IL$TRACE_BASE_CLS;$TRACE_DEFAULT_ARGS)L$AFU_PKG/AtomicInt;", INVOKESTATIC),
+    MethodId(AFU_CLS, ATOMIC + DEFAULT, "(JL$TRACE_BASE_CLS;$TRACE_DEFAULT_ARGS)L$AFU_PKG/AtomicLong;", INVOKESTATIC),
+    MethodId(AFU_CLS, ATOMIC + DEFAULT, "(ZL$TRACE_BASE_CLS;$TRACE_DEFAULT_ARGS)L$AFU_PKG/AtomicBoolean;", INVOKESTATIC)
 )
 
 private operator fun Int.contains(bit: Int) = this and bit != 0
@@ -129,7 +159,7 @@
             return if (hasExternalAccess) mangleInternal(fuName) else fuName
         }
 
-    val refVolatileClassName = "${owner.replace('.', '/')}${name.capitalize()}RefVolatile"
+    val refVolatileClassName = "${owner.replace('.', '/')}$${name.capitalize()}RefVolatile"
     val staticRefVolatileField = refVolatileClassName.substringAfterLast("/").decapitalize()
 
     fun getPrimitiveType(vh: Boolean): Type = if (vh) typeInfo.originalType else typeInfo.transformedType
@@ -155,6 +185,9 @@
 
     private val fields = mutableMapOf<FieldId, FieldInfo>()
     private val accessors = mutableMapOf<MethodId, FieldInfo>()
+    private val traceFields = mutableSetOf<FieldId>()
+    private val traceAccessors = mutableSetOf<MethodId>()
+    private val fieldDelegates = mutableMapOf<FieldId, FieldInfo>()
     private val removeMethods = mutableSetOf<MethodId>()
 
     override fun transform() {
@@ -236,7 +269,7 @@
         val cv = TransformerCV(cw, vh, analyzePhase2 = false)
         try {
             ClassReader(ByteArrayInputStream(bytes)).accept(cv, SKIP_FRAMES)
-        } catch (e: Exception) {
+        } catch (e: Throwable) {
             error("Failed to transform: $e", cv.sourceInfo)
             e.printStackTrace(System.out)
             if (lastError == null) lastError = e
@@ -304,6 +337,10 @@
             getPotentialAccessorType(access, className, methodType)?.let { onType ->
                 return AccessorCollectorMV(onType.internalName, access, name, desc, signature, exceptions)
             }
+            if (name == "<init>" || name == "<clinit>") {
+                // check for copying atomic values into delegate fields and register potential delegate fields
+                return DelegateFieldsCollectorMV(access, name, desc, signature, exceptions)
+            }
             return null
         }
     }
@@ -329,16 +366,20 @@
                 val fieldType = getType(fi.desc)
                 val accessorMethod = MethodId(className, name, desc, accessToInvokeOpcode(access))
                 info("$field accessor $name found")
-                val fieldInfo = registerField(field, fieldType, isStatic)
-                fieldInfo.accessors += accessorMethod
-                accessors[accessorMethod] = fieldInfo
+                if (fieldType == TRACE_BASE_TYPE) {
+                    traceAccessors.add(accessorMethod)
+                } else {
+                    val fieldInfo = registerField(field, fieldType, isStatic)
+                    fieldInfo.accessors += accessorMethod
+                    accessors[accessorMethod] = fieldInfo
+                }
             }
         }
     }
 
     // returns a type on which this is a potential accessor
     private fun getPotentialAccessorType(access: Int, className: String, methodType: Type): Type? {
-        if (methodType.returnType !in AFU_TYPES) return null
+        if (methodType.returnType !in AFU_TYPES && methodType.returnType != TRACE_BASE_TYPE) return null
         return if (access and ACC_STATIC != 0) {
             if (access and ACC_FINAL != 0 && methodType.argumentTypes.isEmpty()) {
                 // accessor for top-level atomic
@@ -355,8 +396,57 @@
         }
     }
 
+    private inner class DelegateFieldsCollectorMV(
+            access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?
+    ) : MethodNode(ASM5, access, name, desc, signature, exceptions) {
+        override fun visitEnd() {
+            // register delegate field and the corresponding original atomic field
+            // getfield a: *Atomic
+            // putfield a$delegate: *Atomic
+            instructions.forEach { insn ->
+                if (insn is FieldInsnNode) {
+                    insn.checkGetFieldOrGetStatic()?.let { getfieldId ->
+                        val next = insn.next
+                        (next as? FieldInsnNode)?.checkPutFieldOrPutStatic()?.let { delegateFieldId ->
+                            if (delegateFieldId.name.endsWith(DELEGATE)) {
+                                // original atomic value is copied to the synthetic delegate atomic field <delegated field name>$delegate
+                                val originalField = fields[getfieldId]!!
+                                fieldDelegates[delegateFieldId] = originalField
+                            }
+                        }
+                    }
+                }
+                if (insn is MethodInsnNode) {
+                    val methodId = MethodId(insn.owner, insn.name, insn.desc, insn.opcode)
+                    if (methodId in FACTORIES) {
+                        (insn.nextUseful as? FieldInsnNode)?.checkPutFieldOrPutStatic()?.let { delegateFieldId ->
+                            if (delegateFieldId.name.endsWith(DELEGATE)) {
+                                // delegate field is initialized by a factory invocation
+                                val fieldType = getType(insn.desc).returnType
+                                // for volatile delegated properties store FieldInfo of the delegate field itself
+                                fieldDelegates[delegateFieldId] = FieldInfo(delegateFieldId, fieldType)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     private fun descToName(desc: String): String = desc.drop(1).dropLast(1)
 
+    private fun FieldInsnNode.checkPutFieldOrPutStatic(): FieldId? {
+        if (opcode != PUTFIELD && opcode != PUTSTATIC) return null
+        val fieldId = FieldId(owner, name, desc)
+        return if (fieldId in fields) fieldId else null
+    }
+
+    private fun FieldInsnNode.checkGetFieldOrGetStatic(): FieldId? {
+        if (opcode != GETFIELD && opcode != GETSTATIC) return null
+        val fieldId = FieldId(owner, name, desc)
+        return if (fieldId in fields) fieldId else null
+    }
+
     private inner class TransformerCV(
         cv: ClassVisitor?,
         private val vh: Boolean,
@@ -388,16 +478,24 @@
             val fieldType = getType(desc)
             if (fieldType.sort == OBJECT && fieldType.internalName in AFU_CLASSES) {
                 val fieldId = FieldId(className, name, desc)
+                // skip delegate field
+                if (fieldId in fieldDelegates && (fieldId != fieldDelegates[fieldId]!!.fieldId)) {
+                    transformed = true
+                    return null
+                }
                 val f = fields[fieldId]!!
-                val protection = when {
-                    // reference to wrapper class (primitive atomics) or reference to to j.u.c.a.Atomic*Array (atomic array)
-                    f.isStatic && !vh -> ACC_STATIC or ACC_FINAL or ACC_SYNTHETIC
-                    // primitive type field
-                    f.isStatic && vh -> ACC_STATIC or ACC_SYNTHETIC
-                    f.hasExternalAccess -> ACC_PUBLIC or ACC_SYNTHETIC
+                val visibility = when {
+                    f.hasExternalAccess -> ACC_PUBLIC
                     f.accessors.isEmpty() -> ACC_PRIVATE
                     else -> 0
                 }
+                val protection = ACC_SYNTHETIC or visibility or when {
+                    // reference to wrapper class (primitive atomics) or reference to to j.u.c.a.Atomic*Array (atomic array)
+                    f.isStatic && !vh -> ACC_STATIC or ACC_FINAL
+                    // primitive type field
+                    f.isStatic && vh -> ACC_STATIC
+                    else -> 0
+                }
                 val primitiveType = f.getPrimitiveType(vh)
                 val fv = when {
                     // replace (top-level) Atomic*Array with (static) j.u.c.a/Atomic*Array field
@@ -423,6 +521,12 @@
                 transformed = true
                 return fv
             }
+            // skip trace field
+            if (fieldType == TRACE_BASE_TYPE) {
+                traceFields += FieldId(className, name, desc)
+                transformed = true
+                return null
+            }
             return super.visitField(access, name, desc, signature, value)
         }
 
@@ -493,11 +597,11 @@
             exceptions: Array<out String>?
         ): MethodVisitor? {
             val methodId = MethodId(className, name, desc, accessToInvokeOpcode(access))
-            if (methodId in accessors || methodId in removeMethods) {
+            if (methodId in accessors || methodId in traceAccessors || methodId in removeMethods) {
                 // drop and skip the methods that were found in Phase 1
                 // todo: should remove those methods from kotlin metadata, too
                 transformed = true
-                return null
+                return null // drop accessor
             }
             val sourceInfo = SourceInfo(methodId, source)
             val superMV = if (name == "<clinit>" && desc == "()V") {
@@ -532,8 +636,8 @@
             // remove unused methods from metadata
             metadata?.let {
                 val mt = MetadataTransformer(
-                    removeFields = fields.keys,
-                    removeMethods = accessors.keys + removeMethods
+                    removeFields = fields.keys + traceFields,
+                    removeMethods = accessors.keys + traceAccessors + removeMethods
                 )
                 if (mt.transformMetadata(it)) transformed = true
                 if (cv != null) it.accept(cv.visitAnnotation(KOTLIN_METADATA_DESC, true))
@@ -616,10 +720,19 @@
                 accept(mv)
         }
 
-        private fun FieldInsnNode.checkPutFieldOrPutStatic(): FieldId? {
-            if (opcode != PUTFIELD && opcode != PUTSTATIC) return null
+        private fun FieldInsnNode.checkCopyToDelegate(): AbstractInsnNode? {
             val fieldId = FieldId(owner, name, desc)
-            return if (fieldId in fields) fieldId else null
+            if (fieldId in fieldDelegates) {
+                // original atomic value is copied to the synthetic delegate atomic field <delegated field name>$delegate
+                val originalField = fieldDelegates[fieldId]!!
+                val getField = previous as FieldInsnNode
+                val next = this.next
+                if (!originalField.isStatic) instructions.remove(getField.previous) // no aload for static field
+                instructions.remove(getField)
+                instructions.remove(this)
+                return next
+            }
+            return null
         }
 
         // ld: instruction that loads atomic field (already changed to getstatic)
@@ -636,7 +749,6 @@
                 check(!f.isArray || onArrayElement) { "getValue/setValue can only be called on elements of arrays" }
                 val setInsn = iv.name == SET_VALUE
                 if (!onArrayElement) {
-                    instructions.remove(ld) // drop getstatic (we don't need field updater)
                     val primitiveType = f.getPrimitiveType(vh)
                     val owner = if (!vh && f.isStatic) f.refVolatileClassName else f.owner
                     if (!vh && f.isStatic) {
@@ -646,8 +758,9 @@
                             f.staticRefVolatileField,
                             getObjectType(owner).descriptor
                         )
-                        instructions.insertBefore(iv, getOwnerClass)
+                        instructions.insert(ld, getOwnerClass)
                     }
+                    instructions.remove(ld) // drop getstatic (we don't need field updater)
                     val j = FieldInsnNode(
                         when {
                             iv.name == GET_VALUE -> if (f.isStatic && vh) GETSTATIC else GETFIELD
@@ -681,45 +794,65 @@
                     return iv
                 }
             }
+            if (f.isArray && iv.name == GET_SIZE) {
+                if (!vh) {
+                    // map to j.u.c.a.Atomic*Array length()
+                    iv.owner = descToName(f.fuType.descriptor)
+                    iv.name = "length"
+                } else {
+                    // replace with arraylength of the primitive type array
+                    val arrayLength = InsnNode(ARRAYLENGTH)
+                    instructions.insert(ld, arrayLength)
+                    // do not need varhandle
+                    if (!f.isStatic) {
+                        instructions.remove(ld.previous.previous)
+                        instructions.remove(ld.previous)
+                    } else {
+                        instructions.remove(ld.previous)
+                    }
+                    instructions.remove(iv)
+                    return arrayLength
+                }
+                return iv
+            }
             // An operation other than getValue/setValue is used
             if (f.isArray && iv.name == "get") { // "operator get" that retrieves array element, further ops apply to it
                 // fixup atomic operation on this array element
                 return fixupLoadedArrayElement(f, ld, iv)
+            }
+            // non-trivial atomic operation
+            check(f.isArray == onArrayElement) { "Atomic operations can be performed on atomic elements only" }
+            if (analyzePhase2) {
+                f.hasAtomicOps = true // mark the fact that non-trivial atomic op is used here
             } else {
-                // non-trivial atomic operation
-                check(f.isArray == onArrayElement) { "Atomic operations can be performed on atomic elements only" }
-                if (analyzePhase2) {
-                    f.hasAtomicOps = true // mark the fact that non-trivial atomic op is used here
-                } else {
-                    check(f.hasAtomicOps) // should have been set on previous phase
-                }
-                // update method invocation
-                if (vh) {
-                    vhOperation(iv, typeInfo, f)
-                } else {
-                    fuOperation(iv, typeInfo, f)
-                }
-                if (f.isStatic && !onArrayElement) {
-                    if (!vh) {
-                        // getstatic *RefVolatile class
-                        val aload = FieldInsnNode(
-                            GETSTATIC,
-                            f.owner,
-                            f.staticRefVolatileField,
-                            getObjectType(f.refVolatileClassName).descriptor
-                        )
-                        instructions.insert(ld, aload)
-                    }
-                    return iv.next
-                }
-                if (!onArrayElement) {
-                    // insert swap after field load
-                    val swap = InsnNode(SWAP)
-                    instructions.insert(ld, swap)
-                    return swap.next
+                check(f.hasAtomicOps) // should have been set on previous phase
+            }
+            // update method invocation
+            if (vh) {
+                vhOperation(iv, typeInfo, f)
+            } else {
+                fuOperation(iv, typeInfo, f)
+            }
+            if (f.isStatic && !onArrayElement) {
+                if (!vh) {
+                    // getstatic *RefVolatile class
+                    val aload = FieldInsnNode(
+                        GETSTATIC,
+                        f.owner,
+                        f.staticRefVolatileField,
+                        getObjectType(f.refVolatileClassName).descriptor
+                    )
+                    instructions.insert(ld, aload)
                 }
                 return iv.next
             }
+            if (!onArrayElement) {
+                // insert swap after field load
+                val swap = InsnNode(SWAP)
+                instructions.insert(ld, swap)
+                return swap.next
+            }
+            return iv.next
         }
 
         private fun vhOperation(iv: MethodInsnNode, typeInfo: TypeInfo, f: FieldInfo) {
@@ -832,8 +965,12 @@
             // this array element information is only used in case the reference to this element is stored (copied and inserted at the point of loading)
             val arrayElementInfo = mutableListOf<AbstractInsnNode>()
             if (vh) {
-                arrayElementInfo.add(ld.previous.previous) // getstatic VarHandle field
-                arrayElementInfo.add(ld.previous) // swap
+                if (!f.isStatic) {
+                    arrayElementInfo.add(ld.previous.previous) // getstatic VarHandle field
+                    arrayElementInfo.add(ld.previous) // swap
+                } else {
+                    arrayElementInfo.add(ld.previous) // getstatic VarHandle field
+                }
             }
             var i: AbstractInsnNode = ld
             while (i != getter) {
@@ -859,7 +996,8 @@
                     val onArrayElement = arrayElementInfo != null
                     check(f.isArray == onArrayElement)
                     // was stored to local -- needs more processing:
-                    // store owner ref into the variable instead
+                    // for class fields store owner ref into the variable instead
+                    // for static fields store nothing, remove the local var
                     val v = operation.`var`
                     val next = operation.next
                     if (onArrayElement) {
@@ -869,6 +1007,7 @@
                         instructions.remove(ld)
                     }
                     val lv = localVar(v, operation)
+                    if (f.isStatic) instructions.remove(operation) // remove astore operation
                     if (lv != null) {
                         // Stored to a local variable with an entry in LVT (typically because of inline function)
                         if (lv.desc != f.fieldType.descriptor && !onArrayElement)
@@ -890,12 +1029,15 @@
             }
         }
 
-        private fun fixupLoad(f: FieldInfo, ld: FieldInsnNode, otherLd: VarInsnNode, arrayElementInfo: List<AbstractInsnNode>?): AbstractInsnNode? =
-            if (arrayElementInfo != null) {
+        private fun fixupLoad(f: FieldInfo, ld: FieldInsnNode, otherLd: VarInsnNode, arrayElementInfo: List<AbstractInsnNode>?): AbstractInsnNode? {
+            val next = if (arrayElementInfo != null) {
                 fixupArrayElementLoad(f, ld, otherLd, arrayElementInfo)
             } else {
                 fixupVarLoad(f, ld, otherLd)
             }
+            if (f.isStatic) instructions.remove(otherLd) // remove aload instruction for static fields, nothing is stored there
+            return next
+        }
 
         private fun fixupVarLoad(f: FieldInfo, ld: FieldInsnNode, otherLd: VarInsnNode): AbstractInsnNode? {
             val ldCopy = ld.clone(null) as FieldInsnNode
@@ -946,29 +1088,29 @@
 
         private fun putPrimitiveTypeWrapper(
             factoryInsn: MethodInsnNode,
+            initStart: AbstractInsnNode,
             f: FieldInfo,
             next: FieldInsnNode
         ): AbstractInsnNode? {
             // generate wrapper class for static fields of primitive type
             val factoryArg = getMethodType(factoryInsn.desc).argumentTypes[0]
             generateRefVolatileClass(f, factoryArg)
-            val firstInitInsn = FlowAnalyzer(next).getInitStart()
             // remove calling atomic factory for static field and following putstatic
             val afterPutStatic = next.next
             instructions.remove(factoryInsn)
             instructions.remove(next)
-            initRefVolatile(f, factoryArg, firstInitInsn, afterPutStatic)
+            initRefVolatile(f, factoryArg, initStart, afterPutStatic)
             return afterPutStatic
         }
 
         private fun putJucaAtomicArray(
             arrayfactoryInsn: MethodInsnNode,
+            initStart: AbstractInsnNode,
             f: FieldInfo,
             next: FieldInsnNode
         ): AbstractInsnNode? {
             // replace with invoking j.u.c.a.Atomic*Array constructor
             val jucaAtomicArrayDesc = f.typeInfo.fuType.descriptor
-            val initStart = FlowAnalyzer(next).getInitStart().next
             if (initStart.opcode == NEW) {
                 // change descriptor of NEW instruction
                 (initStart as TypeInsnNode).desc = descToName(jucaAtomicArrayDesc)
@@ -992,10 +1134,10 @@
 
         private fun putPureVhArray(
             arrayFactoryInsn: MethodInsnNode,
+            initStart: AbstractInsnNode,
             f: FieldInfo,
             next: FieldInsnNode
         ): AbstractInsnNode? {
-            val initStart = FlowAnalyzer(next).getInitStart().next
             if (initStart.opcode == NEW) {
                 // remove dup
                 instructions.remove(initStart.next)
@@ -1015,6 +1157,55 @@
             return next.next
         }
 
+        // removes pushing atomic factory trace arguments
+        // returns the first value argument push
+        private fun removeTraceInit(atomicFactory: MethodInsnNode, isArrayFactory: Boolean): AbstractInsnNode {
+            val initStart = FlowAnalyzer(atomicFactory).getInitStart(1)
+            if (isArrayFactory) return initStart
+            var lastArg = atomicFactory.previous
+            val valueArgInitLast = FlowAnalyzer(atomicFactory).getValueArgInitLast()
+            while (lastArg != valueArgInitLast) {
+                val prev = lastArg.previous
+                instructions.remove(lastArg)
+                lastArg = prev
+            }
+            return initStart
+        }
+
+        private fun removeTraceAppend(append: AbstractInsnNode): AbstractInsnNode {
+            // remove append trace instructions: from append invocation up to getfield Trace or accessor to Trace field
+            val afterAppend = append.next
+            var start = append
+            val isGetFieldTrace = { insn: AbstractInsnNode ->
+                insn.opcode == GETFIELD && (start as FieldInsnNode).desc == getObjectType(TRACE_BASE_CLS).descriptor }
+            val isTraceAccessor = { insn: AbstractInsnNode ->
+                if (insn is MethodInsnNode) {
+                    val methodId = MethodId(insn.owner, insn.name, insn.desc, insn.opcode)
+                    methodId in traceAccessors
+                } else false
+            }
+            while (!(isGetFieldTrace(start) || isTraceAccessor(start))) {
+                start = start.previous
+            }
+            // now start contains Trace getfield insn or Trace accessor
+            if (isTraceAccessor(start)) {
+                instructions.remove(start.previous.previous)
+                instructions.remove(start.previous)
+            } else {
+                instructions.remove(start.previous)
+            }
+            while (start != afterAppend) {
+                if (start is VarInsnNode) {
+                    // remove all local store instructions
+                    localVariables.removeIf { it.index == (start as VarInsnNode).`var` }
+                }
+                val next = start.next
+                instructions.remove(start)
+                start = next
+            }
+            return afterAppend
+        }
+
         private fun transform(i: AbstractInsnNode): AbstractInsnNode? {
             when (i) {
                 is MethodInsnNode -> {
@@ -1026,15 +1217,18 @@
                             val fieldId = (next as? FieldInsnNode)?.checkPutFieldOrPutStatic()
                                 ?: abort("factory $methodId invocation must be followed by putfield")
                             val f = fields[fieldId]!!
+                            val isArray = AFU_CLASSES[i.owner]?.let { it.originalType.sort == ARRAY } ?: false
+                            // erase pushing arguments for trace initialisation
+                            val newInitStart = removeTraceInit(i, isArray)
                             // in FU mode wrap values of top-level primitive atomics into corresponding *RefVolatile class
                             if (!vh && f.isStatic && !f.isArray) {
-                                return putPrimitiveTypeWrapper(i, f, next)
+                                return putPrimitiveTypeWrapper(i, newInitStart, f, next)
                             }
                             if (f.isArray) {
                                 return if (vh) {
-                                    putPureVhArray(i, f, next)
+                                    putPureVhArray(i, newInitStart, f, next)
                                 } else {
-                                    putJucaAtomicArray(i, f, next)
+                                    putJucaAtomicArray(i, newInitStart, f, next)
                                 }
                             }
                             instructions.remove(i)
@@ -1063,10 +1257,44 @@
                             transformed = true
                             return fixupLoadedAtomicVar(f, j)
                         }
+                        methodId == TRACE_FACTORY || methodId == TRACE_PARTIAL_ARGS_FACTORY -> {
+                            if (methodId == TRACE_FACTORY) {
+                                // remove trace format initialization
+                                var checkcastTraceFormat = i
+                                while (checkcastTraceFormat.opcode != CHECKCAST) checkcastTraceFormat = checkcastTraceFormat.previous
+                                val astoreTraceFormat = checkcastTraceFormat.next
+                                val tranceFormatInitStart = FlowAnalyzer(checkcastTraceFormat.previous).getInitStart(1).previous
+                                var initInsn = checkcastTraceFormat
+                                while (initInsn != tranceFormatInitStart) {
+                                    val prev = initInsn.previous
+                                    instructions.remove(initInsn)
+                                    initInsn = prev
+                                }
+                                instructions.insertBefore(astoreTraceFormat, InsnNode(ACONST_NULL))
+                            }
+                            // remove trace factory and following putfield
+                            val argsSize = getMethodType(methodId.desc).argumentTypes.size
+                            val putfield = i.next
+                            val next = putfield.next
+                            val depth = if (i.opcode == INVOKESPECIAL) 2 else argsSize
+                            val initStart = FlowAnalyzer(i.previous).getInitStart(depth).previous
+                            var lastArg = i
+                            while (lastArg != initStart) {
+                                val prev = lastArg.previous
+                                instructions.remove(lastArg)
+                                lastArg = prev
+                            }
+                            instructions.remove(initStart) // aload of the parent class
+                            instructions.remove(putfield)
+                            return next
+                        }
+                        methodId == TRACE_APPEND || methodId == TRACE_APPEND_2 || methodId == TRACE_APPEND_3 || methodId == TRACE_APPEND_4 -> {
+                            return removeTraceAppend(i)
+                        }
                         methodId in removeMethods -> {
                             abort(
                                 "invocation of method $methodId on atomic types. " +
-                                        "Make the later method 'inline' to use it", i
+                                    "Make the latter method 'inline' to use it", i
                             )
                         }
                         i.opcode == INVOKEVIRTUAL && i.owner in AFU_CLASSES -> {
@@ -1077,6 +1305,10 @@
                 is FieldInsnNode -> {
                     val fieldId = FieldId(i.owner, i.name, i.desc)
                     if ((i.opcode == GETFIELD || i.opcode == GETSTATIC) && fieldId in fields) {
+                        if (fieldId in fieldDelegates && i.next.opcode == ASTORE) {
+                            return transformDelegatedFieldAccessor(i, fieldId)
+                        }
+                        (i.next as? FieldInsnNode)?.checkCopyToDelegate()?.let { return it } // atomic field is copied to delegate field
                         // Convert GETFIELD to GETSTATIC on var handle / field updater
                         val f = fields[fieldId]!!
                         val isArray = f.getPrimitiveType(vh).sort == ARRAY
@@ -1091,17 +1323,54 @@
                             i.name = f.name
                         }
                         i.desc = if (vh) VH_TYPE.descriptor else f.fuType.descriptor
+                        val prev = i.previous
                         if (vh && f.getPrimitiveType(vh).sort == ARRAY) {
-                            return insertPureVhArray(i, f)
+                            return getInsnOrNull(from = prev, to = insertPureVhArray(i, f)) { it.isAtomicGetFieldOrGetStatic() }
                         }
                         transformed = true
-                        return fixupLoadedAtomicVar(f, i)
+                        // in order not to skip the transformation of atomic field loads
+                        // check if there are any nested between the current atomic field load instruction i and it's transformed operation
+                        // and return the first one
+                        return getInsnOrNull(from = prev, to = fixupLoadedAtomicVar(f, i)) { it.isAtomicGetFieldOrGetStatic() }
                     }
                 }
             }
             return i.next
         }
 
+        private fun transformDelegatedFieldAccessor(i: FieldInsnNode, fieldId: FieldId): AbstractInsnNode {
+            val f = fieldDelegates[fieldId]!!
+            val astore = (i.next as? VarInsnNode) ?: abort("Method $name does not match the pattern of a delegated field accessor")
+            val v = astore.`var`
+            var cur: AbstractInsnNode = i
+            while (!(cur is VarInsnNode && cur.opcode == ALOAD && cur.`var` == v)) {
+                val next = cur.next
+                instructions.remove(cur)
+                cur = next
+            }
+            val invokeVirtual = FlowAnalyzer(cur.next).execute()
+            instructions.remove(cur)
+            check(invokeVirtual.isAtomicGetValueOrSetValue()) { "Aload of the field delegate $f should be followed with Atomic*.getValue()/setValue() invocation" }
+            val accessorName = (invokeVirtual as MethodInsnNode).name.substring(0, 3)
+            val isGetter = accessorName == "get"
+            val primitiveType = f.getPrimitiveType(vh)
+            val j = FieldInsnNode(if (isGetter) GETFIELD else PUTFIELD, f.owner, f.name, primitiveType.descriptor)
+            instructions.set(invokeVirtual, j)
+            localVariables.removeIf {
+                !(getType(it.desc).internalName == f.owner ||
+                        (!isGetter && getType(it.desc) == getType(desc).argumentTypes.first() && it.name == "<set-?>"))
+            }
+            return j.next
+        }
+
+        private fun AbstractInsnNode.isAtomicGetFieldOrGetStatic() =
+                this is FieldInsnNode && (opcode == GETFIELD || opcode == GETSTATIC) &&
+                        FieldId(owner, name, desc) in fields
+
+        private fun AbstractInsnNode.isAtomicGetValueOrSetValue() =
+                isInvokeVirtual() && (getObjectType((this as MethodInsnNode).owner) in AFU_TYPES) &&
+                        (name == GET_VALUE || name == SET_VALUE)
+
         private fun insertPureVhArray(getVarHandleInsn: FieldInsnNode, f: FieldInfo): AbstractInsnNode? {
             val getPureArray = FieldInsnNode(GETFIELD, f.owner, f.name, f.getPrimitiveType(vh).descriptor)
             if (!f.isStatic) {
@@ -1121,7 +1390,8 @@
         private fun generateRefVolatileClass(f: FieldInfo, arg: Type) {
             if (analyzePhase2) return // nop
             val cw = ClassWriter(0)
-            cw.visit(V1_6, ACC_PUBLIC or ACC_SYNTHETIC, f.refVolatileClassName, null, "java/lang/Object", null)
+            val visibility = if (f.hasExternalAccess) ACC_PUBLIC else 0
+            cw.visit(V1_6, visibility or ACC_SYNTHETIC, f.refVolatileClassName, null, "java/lang/Object", null)
             //creating class constructor
             val cons = cw.visitMethod(ACC_PUBLIC, "<init>", "(${arg.descriptor})V", null, null)
             code(cons) {
@@ -1135,8 +1405,7 @@
                 visitMaxs(3, 3)
             }
             //declaring volatile field of primitive type
-            val protection = ACC_VOLATILE
-            cw.visitField(protection, f.name, f.getPrimitiveType(vh).descriptor, null, null)
+            cw.visitField(visibility or ACC_VOLATILE, f.name, f.getPrimitiveType(vh).descriptor, null, null)
             val genFile = outputDir / "${f.refVolatileClassName}.class"
             genFile.mkdirsAndWrite(cw.toByteArray())
         }
@@ -1200,4 +1469,4 @@
     if (args.size > 2) t.variant = enumValueOf(args[2].toUpperCase(Locale.US))
     t.verbose = true
     t.transform()
-}
\ No newline at end of file
+}
diff --git a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt
index 1efa6e4..91f3037 100644
--- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt
+++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt
@@ -12,13 +12,23 @@
 import java.util.regex.*
 
 private const val ATOMIC_CONSTRUCTOR = """(atomic\$(ref|int|long|boolean)\$|Atomic(Ref|Int|Long|Boolean))"""
-private const val ATOMIC_ARRAY_CONSTRUCTOR = """Atomic(Ref|Int|Long|Boolean)Array\$(ref|int|long|boolean|ofNulls)"""
+private const val ATOMIC_CONSTRUCTOR_BINARY_COMPATIBILITY = """(atomic\$(ref|int|long|boolean)\$1)""" // mangled names for declarations left for binary compatibility
+private const val ATOMIC_ARRAY_CONSTRUCTOR = """(atomicfu)\$(Atomic(Ref|Int|Long|Boolean)Array)\$(ref|int|long|boolean|ofNulls)"""
 private const val MANGLED_VALUE_PROP = "kotlinx\$atomicfu\$value"
 
-private const val RECEIVER = "\$receiver"
+private const val TRACE_CONSTRUCTOR = "atomicfu\\\$Trace"
+private const val TRACE_BASE_CLASS = "atomicfu\\\$TraceBase"
+private const val TRACE_APPEND = """(atomicfu)\$(Trace)\$(append)\$([1234])""" // [1234] is the number of arguments in the append overload
+private const val TRACE_NAMED = "atomicfu\\\$Trace\\\$named"
+private const val TRACE_FORMAT = "TraceFormat"
+private const val TRACE_FORMAT_CONSTRUCTOR = "atomicfu\\\$$TRACE_FORMAT"
+private const val TRACE_FORMAT_FORMAT = "atomicfu\\\$$TRACE_FORMAT\\\$format"
+
+private const val RECEIVER = """(\$(receiver)(_\d+)?)"""
 private const val SCOPE = "scope"
 private const val FACTORY = "factory"
 private const val REQUIRE = "require"
+private const val PROTOTYPE = "prototype"
 private const val KOTLINX_ATOMICFU = "'kotlinx-atomicfu'"
 private const val KOTLINX_ATOMICFU_PACKAGE = "kotlinx.atomicfu"
 private const val KOTLIN_TYPE_CHECK = "Kotlin.isType"
@@ -26,9 +36,12 @@
 private const val MODULE_KOTLINX_ATOMICFU = "\\\$module\\\$kotlinx_atomicfu"
 private const val ARRAY = "Array"
 private const val FILL = "fill"
-private const val GET_ELEMENT = "get\\\$atomicfu"
+private const val GET_ELEMENT = "atomicfu\\\$get"
+private const val ARRAY_SIZE = "atomicfu\$size"
+private const val LENGTH = "length"
 private const val LOCKS = "locks"
-private const val REENTRANT_LOCK_ATOMICFU_SINGLETON = "$LOCKS.reentrantLock\\\$atomicfu"
+private const val REENTRANT_LOCK_ATOMICFU_SINGLETON = "$LOCKS.atomicfu\\\$reentrantLock"
+
 
 private val MANGLE_VALUE_REGEX = Regex(".${Pattern.quote(MANGLED_VALUE_PROP)}")
 // matches index until the first occurence of ')', parenthesised index expressions not supported
@@ -39,7 +52,11 @@
     outputDir: File
 ) : AtomicFUTransformerBase(inputDir, outputDir) {
     private val atomicConstructors = mutableSetOf<String>()
+    private val fieldDelegates = mutableMapOf<String, String>()
+    private val delegatedProperties = mutableMapOf<String, String>()
     private val atomicArrayConstructors = mutableMapOf<String, String?>()
+    private val traceConstructors = mutableSetOf<String>()
+    private val traceFormatObjects = mutableSetOf<String>()
 
     override fun transform() {
         info("Transforming to $outputDir")
@@ -62,6 +79,8 @@
         val root = p.parse(FileReader(file), null, 0)
         root.visit(DependencyEraser())
         root.visit(AtomicConstructorDetector())
+        root.visit(FieldDelegatesVisitor())
+        root.visit(DelegatedPropertyAccessorsVisitor())
         root.visit(TransformVisitor())
         root.visit(AtomicOperationsInliner())
         return root.eraseGetValue().toByteArray()
@@ -118,10 +137,10 @@
                     }
                 }
                 Token.FUNCTION -> {
-                    // erasing 'kotlinx-atomicfu' module passed as parameter
                     if (node is FunctionNode) {
                         val it = node.params.listIterator()
                         while (it.hasNext()) {
+                            // erasing 'kotlinx-atomicfu' module passed as parameter
                             if (isAtomicfuModule(it.next())) {
                                 it.remove()
                             }
@@ -198,22 +217,32 @@
                         val varInit = stmt.variables[0] as VariableInitializer
                         if (varInit.initializer is PropertyGet) {
                             val initializer = varInit.initializer.toSource()
-                            if (initializer.matches(Regex(kotlinxAtomicfuModuleName(ATOMIC_CONSTRUCTOR)))) {
+                            if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($ATOMIC_CONSTRUCTOR|$ATOMIC_CONSTRUCTOR_BINARY_COMPATIBILITY)""")))) {
                                 atomicConstructors.add(varInit.target.toSource())
                                 node.replaceChild(stmt, EmptyLine())
-                            } else if (initializer.matches(Regex(kotlinxAtomicfuModuleName(LOCKS)))){
+                            } else if (initializer.matches(Regex(kotlinxAtomicfuModuleName(TRACE_CONSTRUCTOR)))) {
+                                traceConstructors.add(varInit.target.toSource())
+                                node.replaceChild(stmt, EmptyLine())
+                            } else if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($LOCKS|$TRACE_FORMAT_CONSTRUCTOR|$TRACE_BASE_CLASS|$TRACE_NAMED)""")))) {
                                 node.replaceChild(stmt, EmptyLine())
                             }
                         }
                     }
                 }
             }
+            if (node is PropertyGet && node.property.toSource().matches(Regex(TRACE_FORMAT_FORMAT))) {
+                val target = node.target
+                node.property = Name().also { it.identifier = "emptyProperty" }
+                if (target is PropertyGet && target.property.toSource().matches(Regex(PROTOTYPE))) {
+                    traceFormatObjects.add(target.target.toSource())
+                }
+            }
             if (node is VariableInitializer && node.initializer is PropertyGet) {
                 val initializer = node.initializer.toSource()
                 if (initializer.matches(Regex(REENTRANT_LOCK_ATOMICFU_SINGLETON))) {
                     node.initializer = null
                 }
-                if (initializer.matches(Regex(kotlinxAtomicfuModuleName(ATOMIC_CONSTRUCTOR)))) {
+                if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($ATOMIC_CONSTRUCTOR|$ATOMIC_CONSTRUCTOR_BINARY_COMPATIBILITY)""")))) {
                     atomicConstructors.add(node.target.toSource())
                     node.initializer = null
                 }
@@ -239,6 +268,62 @@
         }
     }
 
+    inner class FieldDelegatesVisitor : NodeVisitor {
+        override fun visit(node: AstNode?): Boolean {
+            if (node is FunctionCall) {
+                val functionName = node.target.toSource()
+                if (atomicConstructors.contains(functionName)) {
+                    if (node.parent is Assignment) {
+                        val assignment = node.parent as Assignment
+                        val atomicField = assignment.left
+                        val constructorBlock = ((node.parent.parent as? ExpressionStatement)?.parent as? Block)
+                                ?: abort("Incorrect tree structure of the constructor block initializing ${node.parent.toSource()}")
+                        // check if there is a delegate field initialized by the reference to this atomic
+                        for (stmt in constructorBlock) {
+                            if (stmt is ExpressionStatement) {
+                                if (stmt.expression is Assignment) {
+                                    val delegateAssignment = stmt.expression as Assignment
+                                    if (delegateAssignment.right is PropertyGet) {
+                                        val initializer = delegateAssignment.right as PropertyGet
+                                        if (initializer.toSource() == atomicField.toSource()) {
+                                            // register field delegate and the original atomic field
+                                            fieldDelegates[(delegateAssignment.left as PropertyGet).property.toSource()] =
+                                                    (atomicField as PropertyGet).property.toSource()
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            return true
+        }
+    }
+
+    inner class DelegatedPropertyAccessorsVisitor : NodeVisitor {
+        override fun visit(node: AstNode?): Boolean {
+            if (node is PropertyGet) {
+                if (node.target is PropertyGet) {
+                    if ((node.target as PropertyGet).property.toSource() in fieldDelegates && node.property.toSource() == MANGLED_VALUE_PROP) {
+                        if (node.parent is ReturnStatement) {
+                            val getter = ((((node.parent.parent as? Block)?.parent as? FunctionNode)?.parent as? ObjectProperty)?.parent as? ObjectLiteral)
+                                    ?: abort("Incorrect tree structure of the accessor for the property delegated " +
+                                            "to the atomic field ${fieldDelegates[node.target.toSource()]}")
+                            val definePropertyCall = getter.parent as FunctionCall
+                            val stringLiteral = definePropertyCall.arguments[1] as? StringLiteral
+                                    ?: abort ("Object.defineProperty invocation should take a property name as the second argument")
+                            val delegatedProperty = stringLiteral.value.toString()
+                            delegatedProperties[delegatedProperty] = (node.target as PropertyGet).property.toSource()
+                        }
+                    }
+                }
+
+            }
+            return true
+        }
+    }
+
     inner class TransformVisitor : NodeVisitor {
         override fun visit(node: AstNode): Boolean {
             // remove atomic constructors from classes fields
@@ -247,7 +332,7 @@
                 if (atomicConstructors.contains(functionName)) {
                     if (node.parent is Assignment) {
                         val valueNode = node.arguments[0]
-                        (node.parent as InfixExpression).right = valueNode
+                        (node.parent as Assignment).right = valueNode
                     }
                     return true
                 } else if (atomicArrayConstructors.contains(functionName)) {
@@ -296,12 +381,75 @@
                         node.setLeftAndRight(targetNode, clearProperety)
                     }
                     // other cases with $receiver.kotlinx$atomicfu$value in inline functions
-                    else if (node.target.toSource() == RECEIVER) {
-                        val rr = ReceiverResolver()
+                    else if (node.target.toSource().matches(Regex(RECEIVER))) {
+                        val receiverName = node.target.toSource() // $receiver_i
+                        val rr = ReceiverResolver(receiverName)
                         node.enclosingFunction.visit(rr)
                         rr.receiver?.let { node.target = it }
                     }
                 }
+                if (node.property.toSource() in delegatedProperties) {
+                    // replace delegated property name with the name of the original atomic field
+                    val fieldDelegate = delegatedProperties[node.property.toSource()]
+                    val originalField = fieldDelegates[fieldDelegate]!!
+                    node.property = Name().apply { identifier = originalField }
+                }
+                // replace Atomic*Array.size call with `length` property on the pure type js array
+                if (node.property.toSource() == ARRAY_SIZE) {
+                    node.property = Name().also { it.identifier = LENGTH }
+                }
+            }
+            if (node is Block) {
+                for (stmt in node) {
+                    if (stmt is ExpressionStatement) {
+                        if (stmt.expression is Assignment) {
+                            // erase field initialisation
+                            val assignment = stmt.expression as Assignment
+                            if (assignment.right is FunctionCall) {
+                                val functionName = (assignment.right as FunctionCall).target.toSource()
+                                if (traceConstructors.contains(functionName)) {
+                                    node.replaceChild(stmt, EmptyLine())
+                                }
+                            }
+                        }
+                        if (stmt.expression is FunctionCall) {
+                            // erase append(text) call
+                            val funcNode = (stmt.expression as FunctionCall).target
+                            if (funcNode is PropertyGet && funcNode.property.toSource().matches(Regex(TRACE_APPEND))) {
+                                node.replaceChild(stmt, EmptyLine())
+                            }
+                        }
+                    }
+                }
+            }
+            if (node is Assignment && node.left is PropertyGet) {
+                val left = node.left as PropertyGet
+                if (traceFormatObjects.contains(left.target.toSource())) {
+                    if (node.right is FunctionCall) {
+                        // TraceFormatObject initialization
+                        (node.right as FunctionCall).arguments = listOf(Name().also { it.identifier = "null" })
+                    }
+                }
+            }
+            // remove TraceFormatObject constructor definition
+            if (node is FunctionNode && traceFormatObjects.contains(node.name)) {
+                val body  = node.body
+                for (stmt in body) { body.replaceChild(stmt, EmptyLine()) }
+            }
+            // remove TraceFormat from TraceFormatObject interfaces
+            if (node is Assignment && node.left is PropertyGet && node.right is ObjectLiteral) {
+                val left = node.left as PropertyGet
+                val metadata = node.right as ObjectLiteral
+                if (traceFormatObjects.contains(left.target.toSource())) {
+                    for (e in metadata.elements) {
+                        if (e.right is ArrayLiteral) {
+                            val array = (e.right as ArrayLiteral).toSource()
+                            if (array.contains(TRACE_FORMAT)) {
+                                (e.right as ArrayLiteral).elements = emptyList()
+                            }
+                        }
+                    }
+                }
             }
             return true
         }
@@ -328,11 +476,11 @@
 
 
     // receiver data flow
-    inner class ReceiverResolver : NodeVisitor {
+    inner class ReceiverResolver(private val receiverName: String) : NodeVisitor {
         var receiver: AstNode? = null
         override fun visit(node: AstNode): Boolean {
             if (node is VariableInitializer) {
-                if (node.target.toSource() == RECEIVER) {
+                if (node.target.toSource() == receiverName) {
                     receiver = node.initializer
                     return false
                 }
@@ -348,8 +496,9 @@
                 if (node.target is PropertyGet) {
                     val funcName = (node.target as PropertyGet).property
                     var field = (node.target as PropertyGet).target
-                    if (field.toSource() == RECEIVER) {
-                        val rr = ReceiverResolver()
+                    if (field.toSource().matches(Regex(RECEIVER))) {
+                        val receiverName = field.toSource() // $receiver_i
+                        val rr = ReceiverResolver(receiverName)
                         node.enclosingFunction.visit(rr)
                         if (rr.receiver != null) {
                             field = rr.receiver
@@ -431,65 +580,65 @@
     ): Boolean {
         val f = field.scopedSource()
         val code = when (funcName) {
-            "getAndSet\$atomicfu" -> {
+            "atomicfu\$getAndSet" -> {
                 val arg = args[0].toSource()
                 "(function($SCOPE) {var oldValue = $f; $f = $arg; return oldValue;})()"
             }
-            "compareAndSet\$atomicfu" -> {
+            "atomicfu\$compareAndSet" -> {
                 val expected = args[0].scopedSource()
                 val updated = args[1].scopedSource()
                 val equals = if (expected == "null") "==" else "==="
                 "(function($SCOPE) {return $f $equals $expected ? function() { $f = $updated; return true }() : false})()"
             }
-            "getAndIncrement\$atomicfu" -> {
+            "atomicfu\$getAndIncrement" -> {
                 "(function($SCOPE) {return $f++;})()"
             }
 
-            "getAndIncrement\$atomicfu\$long" -> {
+            "atomicfu\$getAndIncrement\$long" -> {
                 "(function($SCOPE) {var oldValue = $f; $f = $f.inc(); return oldValue;})()"
             }
 
-            "getAndDecrement\$atomicfu" -> {
+            "atomicfu\$getAndDecrement" -> {
                 "(function($SCOPE) {return $f--;})()"
             }
 
-            "getAndDecrement\$atomicfu\$long" -> {
+            "atomicfu\$getAndDecrement\$long" -> {
                 "(function($SCOPE) {var oldValue = $f; $f = $f.dec(); return oldValue;})()"
             }
 
-            "getAndAdd\$atomicfu" -> {
+            "atomicfu\$getAndAdd" -> {
                 val arg = args[0].scopedSource()
                 "(function($SCOPE) {var oldValue = $f; $f += $arg; return oldValue;})()"
             }
 
-            "getAndAdd\$atomicfu\$long" -> {
+            "atomicfu\$getAndAdd\$long" -> {
                 val arg = args[0].scopedSource()
                 "(function($SCOPE) {var oldValue = $f; $f = $f.add($arg); return oldValue;})()"
             }
 
-            "addAndGet\$atomicfu" -> {
+            "atomicfu\$addAndGet" -> {
                 val arg = args[0].scopedSource()
                 "(function($SCOPE) {$f += $arg; return $f;})()"
             }
 
-            "addAndGet\$atomicfu\$long" -> {
+            "atomicfu\$addAndGet\$long" -> {
                 val arg = args[0].scopedSource()
                 "(function($SCOPE) {$f = $f.add($arg); return $f;})()"
             }
 
-            "incrementAndGet\$atomicfu" -> {
+            "atomicfu\$incrementAndGet" -> {
                 "(function($SCOPE) {return ++$f;})()"
             }
 
-            "incrementAndGet\$atomicfu\$long" -> {
+            "atomicfu\$incrementAndGet\$long" -> {
                 "(function($SCOPE) {return $f = $f.inc();})()"
             }
 
-            "decrementAndGet\$atomicfu" -> {
+            "atomicfu\$decrementAndGet" -> {
                 "(function($SCOPE) {return --$f;})()"
             }
 
-            "decrementAndGet\$atomicfu\$long" -> {
+            "atomicfu\$decrementAndGet\$long" -> {
                 "(function($SCOPE) {return $f = $f.dec();})()"
             }
             else -> null
@@ -513,7 +662,7 @@
     }
 }
 
-private class EmptyLine: EmptyExpression() {
+private class EmptyLine : EmptyExpression() {
     override fun toSource(depth: Int) = "\n"
 }
 
diff --git a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/FlowAnalyzer.kt b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/FlowAnalyzer.kt
index 336f86c..d5b98a5 100644
--- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/FlowAnalyzer.kt
+++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/FlowAnalyzer.kt
@@ -46,15 +46,15 @@
         abort("Flow control falls after the end of the method")
     }
 
-    // from putfield/putstatic up to the start of initialisation
-    fun getInitStart(): AbstractInsnNode {
-        var i = start!!.previous
-        depth = -1
-        while (depth != 0 && i != null) {
+    // returns instruction preceding pushing arguments to the atomic factory
+    fun getInitStart(stack: Int): AbstractInsnNode {
+        var i = start
+        depth = -stack
+        while (i != null) {
             executeOne(i, false)
+            if (depth == 0) break
             i = i.previous
         }
-
         // This may be array initialization in JVM_IR generated bytecode.
         // Old BE does not empty stack after each array element,
         // but JVM_IR does, thus, we cannot just assume, that empty stack means initialization start,
@@ -67,13 +67,26 @@
                     executeOne(i, false)
                     i = i.previous
                 }
+                if (i == null) break
                 // Before ANEWARRAY there should be constant integer
                 executeOne(i, false)
-                i = i?.previous
-            } while (depth != 0 && i != null)
+                if (depth == 0) break
+                i = i.previous
+            } while (i != null)
         }
+        return i ?: abort("Backward flow control falls after the beginning of the method")
+    }
 
-        return i
+    fun getValueArgInitLast(): AbstractInsnNode {
+        var i = start
+        val valueArgSize = Type.getArgumentTypes((start as MethodInsnNode).desc)[0].size
+        depth = -1
+        while (i != null) {
+            executeOne(i, false)
+            i = i.previous
+            if (depth == -valueArgSize) return i
+        }
+        abort("Backward flow control falls after the beginning of the method")
     }
 
     private fun AbstractInsnNode?.isArrayStore(): Boolean = when(this?.opcode) {
diff --git a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt
index 1ba8d64..f8bcaf1 100644
--- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt
+++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt
@@ -37,17 +37,17 @@
             is KotlinClassMetadata.Class -> {
                 val w = KotlinClassMetadata.Class.Writer()
                 metadata.accept(ClassFilter(w))
-                w.write(hdr.metadataVersion, hdr.bytecodeVersion, hdr.extraInt)
+                w.write(hdr.metadataVersion, hdr.extraInt)
             }
             is KotlinClassMetadata.FileFacade -> {
                 val w = KotlinClassMetadata.FileFacade.Writer()
                 metadata.accept(PackageFilter(w))
-                w.write(hdr.metadataVersion, hdr.bytecodeVersion, hdr.extraInt)
+                w.write(hdr.metadataVersion, hdr.extraInt)
             }
             is KotlinClassMetadata.MultiFileClassPart -> {
                 val w = KotlinClassMetadata.MultiFileClassPart.Writer()
                 metadata.accept(PackageFilter(w))
-                w.write(metadata.facadeClassName, hdr.metadataVersion, hdr.bytecodeVersion, hdr.extraInt)
+                w.write(metadata.facadeClassName, hdr.metadataVersion, hdr.extraInt)
             }
             else -> return false // not transformed
         }
diff --git a/atomicfu/build.gradle b/atomicfu/build.gradle
index 682c48a..66cfcb8 100644
--- a/atomicfu/build.gradle
+++ b/atomicfu/build.gradle
@@ -29,26 +29,9 @@
         nodejs()
     }
 
-    targets {
-        // JVM -- always
-        fromPreset(presets.jvm, 'jvm')
+    // JVM -- always
+    jvm()
 
-        if (project.ext.ideaActive) {
-            addNative(fromPreset(project.ext.ideaPreset, 'native'))
-        } else {
-            addTarget(presets.linuxX64)
-            addTarget(presets.iosArm64)
-            addTarget(presets.iosArm32)
-            addTarget(presets.iosX64)
-            addTarget(presets.macosX64)
-            addTarget(presets.mingwX64)
-            addTarget(presets.tvosArm64)
-            addTarget(presets.tvosX64)
-            addTarget(presets.watchosArm32)
-            addTarget(presets.watchosArm64)
-            addTarget(presets.watchosX86)
-        }
-    }
     sourceSets {
         commonMain {
             dependencies {
@@ -84,43 +67,70 @@
                 implementation "junit:junit:$junit_version"
             }
         }
-        nativeMain { dependsOn commonMain }
-
-        nativeTest {}
-
-        if (!project.ext.ideaActive) {
-            configure(nativeMainSets) {
-                dependsOn nativeMain
-            }
-
-            configure(nativeTestSets) {
-                dependsOn nativeTest
-            }
-        }
-    }
-
-    configure(nativeCompilations) {
-        cinterops {
-            interop {
-                defFile 'src/nativeInterop/cinterop/interop.def'
-            }
-        }
     }
 }
 
-// Hack for publishing as HMPP: pack the cinterop klib as a source set:
-if (!project.ext.ideaActive) {
-    kotlin.sourceSets {
-        nativeInterop
-        nativeMain.dependsOn(nativeInterop)
+// configure native targets only if they're not disabled
+if (rootProject.ext.native_targets_enabled) {
+    kotlin {
+        targets {
+            if (project.ext.ideaActive) {
+                addNative(fromPreset(project.ext.ideaPreset, 'native'))
+            } else {
+                addTarget(presets.linuxX64)
+                addTarget(presets.iosArm64)
+                addTarget(presets.iosArm32)
+                addTarget(presets.iosX64)
+                addTarget(presets.macosX64)
+                addTarget(presets.mingwX64)
+                addTarget(presets.tvosArm64)
+                addTarget(presets.tvosX64)
+                addTarget(presets.watchosArm32)
+                addTarget(presets.watchosArm64)
+                addTarget(presets.watchosX86)
+                addTarget(presets.watchosX64)
+            }
+        }
+
+        sourceSets {
+            nativeMain { dependsOn commonMain }
+
+            nativeTest {}
+
+            if (!project.ext.ideaActive) {
+                configure(nativeMainSets) {
+                    dependsOn nativeMain
+                }
+
+                configure(nativeTestSets) {
+                    dependsOn nativeTest
+                }
+            }
+        }
+
+        configure(nativeCompilations) {
+            cinterops {
+                interop {
+                    defFile 'src/nativeInterop/cinterop/interop.def'
+                }
+            }
+        }
     }
 
-    apply from: "$rootDir/gradle/interop-as-source-set-klib.gradle"
+    // Hack for publishing as HMPP: pack the cinterop klib as a source set:
+    if (!project.ext.ideaActive) {
+        kotlin.sourceSets {
+            nativeInterop
+            nativeMain.dependsOn(nativeInterop)
+        }
 
-    registerInteropAsSourceSetOutput(
-            kotlin.linuxX64().compilations["main"].cinterops["interop"],
-            kotlin.sourceSets["nativeInterop"]
-    )
+        apply from: "$rootDir/gradle/interop-as-source-set-klib.gradle"
+
+        registerInteropAsSourceSetOutput(
+                kotlin.linuxX64().compilations["main"].cinterops["interop"],
+                kotlin.sourceSets["nativeInterop"]
+        )
+    }
 }
 
 configurations {
@@ -129,7 +139,7 @@
 
 apply from: rootProject.file('gradle/compile-options.gradle')
 
-ext.configureKotlin(org.jetbrains.kotlin.gradle.tasks.KotlinCompile)
+ext.configureKotlin(true)
 
 dependencies {
     transformer project(":atomicfu-transformer")
@@ -204,6 +214,12 @@
 def classesPostTransformVH = file("$buildDir/classes/kotlin/jvm/postTransformedVH")
 def classesPostTransformBOTH = file("$buildDir/classes/kotlin/jvm/postTransformedBOTH")
 
+tasks.withType(compileTestKotlinJvm.getClass()) {
+    kotlinOptions {
+        jvmTarget = "1.6"
+    }
+}
+
 task transformFU(type: JavaExec, dependsOn: compileTestKotlinJvm) {
     main = "kotlinx.atomicfu.transformer.AtomicFUTransformerKt"
     args = [classesPreAtomicFuDir, classesPostTransformFU, "FU"]
@@ -242,32 +258,37 @@
     executable = "$System.env.JDK_16/bin/java"
     classpath = configurations.jvmTestRuntimeClasspath + project.files(classesPostTransformFU)
     testClassesDirs = project.files(classesPostTransformFU)
-    exclude '**/*LFTest.*'
+    exclude '**/*LFTest.*', '**/TraceToStringTest.*', '**/AtomicfuReferenceJsTest.*'
+    filter { setFailOnNoMatchingTests(false) } // to run excluded tests in Idea
 }
 
 task transformedTestFU_current(type: Test, dependsOn: transformFU) {
     classpath = files(configurations.jvmTestRuntimeClasspath, classesPostTransformFU)
     testClassesDirs = project.files(classesPostTransformFU)
-    exclude '**/*LFTest.*'
+    exclude '**/*LFTest.*', '**/TraceToStringTest.*', '**/AtomicfuReferenceJsTest.*'
+    filter { setFailOnNoMatchingTests(false) }
 }
 
 task transformedTestBOTH_6(type: Test, dependsOn: [checkJdk16, transformBOTH]) {
     executable = "$System.env.JDK_16/bin/java"
     classpath = files(configurations.jvmTestRuntimeClasspath, classesPostTransformBOTH)
     testClassesDirs = project.files(classesPostTransformBOTH)
-    exclude '**/*LFTest.*'
+    exclude '**/*LFTest.*', '**/TraceToStringTest.*', '**/TopLevelGeneratedDeclarationsReflectionTest.*', '**/SyntheticFUFieldsTest.*', '**/AtomicfuReferenceJsTest.*'
+    filter { setFailOnNoMatchingTests(false) }
 }
 
 task transformedTestBOTH_current(type: Test, dependsOn: transformBOTH) {
     classpath = files(configurations.jvmTestRuntimeClasspath, classesPostTransformBOTH)
     testClassesDirs = project.files(classesPostTransformBOTH)
-    exclude '**/*LFTest.*'
+    exclude '**/*LFTest.*', '**/TraceToStringTest.*', '**/TopLevelGeneratedDeclarationsReflectionTest.*', '**/SyntheticFUFieldsTest.*', '**/AtomicfuReferenceJsTest.*'
+    filter { setFailOnNoMatchingTests(false) }
 }
 
 task transformedTestVH(type: Test, dependsOn: transformVH) {
     classpath = files(configurations.jvmTestRuntimeClasspath, classesPostTransformVH)
     testClassesDirs = project.files(classesPostTransformVH)
-    exclude '**/*LFTest.*'
+    exclude '**/*LFTest.*', '**/TraceToStringTest.*', '**/TopLevelGeneratedDeclarationsReflectionTest.*', '**/SyntheticFUFieldsTest.*', '**/AtomicfuReferenceJsTest.*'
+    filter { setFailOnNoMatchingTests(false) }
 }
 
 transformedTestVH.onlyIf {
@@ -275,12 +296,21 @@
     JavaVersion.current().ordinal() >= JavaVersion.VERSION_1_9.ordinal()
 }
 
+task testAtomicfuReferenceJs(type: Test, dependsOn: [compileTestKotlinJvm, transformJS]) {
+    environment "transformedJsFile", transformedJsFile
+    classpath = files(configurations.jvmTestRuntimeClasspath, classesPreAtomicFuDir)
+    testClassesDirs = project.files(classesPreAtomicFuDir)
+    include '**/AtomicfuReferenceJsTest.*'
+    filter { setFailOnNoMatchingTests(false) }
+}
+
 task jvmTestAll(dependsOn: [
         transformedTestFU_6,
         transformedTestFU_current,
         transformedTestBOTH_6,
         transformedTestBOTH_current,
-        transformedTestVH])
+        transformedTestVH,
+        testAtomicfuReferenceJs])
 
 tasks.withType(Test) {
     testLogging {
@@ -290,15 +320,13 @@
 }
 
 jvmTest {
-    exclude "**/AtomicfuBytecodeTest*" // run it only for transformed code
+    exclude "**/AtomicfuBytecodeTest*", "**/AtomicfuReferenceJsTest*", '**/TopLevelGeneratedDeclarationsReflectionTest.*', '**/SyntheticFUFieldsTest.*' // run them only for transformed code
 }
 
 jvmTest.dependsOn jvmTestAll
 
 afterEvaluate {
     publishing.publications {
-        metadata.artifactId = 'atomicfu-common'
-
         kotlinMultiplatform {
             apply from: "$rootDir/gradle/publish-mpp-root-module-in-platform.gradle"
             publishPlatformArtifactsInRootModule(jvm)
@@ -306,6 +334,6 @@
     }
 }
 
-tasks.matching { it.name == "generatePomFileForKotlinMultiplatformPublication"}.configureEach {
+tasks.matching { it.name == "generatePomFileForKotlinMultiplatformPublication" }.configureEach {
     dependsOn(tasks["generatePomFileForJvmPublication"])
 }
diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt
index 37e3c79..39950e6 100644
--- a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt
+++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/AtomicFU.common.kt
@@ -2,14 +2,32 @@
  * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
 package kotlinx.atomicfu
 
 import kotlin.js.JsName
+import kotlin.internal.InlineOnly
+import kotlinx.atomicfu.TraceBase.None
+import kotlin.reflect.KProperty
+
+/**
+ * Creates atomic reference with a given [initial] value and a [trace] object to [trace modifications][Trace] of the value.
+ *
+ * It can only be used to initialize a private or internal read-only property, like this:
+ *
+ * ```
+ * private val f = atomic<Type>(initial, trace)
+ * ```
+ */
+public expect fun <T> atomic(initial: T, trace: TraceBase = None): AtomicRef<T>
+
+// Binary compatibility with IR, should be removed with Kotlin 1.5 release
 
 /**
  * Creates atomic reference with a given [initial] value.
  *
- * It can only be used in initialize of private read-only property, like this:
+ * It can only be used to initialize a private or internal read-only property, like this:
  *
  * ```
  * private val f = atomic<Type>(initial)
@@ -18,9 +36,22 @@
 public expect fun <T> atomic(initial: T): AtomicRef<T>
 
 /**
+ * Creates atomic [Int] with a given [initial] value and a [trace] object to [trace modifications][Trace] of the value.
+ *
+ * It can only be used to initialize a private or internal read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialInt, trace)
+ * ```
+ */
+public expect fun atomic(initial: Int, trace: TraceBase = None): AtomicInt
+
+// Binary compatibility with IR, should be removed with Kotlin 1.5 release
+
+/**
  * Creates atomic [Int] with a given [initial] value.
  *
- * It can only be used in initialize of private read-only property, like this:
+ * It can only be used to initialize a private or internal read-only property, like this:
  *
  * ```
  * private val f = atomic(initialInt)
@@ -29,9 +60,22 @@
 public expect fun atomic(initial: Int): AtomicInt
 
 /**
+ * Creates atomic [Long] with a given [initial] value and a [trace] object to [trace modifications][Trace] of the value.
+ *
+ * It can only be used to initialize a private or internal read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialLong, trace)
+ * ```
+ */
+public expect fun atomic(initial: Long, trace: TraceBase = None): AtomicLong
+
+// Binary compatibility with IR, should be removed with Kotlin 1.5 release
+
+/**
  * Creates atomic [Long] with a given [initial] value.
  *
- * It can only be used in initialize of private read-only property, like this:
+ * It can only be used to initialize a private or internal read-only property, like this:
  *
  * ```
  * private val f = atomic(initialLong)
@@ -40,9 +84,22 @@
 public expect fun atomic(initial: Long): AtomicLong
 
 /**
+ * Creates atomic [Boolean] with a given [initial] value and a [trace] object to [trace modifications][Trace] of the value.
+ *
+ * It can only be used to initialize a private or internal read-only property, like this:
+ *
+ * ```
+ * private val f = atomic(initialBoolean, trace)
+ * ```
+ */
+public expect fun atomic(initial: Boolean, trace: TraceBase = None): AtomicBoolean
+
+// Binary compatibility with IR, should be removed with Kotlin 1.5 release
+
+/**
  * Creates atomic [Boolean] with a given [initial] value.
  *
- * It can only be used in initialize of private read-only property, like this:
+ * It can only be used to initialize a private or internal read-only property, like this:
  *
  * ```
  * private val f = atomic(initialBoolean)
@@ -53,7 +110,7 @@
 /**
  * Creates array of AtomicRef<T> of specified size, where each element is initialised with null value
  */
-@JsName("AtomicLongArray\$ofNulls")
+@JsName(ATOMIC_ARRAY_OF_NULLS)
 public fun <T> atomicArrayOfNulls(size: Int): AtomicArray<T?> = AtomicArray(size)
 
 // ==================================== AtomicRef ====================================
@@ -69,6 +126,12 @@
      */
     public var value: T
 
+    @InlineOnly
+    public inline operator fun getValue(thisRef: Any?, property: KProperty<*>): T
+
+    @InlineOnly
+    public inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T)
+
     /**
      * Maps to [AtomicReferenceFieldUpdater.lazySet].
      */
@@ -141,6 +204,12 @@
      */
     public var value: Boolean
 
+    @InlineOnly
+    public inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean
+
+    @InlineOnly
+    public inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean)
+
     /**
      * Maps to [AtomicIntegerFieldUpdater.lazySet].
      */
@@ -213,6 +282,12 @@
      */
     public var value: Int
 
+    @InlineOnly
+    public inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Int
+
+    @InlineOnly
+    public inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int)
+
     /**
      * Maps to [AtomicIntegerFieldUpdater.lazySet].
      */
@@ -246,7 +321,7 @@
     /**
      * Maps to [AtomicIntegerFieldUpdater.addAndGet].
      */
-    public fun addAndGet(delta: Int): Int 
+    public fun addAndGet(delta: Int): Int
 
     /**
      * Maps to [AtomicIntegerFieldUpdater.incrementAndGet].
@@ -324,6 +399,12 @@
      */
     public var value: Long
 
+    @InlineOnly
+    public operator fun getValue(thisRef: Any?, property: KProperty<*>): Long
+
+    @InlineOnly
+    public operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Long)
+
     /**
      * Maps to [AtomicLongFieldUpdater.lazySet].
      */
@@ -427,11 +508,15 @@
 /**
  * Creates a new array of AtomicInt values of the specified size, where each element is initialised with 0
  */
-@JsName("AtomicIntArray\$int")
+@JsName(ATOMIC_INT_ARRAY)
 public class AtomicIntArray(size: Int) {
     private val array = Array(size) { atomic(0) }
 
-    @JsName("get\$atomicfu")
+    @JsName(ARRAY_SIZE)
+    public val size: Int
+        get() = array.size
+
+    @JsName(ARRAY_ELEMENT_GET)
     public operator fun get(index: Int): AtomicInt = array[index]
 }
 
@@ -440,11 +525,15 @@
 /**
  * Creates a new array of AtomicLong values of the specified size, where each element is initialised with 0L
  */
-@JsName("AtomicLongArray\$long")
+@JsName(ATOMIC_LONG_ARRAY)
 public class AtomicLongArray(size: Int) {
     private val array = Array(size) { atomic(0L) }
 
-    @JsName("get\$atomicfu")
+    @JsName(ARRAY_SIZE)
+    public val size: Int
+        get() = array.size
+
+    @JsName(ARRAY_ELEMENT_GET)
     public operator fun get(index: Int): AtomicLong = array[index]
 }
 
@@ -453,21 +542,29 @@
 /**
  * Creates a new array of AtomicBoolean values of the specified size, where each element is initialised with false
  */
-@JsName("AtomicBooleanArray\$boolean")
+@JsName(ATOMIC_BOOLEAN_ARRAY)
 public class AtomicBooleanArray(size: Int) {
     private val array = Array(size) { atomic(false) }
 
-    @JsName("get\$atomicfu")
+    @JsName(ARRAY_SIZE)
+    public val size: Int
+        get() = array.size
+
+    @JsName(ARRAY_ELEMENT_GET)
     public operator fun get(index: Int): AtomicBoolean = array[index]
 }
 
 
 // ==================================== AtomicArray ====================================
 
-@JsName("AtomicRefArray\$ref")
+@JsName(ATOMIC_REF_ARRAY)
 public class AtomicArray<T> internal constructor(size: Int) {
     private val array = Array(size) { atomic<T?>(null) }
 
-    @JsName("get\$atomicfu")
+    @JsName(ARRAY_SIZE)
+    public val size: Int
+        get() = array.size
+
+    @JsName(ARRAY_ELEMENT_GET)
     public operator fun get(index: Int): AtomicRef<T?> = array[index]
 }
diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt
new file mode 100644
index 0000000..99b4298
--- /dev/null
+++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/MangledJsNames.kt
@@ -0,0 +1,63 @@
+package kotlinx.atomicfu
+
+/**
+ * All atomicfu declarations are annotated with [@JsName][kotlin.js.JsName] to have specific names in JS output.
+ * JS output transformer relies on these mangled names to erase all atomicfu references.
+ */
+
+// Atomic factory functions
+internal const val ATOMIC_REF_FACTORY = "atomic\$ref\$"
+internal const val ATOMIC_REF_FACTORY_BINARY_COMPATIBILITY = "atomic\$ref\$1"
+internal const val ATOMIC_INT_FACTORY = "atomic\$int\$"
+internal const val ATOMIC_INT_FACTORY_BINARY_COMPATIBILITY = "atomic\$int\$1"
+internal const val ATOMIC_LONG_FACTORY = "atomic\$long\$"
+internal const val ATOMIC_LONG_FACTORY_BINARY_COMPATIBILITY = "atomic\$long\$1"
+internal const val ATOMIC_BOOLEAN_FACTORY = "atomic\$boolean\$"
+internal const val ATOMIC_BOOLEAN_FACTORY_BINARY_COMPATIBILITY = "atomic\$boolean\$1"
+
+// Atomic value
+internal const val ATOMIC_VALUE = "kotlinx\$atomicfu\$value"
+
+// Atomic operations
+internal const val COMPARE_AND_SET = "atomicfu\$compareAndSet"
+internal const val GET_AND_SET = "atomicfu\$getAndSet"
+internal const val GET_AND_INCREMENT = "atomicfu\$getAndIncrement"
+internal const val GET_AND_INCREMENT_LONG = "atomicfu\$getAndIncrement\$long"
+internal const val GET_AND_DECREMENT = "atomicfu\$getAndDecrement"
+internal const val GET_AND_DECREMENT_LONG = "atomicfu\$getAndDecrement\$long"
+internal const val INCREMENT_AND_GET = "atomicfu\$incrementAndGet"
+internal const val INCREMENT_AND_GET_LONG = "atomicfu\$incrementAndGet\$long"
+internal const val DECREMENT_AND_GET = "atomicfu\$decrementAndGet"
+internal const val DECREMENT_AND_GET_LONG = "atomicfu\$decrementAndGet\$long"
+internal const val GET_AND_ADD = "atomicfu\$getAndAdd"
+internal const val GET_AND_ADD_LONG = "atomicfu\$getAndAdd\$long"
+internal const val ADD_AND_GET = "atomicfu\$addAndGet"
+internal const val ADD_AND_GET_LONG = "atomicfu\$addAndGet\$long"
+
+// Atomic arrays constructors
+internal const val ATOMIC_ARRAY_OF_NULLS = "atomicfu\$AtomicRefArray\$ofNulls"
+internal const val ATOMIC_INT_ARRAY = "atomicfu\$AtomicIntArray\$int"
+internal const val ATOMIC_LONG_ARRAY = "atomicfu\$AtomicLongArray\$long"
+internal const val ATOMIC_BOOLEAN_ARRAY = "atomicfu\$AtomicBooleanArray\$boolean"
+internal const val ATOMIC_REF_ARRAY = "atomicfu\$AtomicRefArray\$ref"
+
+// Atomic array operations
+internal const val ARRAY_SIZE = "atomicfu\$size"
+internal const val ARRAY_ELEMENT_GET = "atomicfu\$get"
+
+// Locks
+internal const val REENTRANT_LOCK = "atomicfu\$reentrantLock"
+
+// Trace
+internal const val TRACE_FACTORY_FUNCTION = "atomicfu\$Trace"
+internal const val TRACE_BASE_CONSTRUCTOR = "atomicfu\$TraceBase"
+internal const val TRACE_NAMED = "atomicfu\$Trace\$named"
+internal const val TRACE_FORMAT_CLASS = "atomicfu\$TraceFormat"
+internal const val TRACE_FORMAT_FORMAT_FUNCTION = "atomicfu\$TraceFormat\$format"
+
+// Trace methods that append logging events to the trace
+// [1234] used as a suffix is the number of arguments in the append overload
+internal const val TRACE_APPEND_1 = "atomicfu\$Trace\$append\$1"
+internal const val TRACE_APPEND_2 = "atomicfu\$Trace\$append\$2"
+internal const val TRACE_APPEND_3 = "atomicfu\$Trace\$append\$3"
+internal const val TRACE_APPEND_4 = "atomicfu\$Trace\$append\$4"
\ No newline at end of file
diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/Trace.common.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/Trace.common.kt
new file mode 100644
index 0000000..4cc1e40
--- /dev/null
+++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/Trace.common.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
+package kotlinx.atomicfu
+
+import kotlin.js.JsName
+import kotlin.internal.InlineOnly
+
+/**
+ * Creates `Trace` object for tracing atomic operations.
+ *
+ * To use a trace create a separate field for `Trace`:
+ *
+ * ```
+ * val trace = Trace(size)
+ * ```
+ *
+ * Using it to add trace messages:
+ *
+ * ```
+ * trace { "Doing something" }
+ * ```
+ * or you can do multi-append in a garbage-free manner
+ * ```
+ * // Before queue.send(element) invocation
+ * trace.append("Adding element to the queue", element, Thread.currentThread())
+ * ```
+ *
+ * Pass it to `atomic` constructor to automatically trace all modifications of the corresponding field:
+ *
+ * ```
+ * val state = atomic(initialValue, trace)
+ * ```
+ * An optional [named][TraceBase.named] call can be used to name all the messages related to this specific instance:
+ *
+ * ```
+ * val state = atomic(initialValue, trace.named("state"))
+ * ```
+ *
+ * An optional [format] parameter can be specified to add context-specific information to each trace.
+ * The default format is [traceFormatDefault].
+ */
+@Suppress("FunctionName")
+public expect fun Trace(size: Int = 32, format: TraceFormat = traceFormatDefault): TraceBase
+
+/**
+ * Adds a name to the trace. For example:
+ * 
+ * ```
+ * val state = atomic(initialValue, trace.named("state"))
+ * ```
+ */
+public expect fun TraceBase.named(name: String): TraceBase
+
+/**
+ * The default trace string formatter.
+ *
+ * On JVM when `kotlinx.atomicfu.trace.thread` system property is set, then the default format
+ * also includes thread name for each operation.
+ */
+public expect val traceFormatDefault: TraceFormat
+
+/**
+ * Base class for implementations of `Trace`.
+ */
+@JsName(TRACE_BASE_CONSTRUCTOR)
+public open class TraceBase internal constructor() {
+    /**
+     * Accepts the logging [event] and appends it to the trace.
+     */
+    @JsName(TRACE_APPEND_1)
+    public open fun append(event: Any) {}
+
+    /**
+     * Accepts the logging events [event1], [event2] and appends them to the trace.
+     */
+    @JsName(TRACE_APPEND_2)
+    public open fun append(event1: Any, event2: Any) {}
+
+    /**
+     * Accepts the logging events [event1], [event2], [event3] and appends them to the trace.
+     */
+    @JsName(TRACE_APPEND_3)
+    public open fun append(event1: Any, event2: Any, event3: Any) {}
+
+    /**
+     * Accepts the logging events [event1], [event2], [event3], [event4] and appends them to the trace.
+     */
+    @JsName(TRACE_APPEND_4)
+    public open fun append(event1: Any, event2: Any, event3: Any, event4: Any) {}
+
+    /**
+     * Accepts the logging [event] and appends it to the trace.
+     */
+    @InlineOnly
+    public inline operator fun invoke(event: () -> Any) {
+        append(event())
+    }
+
+    /**
+     * NOP tracing.
+     */
+    public object None : TraceBase()
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/TraceFormat.kt b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/TraceFormat.kt
new file mode 100644
index 0000000..dd8d0de
--- /dev/null
+++ b/atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/TraceFormat.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+
+package kotlinx.atomicfu
+
+import kotlin.internal.InlineOnly
+import kotlin.js.JsName
+
+/**
+ * Trace string formatter.
+ */
+@JsName(TRACE_FORMAT_CLASS)
+public open class TraceFormat {
+    /**
+     * Formats trace at the given [index] with the given [event] of Any type.
+     */
+    @JsName(TRACE_FORMAT_FORMAT_FUNCTION)
+    public open fun format(index: Int, event: Any): String = "$index: $event"
+}
+
+/**
+ * Creates trace string formatter with the given [format] code block.
+ */
+@InlineOnly
+public inline fun TraceFormat(crossinline format: (index: Int, event: Any) -> String): TraceFormat =
+    object : TraceFormat() {
+        override fun format(index: Int, event: Any): String = format(index, event)
+    }
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/bytecode_test/NoAccessPrivateTopLevelReflectionTest.kt b/atomicfu/src/commonTest/kotlin/bytecode_test/NoAccessPrivateTopLevelReflectionTest.kt
new file mode 100644
index 0000000..c26a8e0
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/bytecode_test/NoAccessPrivateTopLevelReflectionTest.kt
@@ -0,0 +1,8 @@
+@file:JvmName("NoAccessPrivateTopLevel")
+
+package bytecode_test
+
+import kotlinx.atomicfu.*
+import kotlin.jvm.JvmName
+
+private val a = atomic(1) // no accessors
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/bytecode_test/PackagePrivateTopLevelReflectionTest.kt b/atomicfu/src/commonTest/kotlin/bytecode_test/PackagePrivateTopLevelReflectionTest.kt
new file mode 100644
index 0000000..a0e0eab
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/bytecode_test/PackagePrivateTopLevelReflectionTest.kt
@@ -0,0 +1,8 @@
+@file:JvmName("PackagePrivateTopLevel")
+
+package bytecode_test
+
+import kotlinx.atomicfu.*
+import kotlin.jvm.JvmName
+
+internal val d = atomic(0) // accessed from the same package PublicTopLevel.class
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/bytecode_test/PrivateFieldAccessFromInnerClassReflectonTest.kt b/atomicfu/src/commonTest/kotlin/bytecode_test/PrivateFieldAccessFromInnerClassReflectonTest.kt
new file mode 100644
index 0000000..375d54f
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/bytecode_test/PrivateFieldAccessFromInnerClassReflectonTest.kt
@@ -0,0 +1,13 @@
+package bytecode_test
+
+import kotlinx.atomicfu.atomic
+
+class PrivateFieldAccessFromInnerClassReflectonTest {
+    private val state = atomic(0)
+
+    inner class InnerClass {
+        fun m() {
+            state.compareAndSet(0, 77)
+        }
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/bytecode_test/PrivateTopLevelReflectionTest.kt b/atomicfu/src/commonTest/kotlin/bytecode_test/PrivateTopLevelReflectionTest.kt
new file mode 100644
index 0000000..6cfd4e5
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/bytecode_test/PrivateTopLevelReflectionTest.kt
@@ -0,0 +1,14 @@
+@file:JvmName("PrivateTopLevel")
+
+package bytecode_test
+
+import kotlinx.atomicfu.atomic
+import kotlin.jvm.JvmName
+
+private val b = atomic(2)
+
+class PrivateTopLevelReflectionTest {
+    fun update() {
+        b.compareAndSet(0, 42)
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/bytecode_test/PublicTopLevelReflectionTest.kt b/atomicfu/src/commonTest/kotlin/bytecode_test/PublicTopLevelReflectionTest.kt
new file mode 100644
index 0000000..8732d33
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/bytecode_test/PublicTopLevelReflectionTest.kt
@@ -0,0 +1,14 @@
+@file:JvmName("PublicTopLevel")
+
+package bytecode_test
+
+import kotlinx.atomicfu.*
+import kotlin.jvm.JvmName
+
+internal val c = atomic(0)
+
+class PublicTopLevelReflectionTest {
+    fun update() {
+        d.lazySet(56)
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/bytecode_test/TraceUseTest.kt b/atomicfu/src/commonTest/kotlin/bytecode_test/TraceUseTest.kt
new file mode 100644
index 0000000..adf280d
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/bytecode_test/TraceUseTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package bytecode_test
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class TraceUseTest {
+    val trace = Trace(size = 64, format = TraceFormat { i, t -> "[$i$t]" } )
+    val current = atomic(0, trace.named("current"))
+
+    @Test
+    fun testTraceUse() {
+        assertEquals(0, update(42))
+        assertEquals(42, current.value)
+    }
+
+    fun update(x: Int): Int {
+        // custom trace message
+        trace { "calling update($x)" }
+        // automatic tracing of modification operations
+        return current.getAndAdd(x)
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/internal_test1/B.kt b/atomicfu/src/commonTest/kotlin/internal_test1/B.kt
index df9f8fa..b87f56c 100644
--- a/atomicfu/src/commonTest/kotlin/internal_test1/B.kt
+++ b/atomicfu/src/commonTest/kotlin/internal_test1/B.kt
@@ -4,7 +4,10 @@
 
 package internal_test1
 
+import bytecode_test.c
 import kotlinx.atomicfu.test.A
+import kotlinx.atomicfu.test.internalTopLevelField
+import kotlinx.atomicfu.test.publicTopLevelField
 import kotlin.test.*
 
 class B {
@@ -24,6 +27,15 @@
         check(a.refArr[3].compareAndSet(null, "OK"))
         assertEquals("OK", a.refArr[3].value)
     }
+
+    @Test
+    fun testInternalTopLevel() {
+        internalTopLevelField.lazySet(55)
+        check(internalTopLevelField.value == 55)
+        check(publicTopLevelField.compareAndSet(0, 66))
+        check(publicTopLevelField.value == 66)
+        check(c.getAndSet(145) == 0)
+    }
 }
 
 class D {
diff --git a/atomicfu/src/commonTest/kotlin/internal_test2/InternalTrace.kt b/atomicfu/src/commonTest/kotlin/internal_test2/InternalTrace.kt
new file mode 100644
index 0000000..5878b8c
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/internal_test2/InternalTrace.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package internal_test2
+
+import kotlinx.atomicfu.*
+
+class Updater {
+    internal val internalTrace = Trace(format = TraceFormat { i, text -> "Updater: $i [$text]" })
+    private val t = Trace(20)
+
+    val a1 = atomic(5, internalTrace)
+    private val a2 = atomic(6, t)
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/ArithmeticTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/ArithmeticTest.kt
index cec5c11..e14ef7b 100644
--- a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/ArithmeticTest.kt
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/ArithmeticTest.kt
@@ -6,7 +6,6 @@
 
 import kotlinx.atomicfu.*
 import kotlin.test.*
-import kotlin.math.*
 
 class ArithmeticTest {
     @Test
@@ -66,6 +65,15 @@
         check(a._x.compareAndSet(true, false))
         check(!a.x)
     }
+
+    @Test
+    fun testDeepReference() {
+        val a = DeepReference()
+        check(a.abcderef.value.b.c.d.e.n == 5)
+        val new = ARef(BRef(CRef(DRef(ERef(7)))))
+        a.abcderef.lazySet(new)
+        check(a.abcderef.value.b.c.d.e.n == 7)
+    }
 }
 
 class IntArithmetic {
@@ -85,3 +93,13 @@
     val _x = atomic(false)
     val x get() = _x.value
 }
+
+class DeepReference {
+    val abcderef = atomic(ARef(BRef(CRef(DRef(ERef(5))))))
+}
+
+data class ARef(val b: BRef)
+data class BRef(val c: CRef)
+data class CRef(val d: DRef)
+data class DRef(val e: ERef)
+data class ERef(val n: Int)
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/AtomicArrayTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/AtomicArrayTest.kt
index 0c3ff6c..8e7598c 100644
--- a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/AtomicArrayTest.kt
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/AtomicArrayTest.kt
@@ -11,6 +11,7 @@
     @Test
     fun testIntArray() {
         val A = AtomicArrayClass()
+        check(A.intArr.size == 10)
         check(A.intArr[0].compareAndSet(0, 3))
         check(A.intArr[1].value == 0)
         A.intArr[0].lazySet(5)
@@ -22,11 +23,17 @@
         check(A.intArr[2].value == 2)
         check(A.intArr[2].compareAndSet(2, 34))
         check(A.intArr[2].value == 34)
+        assertEquals(20, A.intArr.size + A.longArr.size)
+        val size = A.intArr.size
+        var sum = 0
+        for (i in 0 until size) { sum += A.intArr[i].value }
+        assertEquals(43, sum)
     }
 
     @Test
     fun testLongArray() {
         val A = AtomicArrayClass()
+        check(A.longArr.size == 10)
         A.longArr[0].value = 2424920024888888848
         check(A.longArr[0].value == 2424920024888888848)
         A.longArr[0].lazySet(8424920024888888848)
@@ -57,6 +64,7 @@
     @Test
     fun testBooleanArray() {
         val A = AtomicArrayClass()
+        check(A.booleanArr.size == 10)
         check(!A.booleanArr[1].value)
         A.booleanArr[1].compareAndSet(false, true)
         A.booleanArr[0].lazySet(true)
@@ -69,8 +77,10 @@
     @Test
     fun testRefArray() {
         val A = AtomicArrayClass()
-        val a2 = ARef(2)
-        val a3 = ARef(3)
+        check(A.refArr.size == 10)
+        check(A.genericArr.size == 10)
+        val a2 = IntBox(2)
+        val a3 = IntBox(3)
         A.refArr[0].value = a2
         check(A.refArr[0].value!!.n == 2)
         check(A.refArr[0].compareAndSet(a2, a3))
@@ -94,6 +104,12 @@
         val ea = ExtendedApiAtomicArrays()
         check(ea.stringAtomicNullArray[0].value == null)
         check(ea.stringAtomicNullArray[0].compareAndSet(null, "aaa"))
+        val totalString = buildString {
+            for (i in 0 until ea.stringAtomicNullArray.size) {
+                ea.stringAtomicNullArray[i].value?.let { append(it) }
+            }
+        }
+        assertEquals("aaa", totalString)
 
         check(ea.genAtomicNullArr[3].value == null)
         val l1 = listOf("a", "bb", "ccc")
@@ -112,11 +128,11 @@
     val intArr = AtomicIntArray(10)
     val longArr = AtomicLongArray(10)
     val booleanArr = AtomicBooleanArray(10)
-    val refArr = AtomicArray<ARef>(10)
+    val refArr = AtomicArray<IntBox>(10)
     val genericArr = AtomicArray<List<List<String>>>(10)
     val mapArr = atomicArrayOfNulls<Map<List<String>, String>>(10)
     val anyArr = atomicArrayOfNulls<Any?>(10)
-    val a = atomic(ARef(8))
+    val a = atomic(IntBox(8))
 }
 
 class ExtendedApiAtomicArrays {
@@ -124,5 +140,5 @@
     val genAtomicNullArr = atomicArrayOfNulls<List<String>>(7)
 }
 
-data class ARef(val n: Int)
+data class IntBox(val n: Int)
 
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/DelegatedPropertiesTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/DelegatedPropertiesTest.kt
new file mode 100644
index 0000000..1b24e6d
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/DelegatedPropertiesTest.kt
@@ -0,0 +1,147 @@
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
+package kotlinx.atomicfu.test
+
+import kotlinx.atomicfu.atomic
+import kotlin.test.*
+
+class DelegatedProperties {
+
+    private val _a = atomic(42)
+    var a: Int by _a
+
+    private val _l = atomic(55555555555)
+    var l: Long by _l
+
+    private val _b = atomic(false)
+    var b: Boolean by _b
+
+    private val _ref = atomic(A(B(77)))
+    var ref: A by _ref
+
+    var vInt by atomic(77)
+
+    var vLong by atomic(777777777)
+
+    var vBoolean by atomic(false)
+
+    var vRef by atomic(A(B(77)))
+
+    @Test
+    fun testDelegatedAtomicInt() {
+        assertEquals(42, a)
+        _a.compareAndSet(42, 56)
+        assertEquals(56, a)
+        a = 77
+        _a.compareAndSet(77,  66)
+        assertEquals(66, _a.value)
+        assertEquals(66, a)
+    }
+
+    @Test
+    fun testDelegatedAtomicLong() {
+        assertEquals(55555555555, l)
+        _l.getAndIncrement()
+        assertEquals(55555555556, l)
+        l = 7777777777777
+        assertTrue(_l.compareAndSet(7777777777777, 66666666666))
+        assertEquals(66666666666, _l.value)
+        assertEquals(66666666666, l)
+    }
+
+    @Test
+    fun testDelegatedAtomicBoolean() {
+        assertEquals(false, b)
+        _b.lazySet(true)
+        assertEquals(true, b)
+        b = false
+        assertTrue(_b.compareAndSet(false, true))
+        assertEquals(true, _b.value)
+        assertEquals(true, b)
+    }
+
+    @Test
+    fun testDelegatedAtomicRef() {
+        assertEquals(77, ref.b.n)
+        _ref.lazySet(A(B(66)))
+        assertEquals(66, ref.b.n)
+        assertTrue(_ref.compareAndSet(_ref.value, A(B(56))))
+        assertEquals(56, ref.b.n)
+        ref = A(B(99))
+        assertEquals(99, _ref.value.b.n)
+    }
+
+    @Test
+    fun testVolatileInt() {
+        assertEquals(77, vInt)
+        vInt = 55
+        assertEquals(110, vInt * 2)
+    }
+
+    @Test
+    fun testVolatileLong() {
+        assertEquals(777777777, vLong)
+        vLong = 55
+        assertEquals(55, vLong)
+    }
+
+    @Test
+    fun testVolatileBoolean() {
+        assertEquals(false, vBoolean)
+        vBoolean = true
+        assertEquals(true, vBoolean)
+    }
+
+    @Test
+    fun testVolatileRef() {
+        assertEquals(77, vRef.b.n)
+        vRef = A(B(99))
+        assertEquals(99, vRef.b.n)
+    }
+
+    class A (val b: B)
+    class B (val n: Int)
+}
+
+class ExposedDelegatedPropertiesAccessorsTest {
+
+    private inner class A {
+        private val _node = atomic<Node?>(null)
+        var node: Node? by _node
+
+        fun cas(expect: Node, update: Node) = _node.compareAndSet(expect, update)
+    }
+
+    private class Node(val n: Int)
+
+    @Test
+    fun testDelegatedPropertiesAccessors() {
+        val a = A()
+        val update = Node(5)
+        a.node = update
+        assertTrue(a.cas(update, Node(6)))
+        assertEquals(6, a.node?.n)
+    }
+
+    @Test
+    fun testAccessors() {
+        val cl = DelegatedProperties()
+        assertEquals(42, cl.a)
+        cl.a = 66
+        assertEquals(66, cl.a)
+        assertEquals(55555555555, cl.l)
+        cl.l = 66666666
+        assertEquals(66666666, cl.l)
+        assertEquals(false, cl.b)
+        cl.b = true
+        assertEquals(true, cl.b)
+    }
+
+    @Test
+    fun testVolatileProperties() {
+        val cl = DelegatedProperties()
+        assertEquals(77, cl.vInt)
+        cl.vInt = 99
+        assertEquals(99, cl.vInt)
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/JSScopesTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/JSScopesTest.kt
index 25291cb..fda32fe 100644
--- a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/JSScopesTest.kt
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/JSScopesTest.kt
@@ -23,7 +23,6 @@
 }
 
 class B (val value: Int)
-
 class C (val d: D)
 class D (val e: E)
 class E (val x: Int)
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/NestedAtomicFieldLoads.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/NestedAtomicFieldLoads.kt
new file mode 100644
index 0000000..3c26781
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/NestedAtomicFieldLoads.kt
@@ -0,0 +1,42 @@
+package kotlinx.atomicfu.test
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class NestedAtomicFieldLoads {
+    private val a = atomic(0)
+    private val b = atomic(1)
+    private val c = atomic(2)
+    private val ref = atomic(A(B(70)))
+
+    private val flag = atomic(false)
+
+    private val arr = AtomicIntArray(7)
+
+    private fun foo(arg1: Int, arg2: Int, arg3: Int, arg4: Int): Int = arg1 + arg2 + arg3 + arg4
+
+    private class A(val b: B)
+    private class B(val n: Int)
+
+    @Test
+    fun testNestedGetField() {
+        a.value = b.value + c.value
+        assertEquals(3, a.value)
+        a.value = foo(a.value, b.value, c.value, ref.value.b.n)
+        assertEquals(76, a.value)
+    }
+
+    @Test
+    fun testNestedAtomicInvocations() {
+        flag.value = a.compareAndSet(0, 56)
+        assertTrue(flag.value)
+    }
+
+    @Test
+    fun testArrayNestedLoads() {
+        arr[5].value = b.value
+        assertEquals(1, arr[5].value)
+        arr[0].value = foo(arr[5].value, b.value, c.value, ref.value.b.n)
+        assertEquals(74, arr[0].value)
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/NestedInlineFunctionsTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/NestedInlineFunctionsTest.kt
new file mode 100644
index 0000000..4a2c76d
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/NestedInlineFunctionsTest.kt
@@ -0,0 +1,21 @@
+package kotlinx.atomicfu.test
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+// Ensures that inline function receivers are resolved correctly by the JS transformer
+class NestedInlineFunctionsTest {
+    val _a = atomic(5)
+    val _b = atomic(42)
+
+    @Test
+    fun testNestedInlineFunctions() {
+        _a.loop { a ->
+            _b.loop { b ->
+                assertEquals(5, a)
+                assertEquals(42, b)
+                return
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/SetArrayElementTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/SetArrayElementTest.kt
index 8527dfb..59f3b71 100644
--- a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/SetArrayElementTest.kt
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/SetArrayElementTest.kt
@@ -9,17 +9,17 @@
 
 class SetArrayElementTest {
     @Test
-    fun getArrayFieldTest() {
+    fun testGetArrayField() {
         val aes = ArrayElementSetters()
         assertTrue(aes.setInt(2, 5))
         assertFalse(aes.setInt(2, 10))
         assertTrue(aes.setBoolean(1, true))
-        assertTrue(aes.setRef(1, ARef(29472395)))
-        assertFalse(aes.setRef(1, ARef(81397)))
+        assertTrue(aes.setRef(1, IntBox(29472395)))
+        assertFalse(aes.setRef(1, IntBox(81397)))
     }
 
     @Test
-    fun transformInMethodTest() {
+    fun testTransformInMethod() {
         val holder = AtomicArrayWithMethod()
         holder.set("Hello", 0)
     }
@@ -28,11 +28,11 @@
 class ArrayElementSetters {
     private val intArr = AtomicIntArray(3)
     private val booleanArr = AtomicBooleanArray(4)
-    private val refArr = atomicArrayOfNulls<ARef>(5)
+    private val refArr = atomicArrayOfNulls<IntBox>(5)
 
     fun setInt(index: Int, data: Int) = intArr[index].compareAndSet(0, data)
     fun setBoolean(index: Int, data: Boolean) = booleanArr[index].compareAndSet(false, data)
-    fun setRef(index: Int, data: ARef) = refArr[index].compareAndSet(null, data)
+    fun setRef(index: Int, data: IntBox) = refArr[index].compareAndSet(null, data)
 }
 
 class AtomicArrayWithMethod {
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TopLevelStoredToLocalVariableTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TopLevelStoredToLocalVariableTest.kt
new file mode 100644
index 0000000..6111ee4
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TopLevelStoredToLocalVariableTest.kt
@@ -0,0 +1,51 @@
+package kotlinx.atomicfu.test
+
+import kotlinx.atomicfu.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+private val top = atomic(0)
+private val topArr = AtomicIntArray(5)
+
+class TopLevelStoredToLocalVariableTest {
+
+    @Test
+    fun testTopLevelArrayElementUpdate() {
+        topArr[3].update { 55 }
+        assertEquals(55, topArr[3].getAndUpdate { 66 })
+        assertEquals(77, topArr[3].updateAndGet { 77 })
+        topArr[3].loop {  value ->
+            if (value == 77) topArr[3].compareAndSet(value, 66)
+            assertEquals(66, topArr[3].value)
+            return
+        }
+    }
+
+    @Test
+    fun testTopLevelUpdate() {
+        top.update { 5 }
+        assertEquals(5, top.getAndUpdate { 66 })
+        assertEquals(77, top.updateAndGet { 77 })
+        top.loop { value ->
+            assertEquals(77, value)
+            if (value == 77) top.compareAndSet(value, 66)
+            assertEquals(66, top.value)
+            return
+        }
+    }
+
+    @Test
+    fun testObjectFieldUpdate() {
+        Example.update()
+        assertEquals("test !", Example.x)
+    }
+}
+
+object Example {
+    private val _x = atomic("test")
+    val x get() = _x.value
+
+    fun update() {
+        _x.getAndUpdate { "$it !" }
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TopLevelTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TopLevelTest.kt
index 595bcd9..fcbe857 100644
--- a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TopLevelTest.kt
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TopLevelTest.kt
@@ -6,6 +6,7 @@
 
 import kotlinx.atomicfu.*
 import kotlin.test.Test
+import kotlin.test.assertEquals
 
 private val a = atomic(0)
 private val b = atomic(2424920024888888848)
@@ -21,13 +22,18 @@
 
 private val stringAtomicNullArr = atomicArrayOfNulls<String>(10)
 
+internal val internalTopLevelField = atomic(0)
+public val publicTopLevelField = atomic(0)
+
 class TopLevelPrimitiveTest {
     @Test
     fun testTopLevelInt() {
         check(a.value == 0)
         check(a.getAndSet(3) == 0)
         check(a.compareAndSet(3, 8))
-        a.lazySet(1)
+        a.lazySet(5)
+        check(a.value == 5)
+        a.value = 1
         check(a.value == 1)
         check(a.getAndSet(2) == 1)
         check(a.value == 2)
@@ -51,7 +57,9 @@
         check(b.value == 2424920024888888848)
         b.lazySet(8424920024888888848)
         check(b.value == 8424920024888888848)
-        check(b.getAndSet(8924920024888888848) == 8424920024888888848)
+        b.value = 8424920024888888833
+        check(b.value == 8424920024888888833)
+        check(b.getAndSet(8924920024888888848) == 8424920024888888833)
         check(b.value == 8924920024888888848)
         check(b.incrementAndGet() == 8924920024888888849)
         check(b.value == 8924920024888888849)
@@ -72,6 +80,8 @@
         check(c.value)
         c.lazySet(false)
         check(!c.value)
+        c.value = false
+        check(!c.value)
         check(!c.getAndSet(true))
         check(c.compareAndSet(true, false))
         check(!c.value)
@@ -86,14 +96,8 @@
         val l = IntArray(4){i -> i}
         any.lazySet(l)
         check((any.value as IntArray)[2] == 2)
-    }
-
-    @Test
-    fun testTopLevelArrayOfNulls() {
-        check(stringAtomicNullArr[0].value == null)
-        check(stringAtomicNullArr[0].compareAndSet(null, "aa"))
-        stringAtomicNullArr[1].lazySet("aa")
-        check(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value)
+        abcNode.value = ANode(BNode(CNode(88)))
+        check(abcNode.value.b.c.d == 88)
     }
 }
 
@@ -111,6 +115,7 @@
         check(intArr[2].value == 2)
         check(intArr[2].compareAndSet(2, 34))
         check(intArr[2].value == 34)
+        assertEquals(8, intArr.size + longArr.size)
     }
 
     @Test
@@ -150,6 +155,13 @@
         check(!booleanArr[2].getAndSet(true))
         check(booleanArr[0].value && booleanArr[1].value && booleanArr[2].value)
     }
+    @Test
+    fun testTopLevelArrayOfNulls() {
+        check(stringAtomicNullArr[0].value == null)
+        check(stringAtomicNullArr[0].compareAndSet(null, "aa"))
+        stringAtomicNullArr[1].lazySet("aa")
+        check(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value)
+    }
 
     @Suppress("UNCHECKED_CAST")
     @Test
@@ -172,7 +184,6 @@
         check(anyRefArr[5].compareAndSet(l, a2))
         check((anyRefArr[5].value as ANode<BNode<CNode>>).b.c.d == 2)
     }
-
 }
 
 data class ANode<T>(val b: T)
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TraceTestCommon.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TraceTestCommon.kt
new file mode 100644
index 0000000..58090d7
--- /dev/null
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/TraceTestCommon.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.atomicfu.test
+
+import internal_test2.Updater
+import kotlinx.atomicfu.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class CounterWithDefaultTrace {
+    private val a = atomic(0)
+
+    private val defaultTrace = Trace()
+    private val a1 = atomic(5, defaultTrace)
+
+    fun inc(): Int {
+        val x = a.incrementAndGet()
+        return x
+    }
+
+    fun multTen(): Boolean {
+        val oldValue = a1.value
+        defaultTrace { "current value = $oldValue" }
+        return a1.compareAndSet(oldValue, oldValue * 10)
+    }
+
+    internal fun getA() = a.value
+    internal fun getA1() = a1.value
+}
+
+class CounterWithCustomSizeTrace {
+    private val t = Trace(30)
+    private val a = atomic(0, t)
+
+    fun dec(): Int {
+        t { "current value = ${a.value}" }
+        return a.getAndDecrement()
+    }
+    internal fun get() = a.value
+}
+
+class CounterWithCustomSizeAndFuncTrace {
+    private val t = Trace(30, TraceFormat { id, text -> "$id: $text"})
+    private val a = atomic(0)
+
+    fun dec(): Int {
+        t { "current value = ${a.value}" }
+        return a.getAndDecrement()
+    }
+    internal fun get() = a.value
+}
+
+class CounterWithInternalTrace {
+    private val u = Updater()
+    private val a = atomic(0, u.internalTrace)
+
+    fun update() {
+        val oldValue = a.value
+        u.internalTrace { "old value = $oldValue" }
+        a.compareAndSet(oldValue, oldValue + 5)
+    }
+    internal fun get() = a.value
+}
+
+class InternalTraceTest {
+    @Test
+    fun testInternalTrace() {
+        val cit = CounterWithInternalTrace()
+        repeat(5) { cit.update() }
+        assertEquals(cit.get(), 25)
+    }
+}
+
+class MyCounter {
+    @Test
+    fun testEasy() {
+        val c = CounterWithDefaultTrace()
+        assertEquals(0, c.getA())
+        c.inc()
+        assertEquals(1, c.getA())
+        c.multTen()
+        assertEquals(c.getA1(), 50)
+    }
+
+    @Test
+    fun testCustomSizeTrace() {
+        val n = 1000
+        val c = CounterWithCustomSizeTrace()
+        repeat(n) {
+            c.dec()
+        }
+        assertEquals(-n, c.get())
+    }
+
+    @Test
+    fun testCustomSizeAndFuncTrace() {
+        val n = 1000
+        val c = CounterWithCustomSizeAndFuncTrace()
+        repeat(n) {
+            c.dec()
+        }
+        assertEquals(-n, c.get())
+    }
+}
+
+class TraceAppendOverridesTest {
+    private val aTrace = Trace(format = TraceFormat { i, text -> "[$i: $text]" })
+    private val a = atomic(0, aTrace)
+
+    private enum class Status { START, END }
+
+    fun inc(n: Int, i: Int) {
+        aTrace.append(i, n, Status.START)
+        val res = a.getAndAdd(n)
+        aTrace.append(i, n, res, Status.END)
+    }
+
+    @Test
+    fun testTraceAppendOverrides() {
+        val n = 1000
+        repeat(n) {
+            inc(1, it)
+        }
+        assertEquals(n, a.value)
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/VolatileOnlyTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/VolatileOnlyTest.kt
index ceedd0e..5566a6c 100644
--- a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/VolatileOnlyTest.kt
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/VolatileOnlyTest.kt
@@ -17,4 +17,4 @@
         _int.value = 42
         assertEquals(42, _int.value)
     }
-}   
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
index 915c157..da1e7b6 100644
--- a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
+++ b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
@@ -6,34 +6,53 @@
 
 package kotlinx.atomicfu
 
-@JsName("atomic\$ref\$")
-public actual fun <T> atomic(initial: T): AtomicRef<T> = AtomicRef<T>(initial)
+import kotlin.reflect.KProperty
+import kotlinx.atomicfu.TraceBase.None
 
-@JsName("atomic\$int\$")
-public actual fun atomic(initial: Int): AtomicInt = AtomicInt(initial)
+@JsName(ATOMIC_REF_FACTORY)
+public actual fun <T> atomic(initial: T, trace: TraceBase): AtomicRef<T> = AtomicRef<T>(initial)
 
-@JsName("atomic\$long\$")
-public actual fun atomic(initial: Long): AtomicLong = AtomicLong(initial)
+@JsName(ATOMIC_REF_FACTORY_BINARY_COMPATIBILITY)
+public actual fun <T> atomic(initial: T): AtomicRef<T> = atomic(initial, None)
 
-@JsName("atomic\$boolean\$")
-public actual fun atomic(initial: Boolean): AtomicBoolean = AtomicBoolean(initial)
+@JsName(ATOMIC_INT_FACTORY)
+public actual fun atomic(initial: Int, trace: TraceBase): AtomicInt = AtomicInt(initial)
+
+@JsName(ATOMIC_INT_FACTORY_BINARY_COMPATIBILITY)
+public actual fun atomic(initial: Int): AtomicInt = atomic(initial, None)
+
+@JsName(ATOMIC_LONG_FACTORY)
+public actual fun atomic(initial: Long, trace: TraceBase): AtomicLong = AtomicLong(initial)
+
+@JsName(ATOMIC_LONG_FACTORY_BINARY_COMPATIBILITY)
+public actual fun atomic(initial: Long): AtomicLong = atomic(initial, None)
+
+@JsName(ATOMIC_BOOLEAN_FACTORY)
+public actual fun atomic(initial: Boolean, trace: TraceBase): AtomicBoolean = AtomicBoolean(initial)
+
+@JsName(ATOMIC_BOOLEAN_FACTORY_BINARY_COMPATIBILITY)
+public actual fun atomic(initial: Boolean): AtomicBoolean = atomic(initial, None)
 
 // ==================================== AtomicRef ====================================
 
 public actual class AtomicRef<T> internal constructor(value: T) {
-    @JsName("kotlinx\$atomicfu\$value")
+    @JsName(ATOMIC_VALUE)
     public actual var value: T = value
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { this.value = value }
+
     public actual inline fun lazySet(value: T) { this.value = value }
 
-    @JsName("compareAndSet\$atomicfu")
+    @JsName(COMPARE_AND_SET)
     public actual fun compareAndSet(expect: T, update: T): Boolean {
         if (value !== expect) return false
         value = update
         return true
     }
 
-    @JsName("getAndSet\$atomicfu")
+    @JsName(GET_AND_SET)
     public actual fun getAndSet(value: T): T {
         val oldValue = this.value
         this.value = value
@@ -46,21 +65,25 @@
 // ==================================== AtomicBoolean ====================================
 
 public actual class AtomicBoolean internal constructor(value: Boolean) {
-    @JsName("kotlinx\$atomicfu\$value")
+    @JsName(ATOMIC_VALUE)
     public actual var value: Boolean = value
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { this.value = value }
+
     public actual inline fun lazySet(value: Boolean) {
         this.value = value
     }
 
-    @JsName("compareAndSet\$atomicfu")
+    @JsName(COMPARE_AND_SET)
     public actual fun compareAndSet(expect: Boolean, update: Boolean): Boolean {
         if (value != expect) return false
         value = update
         return true
     }
 
-    @JsName("getAndSet\$atomicfu")
+    @JsName(GET_AND_SET)
     public actual fun getAndSet(value: Boolean): Boolean {
         val oldValue = this.value
         this.value = value
@@ -73,48 +96,52 @@
 // ==================================== AtomicInt ====================================
 
 public actual class AtomicInt internal constructor(value: Int) {
-    @JsName("kotlinx\$atomicfu\$value")
+    @JsName(ATOMIC_VALUE)
     public actual var value: Int = value
 
+    actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { this.value = value }
+
     public actual inline fun lazySet(value: Int) { this.value = value }
 
-    @JsName("compareAndSet\$atomicfu")
+    @JsName(COMPARE_AND_SET)
     public actual fun compareAndSet(expect: Int, update: Int): Boolean {
         if (value != expect) return false
         value = update
         return true
     }
 
-    @JsName("getAndSet\$atomicfu")
+    @JsName(GET_AND_SET)
     public actual fun getAndSet(value: Int): Int {
         val oldValue = this.value
         this.value = value
         return oldValue
     }
 
-    @JsName("getAndIncrement\$atomicfu")
+    @JsName(GET_AND_INCREMENT)
     public actual fun getAndIncrement(): Int = value++
 
-    @JsName("getAndDecrement\$atomicfu")
+    @JsName(GET_AND_DECREMENT)
     public actual fun getAndDecrement(): Int = value--
 
-    @JsName("getAndAdd\$atomicfu")
+    @JsName(GET_AND_ADD)
     public actual fun getAndAdd(delta: Int): Int {
         val oldValue = value
         value += delta
         return oldValue
     }
 
-    @JsName("addAndGet\$atomicfu")
+    @JsName(ADD_AND_GET)
     public actual fun addAndGet(delta: Int): Int {
         value += delta
         return value
     }
 
-    @JsName("incrementAndGet\$atomicfu")
+    @JsName(INCREMENT_AND_GET)
     public actual fun incrementAndGet(): Int = ++value
 
-    @JsName("decrementAndGet\$atomicfu")
+    @JsName(DECREMENT_AND_GET)
     public actual fun decrementAndGet(): Int = --value
 
     public actual inline operator fun plusAssign(delta: Int) { getAndAdd(delta) }
@@ -127,48 +154,52 @@
 // ==================================== AtomicLong ====================================
 
 public actual class AtomicLong internal constructor(value: Long) {
-    @JsName("kotlinx\$atomicfu\$value")
+    @JsName(ATOMIC_VALUE)
     public actual var value: Long = value
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Long = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) { this.value = value }
+
     public actual inline fun lazySet(value: Long) { this.value = value }
 
-    @JsName("compareAndSet\$atomicfu")
+    @JsName(COMPARE_AND_SET)
     public actual fun compareAndSet(expect: Long, update: Long): Boolean {
         if (value != expect) return false
         value = update
         return true
     }
 
-    @JsName("getAndSet\$atomicfu")
+    @JsName(GET_AND_SET)
     public actual fun getAndSet(value: Long): Long {
         val oldValue = this.value
         this.value = value
         return oldValue
     }
 
-    @JsName("getAndIncrement\$atomicfu\$long")
+    @JsName(GET_AND_INCREMENT_LONG)
     public actual fun getAndIncrement(): Long = value++
 
-    @JsName("getAndDecrement\$atomicfu\$long")
+    @JsName(GET_AND_DECREMENT_LONG)
     public actual fun getAndDecrement(): Long = value--
 
-    @JsName("getAndAdd\$atomicfu\$long")
+    @JsName(GET_AND_ADD_LONG)
     public actual fun getAndAdd(delta: Long): Long {
         val oldValue = value
         value += delta
         return oldValue
     }
 
-    @JsName("addAndGet\$atomicfu\$long")
+    @JsName(ADD_AND_GET_LONG)
     public actual fun addAndGet(delta: Long): Long {
         value += delta
         return value
     }
 
-    @JsName("incrementAndGet\$atomicfu\$long")
+    @JsName(INCREMENT_AND_GET_LONG)
     public actual fun incrementAndGet(): Long = ++value
 
-    @JsName("decrementAndGet\$atomicfu\$long")
+    @JsName(DECREMENT_AND_GET_LONG)
     public actual fun decrementAndGet(): Long = --value
 
     public actual inline operator fun plusAssign(delta: Long) { getAndAdd(delta) }
@@ -176,4 +207,4 @@
     public actual inline operator fun minusAssign(delta: Long) { getAndAdd(-delta) }
 
     override fun toString(): String = value.toString()
-}
+}
\ No newline at end of file
diff --git a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/Trace.kt b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/Trace.kt
new file mode 100644
index 0000000..03a4338
--- /dev/null
+++ b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/Trace.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.atomicfu
+
+@Suppress("FunctionName")
+@JsName(TRACE_FACTORY_FUNCTION)
+public actual fun Trace(size: Int, format: TraceFormat): TraceBase = TraceBase.None
+
+@JsName(TRACE_NAMED)
+public actual fun TraceBase.named(name: String): TraceBase = TraceBase.None
+
+public actual val traceFormatDefault: TraceFormat = TraceFormat()
\ No newline at end of file
diff --git a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt
index 38539bd..7d8c450 100644
--- a/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt
+++ b/atomicfu/src/jsMain/kotlin/kotlinx/atomicfu/locks/Synchronized.kt
@@ -1,8 +1,10 @@
 package kotlinx.atomicfu.locks
 
+import kotlinx.atomicfu.REENTRANT_LOCK
+
 public actual typealias SynchronizedObject = Any
 
-@JsName("reentrantLock\$atomicfu")
+@JsName(REENTRANT_LOCK)
 public val Lock = ReentrantLock()
 
 @Suppress("NOTHING_TO_INLINE")
diff --git a/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/AtomicFU.kt b/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
index b170bb6..20dc5f2 100644
--- a/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
+++ b/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 @file:JvmName("AtomicFU")
@@ -10,6 +10,8 @@
 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater
 import java.util.concurrent.atomic.AtomicLongFieldUpdater
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
+import kotlin.reflect.KProperty
+import kotlinx.atomicfu.TraceBase.None
 
 /**
  * Creates atomic reference with a given [initial] value.
@@ -20,7 +22,9 @@
  * private val f = atomic<Type>(initial)
  * ```
  */
-public actual fun <T> atomic(initial: T): AtomicRef<T> = AtomicRef<T>(initial)
+public actual fun <T> atomic(initial: T, trace: TraceBase): AtomicRef<T> = AtomicRef<T>(initial, trace)
+
+public actual fun <T> atomic(initial: T): AtomicRef<T> = atomic(initial, None)
 
 /**
  * Creates atomic [Int] with a given [initial] value.
@@ -31,7 +35,9 @@
  * private val f = atomic(initialInt)
  * ```
  */
-public actual fun atomic(initial: Int): AtomicInt = AtomicInt(initial)
+public actual fun atomic(initial: Int, trace: TraceBase): AtomicInt = AtomicInt(initial, trace)
+
+public actual fun atomic(initial: Int): AtomicInt = atomic(initial, None)
 
 /**
  * Creates atomic [Long] with a given [initial] value.
@@ -42,7 +48,9 @@
  * private val f = atomic(initialLong)
  * ```
  */
-public actual fun atomic(initial: Long): AtomicLong = AtomicLong(initial)
+public actual fun atomic(initial: Long, trace: TraceBase): AtomicLong = AtomicLong(initial, trace)
+
+public actual fun atomic(initial: Long): AtomicLong = atomic(initial, None)
 
 /**
  * Creates atomic [Boolean] with a given [initial] value.
@@ -53,7 +61,9 @@
  * private val f = atomic(initialBoolean)
  * ```
  */
-public actual fun atomic(initial: Boolean): AtomicBoolean = AtomicBoolean(initial)
+public actual fun atomic(initial: Boolean, trace: TraceBase): AtomicBoolean = AtomicBoolean(initial, trace)
+
+public actual fun atomic(initial: Boolean): AtomicBoolean = atomic(initial, None)
 
 // ==================================== AtomicRef ====================================
 
@@ -63,7 +73,7 @@
  * like [compareAndSet] and others.
  */
 @Suppress("UNCHECKED_CAST")
-public actual class AtomicRef<T> internal constructor(value: T) {
+public actual class AtomicRef<T> internal constructor(value: T, val trace: TraceBase) {
     /**
      * Reading/writing this property maps to read/write of volatile variable.
      */
@@ -72,15 +82,21 @@
         set(value) {
             interceptor.beforeUpdate(this)
             field = value
+            if (trace !== TraceBase.None) trace { "set($value)" }
             interceptor.afterSet(this, value)
         }
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { this.value = value }
+
     /**
      * Maps to [AtomicReferenceFieldUpdater.lazySet].
      */
     public actual fun lazySet(value: T) {
         interceptor.beforeUpdate(this)
         FU.lazySet(this, value)
+        if (trace !== TraceBase.None) trace { "lazySet($value)" }
         interceptor.afterSet(this, value)
     }
 
@@ -90,7 +106,10 @@
     public actual fun compareAndSet(expect: T, update: T): Boolean {
         interceptor.beforeUpdate(this)
         val result = FU.compareAndSet(this, expect, update)
-        if (result) interceptor.afterRMW(this, expect, update)
+        if (result) {
+            if (trace !== TraceBase.None) trace { "CAS($expect, $update)" }
+            interceptor.afterRMW(this, expect, update)
+        }
         return result
     }
 
@@ -100,6 +119,7 @@
     public actual fun getAndSet(value: T): T {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndSet(this, value) as T
+        if (trace !== TraceBase.None) trace { "getAndSet($value):$oldValue" }
         interceptor.afterRMW(this, oldValue, value)
         return oldValue
     }
@@ -120,11 +140,15 @@
  * like [compareAndSet] and others.
  */
 @Suppress("UNCHECKED_CAST")
-public actual class AtomicBoolean internal constructor(v: Boolean) {
+public actual class AtomicBoolean internal constructor(v: Boolean, val trace: TraceBase) {
 
     @Volatile
     private var _value: Int = if (v) 1 else 0
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { this.value = value }
+
     /**
      * Reading/writing this property maps to read/write of volatile variable.
      */
@@ -133,6 +157,7 @@
         set(value) {
             interceptor.beforeUpdate(this)
             _value = if (value) 1 else 0
+            if (trace !== TraceBase.None) trace { "set($value)" }
             interceptor.afterSet(this, value)
         }
 
@@ -143,6 +168,7 @@
         interceptor.beforeUpdate(this)
         val v = if (value) 1 else 0
         FU.lazySet(this, v)
+        if (trace !== TraceBase.None) trace { "lazySet($value)" }
         interceptor.afterSet(this, value)
     }
 
@@ -154,7 +180,10 @@
         val e = if (expect) 1 else 0
         val u = if (update) 1 else 0
         val result = FU.compareAndSet(this, e, u)
-        if (result) interceptor.afterRMW(this, expect, update)
+        if (result) {
+            if (trace !== TraceBase.None) trace { "CAS($expect, $update)" }
+            interceptor.afterRMW(this, expect, update)
+        }
         return result
     }
 
@@ -165,6 +194,7 @@
         interceptor.beforeUpdate(this)
         val v = if (value) 1 else 0
         val oldValue = FU.getAndSet(this, v)
+        if (trace !== TraceBase.None) trace { "getAndSet($value):$oldValue" }
         interceptor.afterRMW(this, (oldValue == 1), value)
         return oldValue == 1
     }
@@ -183,7 +213,7 @@
  * [value] property and various atomic read-modify-write operations
  * like [compareAndSet] and others.
  */
-public actual class AtomicInt internal constructor(value: Int) {
+public actual class AtomicInt internal constructor(value: Int, val trace: TraceBase) {
     /**
      * Reads/writes of this property maps to read/write of volatile variable.
      */
@@ -192,15 +222,21 @@
         set(value) {
             interceptor.beforeUpdate(this)
             field = value
+            if (trace !== TraceBase.None) trace { "set($value)" }
             interceptor.afterSet(this, value)
         }
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { this.value = value }
+
     /**
      * Maps to [AtomicIntegerFieldUpdater.lazySet].
      */
     public actual fun lazySet(value: Int) {
         interceptor.beforeUpdate(this)
         FU.lazySet(this, value)
+        if (trace !== TraceBase.None) trace { "lazySet($value)" }
         interceptor.afterSet(this, value)
     }
 
@@ -210,7 +246,10 @@
     public actual fun compareAndSet(expect: Int, update: Int): Boolean {
         interceptor.beforeUpdate(this)
         val result = FU.compareAndSet(this, expect, update)
-        if (result) interceptor.afterRMW(this, expect, update)
+        if (result) {
+            if (trace !== TraceBase.None) trace { "CAS($expect, $update)" }
+            interceptor.afterRMW(this, expect, update)
+        }
         return result
     }
 
@@ -220,6 +259,7 @@
     public actual fun getAndSet(value: Int): Int {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndSet(this, value)
+        if (trace !== TraceBase.None) trace { "getAndSet($value):$oldValue" }
         interceptor.afterRMW(this, oldValue, value)
         return oldValue
     }
@@ -230,6 +270,7 @@
     public actual fun getAndIncrement(): Int {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndIncrement(this)
+        if (trace !== TraceBase.None) trace { "getAndInc():$oldValue" }
         interceptor.afterRMW(this, oldValue, oldValue + 1)
         return oldValue
     }
@@ -240,6 +281,7 @@
     public actual fun getAndDecrement(): Int {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndDecrement(this)
+        if (trace !== TraceBase.None) trace { "getAndDec():$oldValue" }
         interceptor.afterRMW(this, oldValue, oldValue - 1)
         return oldValue
     }
@@ -250,6 +292,7 @@
     public actual fun getAndAdd(delta: Int): Int {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndAdd(this, delta)
+        if (trace !== TraceBase.None) trace { "getAndAdd($delta):$oldValue" }
         interceptor.afterRMW(this, oldValue, oldValue + delta)
         return oldValue
     }
@@ -260,6 +303,7 @@
     public actual fun addAndGet(delta: Int): Int {
         interceptor.beforeUpdate(this)
         val newValue = FU.addAndGet(this, delta)
+        if (trace !== TraceBase.None) trace { "addAndGet($delta):$newValue" }
         interceptor.afterRMW(this, newValue - delta, newValue)
         return newValue
     }
@@ -270,6 +314,7 @@
     public actual fun incrementAndGet(): Int {
         interceptor.beforeUpdate(this)
         val newValue = FU.incrementAndGet(this)
+        if (trace !== TraceBase.None) trace { "incAndGet():$newValue" }
         interceptor.afterRMW(this, newValue - 1, newValue)
         return newValue
     }
@@ -280,6 +325,7 @@
     public actual fun decrementAndGet(): Int {
         interceptor.beforeUpdate(this)
         val newValue = FU.decrementAndGet(this)
+        if (trace !== TraceBase.None) trace { "decAndGet():$newValue" }
         interceptor.afterRMW(this, newValue + 1, newValue)
         return newValue
     }
@@ -312,7 +358,7 @@
  * [value] property and various atomic read-modify-write operations
  * like [compareAndSet] and others.
  */
-public actual class AtomicLong internal constructor(value: Long) {
+public actual class AtomicLong internal constructor(value: Long, val trace: TraceBase) {
     /**
      * Reads/writes of this property maps to read/write of volatile variable.
      */
@@ -321,15 +367,21 @@
         set(value) {
             interceptor.beforeUpdate(this)
             field = value
+            if (trace !== TraceBase.None) trace { "set($value)" }
             interceptor.afterSet(this, value)
         }
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Long = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) { this.value = value }
+
     /**
      * Maps to [AtomicLongFieldUpdater.lazySet].
      */
     public actual fun lazySet(value: Long) {
         interceptor.beforeUpdate(this)
         FU.lazySet(this, value)
+        if (trace !== TraceBase.None) trace { "lazySet($value)" }
         interceptor.afterSet(this, value)
     }
 
@@ -339,7 +391,10 @@
     public actual fun compareAndSet(expect: Long, update: Long): Boolean {
         interceptor.beforeUpdate(this)
         val result = FU.compareAndSet(this, expect, update)
-        if (result) interceptor.afterRMW(this, expect, update)
+        if (result) {
+            if (trace !== TraceBase.None) trace { "CAS($expect, $update)" }
+            interceptor.afterRMW(this, expect, update)
+        }
         return result
     }
 
@@ -349,6 +404,7 @@
     public actual fun getAndSet(value: Long): Long {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndSet(this, value)
+        if (trace !== TraceBase.None) trace { "getAndSet($value):$oldValue" }
         interceptor.afterRMW(this, oldValue, value)
         return oldValue
     }
@@ -359,6 +415,7 @@
     public actual fun getAndIncrement(): Long {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndIncrement(this)
+        if (trace !== TraceBase.None) trace { "getAndInc():$oldValue" }
         interceptor.afterRMW(this, oldValue, oldValue + 1)
         return oldValue
     }
@@ -369,6 +426,7 @@
     public actual fun getAndDecrement(): Long {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndDecrement(this)
+        if (trace !== TraceBase.None) trace { "getAndDec():$oldValue" }
         interceptor.afterRMW(this, oldValue, oldValue - 1)
         return oldValue
     }
@@ -379,6 +437,7 @@
     public actual fun getAndAdd(delta: Long): Long {
         interceptor.beforeUpdate(this)
         val oldValue = FU.getAndAdd(this, delta)
+        if (trace !== TraceBase.None) trace { "getAndAdd($delta):$oldValue" }
         interceptor.afterRMW(this, oldValue, oldValue + delta)
         return oldValue
     }
@@ -389,6 +448,7 @@
     public actual fun addAndGet(delta: Long): Long {
         interceptor.beforeUpdate(this)
         val newValue = FU.addAndGet(this, delta)
+        if (trace !== TraceBase.None) trace { "addAndGet($delta):$newValue" }
         interceptor.afterRMW(this, newValue - delta, newValue)
         return newValue
     }
@@ -399,6 +459,7 @@
     public actual fun incrementAndGet(): Long {
         interceptor.beforeUpdate(this)
         val newValue = FU.incrementAndGet(this)
+        if (trace !== TraceBase.None) trace { "incAndGet():$newValue" }
         interceptor.afterRMW(this, newValue - 1, newValue)
         return newValue
     }
@@ -409,6 +470,7 @@
     public actual fun decrementAndGet(): Long {
         interceptor.beforeUpdate(this)
         val newValue = FU.decrementAndGet(this)
+        if (trace !== TraceBase.None) trace { "decAndGet():$newValue" }
         interceptor.afterRMW(this, newValue + 1, newValue)
         return newValue
     }
@@ -432,4 +494,4 @@
     private companion object {
         private val FU = AtomicLongFieldUpdater.newUpdater(AtomicLong::class.java, "value")
     }
-}
+}
\ No newline at end of file
diff --git a/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/Trace.kt b/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/Trace.kt
new file mode 100644
index 0000000..4a1cb59
--- /dev/null
+++ b/atomicfu/src/jvmMain/kotlin/kotlinx/atomicfu/Trace.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
+package kotlinx.atomicfu
+
+import java.util.concurrent.atomic.*
+import kotlin.internal.*
+
+@Suppress("FunctionName")
+@InlineOnly
+public actual fun Trace(size: Int, format: TraceFormat): TraceBase =
+    TraceImpl(size, format)
+
+public actual fun TraceBase.named(name: String): TraceBase =
+    if (this === TraceBase.None) this else NamedTrace(this, name)
+
+private fun getSystemProperty(key: String): String? =
+    try { System.getProperty(key) } catch (e: SecurityException) { null }
+
+public actual val traceFormatDefault: TraceFormat =
+    if (getSystemProperty("kotlinx.atomicfu.trace.thread") != null) TraceFormatThread() else TraceFormat()
+
+private class TraceFormatThread : TraceFormat() {
+    override fun format(index: Int, event: Any): String =
+        "$index: [${Thread.currentThread().name}] $event"
+}
+
+private class NamedTrace(
+    private val trace: TraceBase, 
+    private val name: String
+) : TraceBase() {
+    override fun append(event: Any) = trace.append("$name.$event")
+
+    override fun append(event1: Any, event2: Any) = trace.append("$name.$event1", "$name.$event2")
+
+    override fun append(event1: Any, event2: Any, event3: Any) =
+            trace.append("$name.$event1", "$name.$event2", "$name.$event3")
+
+    override fun append(event1: Any, event2: Any, event3: Any, event4: Any) =
+            trace.append("$name.$event1", "$name.$event2", "$name.$event3", "$name.$event4")
+
+    override fun toString(): String = trace.toString()
+}
+
+private class TraceImpl(size: Int, private val format: TraceFormat) : TraceBase() {
+    init { require(size >= 1) }
+    private val size = ((size shl 1) - 1).takeHighestOneBit() // next power of 2
+    private val mask = this.size - 1
+    private val trace = arrayOfNulls<Any>(this.size)
+    private val index = AtomicInteger(0)
+
+    override fun append(event: Any) {
+        val i = index.getAndIncrement()
+        trace[i and mask] = event
+    }
+
+    override fun append(event1: Any, event2: Any) {
+        val i = index.getAndAdd(2)
+        trace[i and mask] = event1
+        trace[(i + 1) and mask] = event2
+    }
+
+    override fun append(event1: Any, event2: Any, event3: Any) {
+        val i = index.getAndAdd(3)
+        trace[i and mask] = event1
+        trace[(i + 1) and mask] = event2
+        trace[(i + 2) and mask] = event3
+    }
+
+    override fun append(event1: Any, event2: Any, event3: Any, event4: Any) {
+        val i = index.getAndAdd(4)
+        trace[i and mask] = event1
+        trace[(i + 1) and mask] = event2
+        trace[(i + 2) and mask] = event3
+        trace[(i + 3) and mask] = event4
+    }
+
+    override fun toString(): String = buildString {
+        val index = index.get()
+        val start = index and mask
+        var i = if (index > size) index - size else 0
+        var pos = start
+        var cnt = 0
+        do {
+            val s = trace[pos]
+            if (s != null) {
+                if (cnt++ > 0) append('\n')
+                append(format.format(i, s))
+                i++
+            }
+            pos = (pos + 1) and mask
+        } while (pos != start)
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/AtomicfuBytecodeTest.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/AtomicfuBytecodeTest.kt
index 45486b9..958cf5f 100644
--- a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/AtomicfuBytecodeTest.kt
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/AtomicfuBytecodeTest.kt
@@ -3,34 +3,46 @@
 import bytecode_test.*
 import org.junit.*
 
+private const val KOTLINX_ATOMICFU = "kotlinx/atomicfu"
+private const val KOTLIN_REFLECTION = "kotlin/reflect"
+
 /**
  * Makes sure classes do not have bytecode reference to atomicfu.
  */
 class AtomicfuBytecodeTest {
-    private val strings = listOf("kotlinx/atomicfu")
-
     /**
      * Test [SynchronizedObjectTest].
      */
     @Test
-    fun testSynchronizedObjectBytecode() = checkBytecode(SynchronizedObjectTest::class.java)
+    fun testSynchronizedObjectBytecode() = checkBytecode(SynchronizedObjectTest::class.java, listOf(KOTLINX_ATOMICFU))
 
     /**
      * Test [AtomicFieldTest].
      */
     @Test
-    fun testAtomicFieldBytecode() = checkBytecode(AtomicFieldTest::class.java)
+    fun testAtomicFieldBytecode() = checkBytecode(AtomicFieldTest::class.java, listOf(KOTLINX_ATOMICFU))
 
     /**
      * Test [ReentrantLockTest].
      */
     @Test
-    fun testReentrantLockBytecode() = checkBytecode(ReentrantLockTest::class.java)
+    fun testReentrantLockBytecode() = checkBytecode(ReentrantLockTest::class.java, listOf(KOTLINX_ATOMICFU))
 
-    private fun checkBytecode(javaClass: Class<*>) {
+    /**
+     * Test [TraceUseTest].
+     */
+    @Test
+    fun testTraceUseBytecode() = checkBytecode(TraceUseTest::class.java, listOf(KOTLINX_ATOMICFU))
+
+    /**
+     * Test [DelegatedProperties].
+     */
+    @Test
+    fun testDelegatedPropertiesBytecode() = checkBytecode(DelegatedProperties::class.java, listOf(KOTLIN_REFLECTION))
+
+    private fun checkBytecode(javaClass: Class<*>, strings: List<String>) {
         val resourceName = javaClass.name.replace('.', '/') + ".class"
         val bytes = javaClass.classLoader.getResourceAsStream(resourceName)!!.use { it.readBytes() }
         bytes.findString(strings)?.let { error("$it in $resourceName") }
     }
 }
-
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/AtomicfuReferenceJsTest.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/AtomicfuReferenceJsTest.kt
new file mode 100644
index 0000000..dcfb4fe
--- /dev/null
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/AtomicfuReferenceJsTest.kt
@@ -0,0 +1,22 @@
+package kotlinx.atomicfu.test
+
+import org.junit.Test
+import java.io.File
+
+private const val ATOMICFU_PREFIX = "atomicfu\$"
+private const val KOTLINX_ATOMICFU_MODULE = "\$module\$kotlinx_atomicfu"
+
+/**
+ * Makes sure transformed js output does not have references to atomicfu.
+ */
+class AtomicfuReferenceJsTest {
+
+    private val TRANSFORMED_JS_FILE = System.getenv("transformedJsFile")
+    private val dependencies = listOf(ATOMICFU_PREFIX, KOTLINX_ATOMICFU_MODULE)
+
+    @Test
+    fun testAtomicfuDependencies() {
+        val bytes = File(TRANSFORMED_JS_FILE).inputStream().use { it.readBytes() }
+        bytes.findString(dependencies)?.let { error("$it in $TRANSFORMED_JS_FILE") }
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTest.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/MetadataReflectionTest.kt
similarity index 97%
rename from atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTest.kt
rename to atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/MetadataReflectionTest.kt
index 22dbfe5..19ce768 100644
--- a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTest.kt
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/MetadataReflectionTest.kt
@@ -9,7 +9,7 @@
 /**
  * Make sure metadata is intact after transformation.
  */
-class ReflectionTest {
+class MetadataReflectionTest {
     @Test
     fun testReflection() {
         val f =
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTestBase.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTestBase.kt
new file mode 100644
index 0000000..6101931
--- /dev/null
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTestBase.kt
@@ -0,0 +1,23 @@
+package kotlinx.atomicfu.test
+
+import java.lang.reflect.Modifier
+import kotlin.test.assertEquals
+
+public open class ReflectionTestBase {
+    fun checkDeclarations(javaClass: Class<*>, expect: List<FieldDesc>) =
+            assertEquals(expect.joinToString(";"), getClassDeclarations(javaClass))
+
+    fun checkClassModifiers(javaClass: Class<*>, modifiers: Int, isSynthetic: Boolean) {
+        assertEquals(isSynthetic, javaClass.isSynthetic)
+        assertEquals(Modifier.toString(modifiers), Modifier.toString(javaClass.modifiers))
+    }
+
+    private fun getClassDeclarations(javaClass: Class<*>) =
+            javaClass.declaredFields.joinToString(separator = ";") {
+                "${Modifier.toString(it.modifiers)} ${if (it.isSynthetic) "synthetic " else ""}${it.type.name} ${it.name}"
+            }
+
+    data class FieldDesc(val modifiers: Int, val isSynthetic: Boolean, val typeName: String, val name: String) {
+        override fun toString() = "${Modifier.toString(modifiers)} ${if (isSynthetic) "synthetic " else ""}$typeName $name"
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/SyntheticFUFieldsTest.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/SyntheticFUFieldsTest.kt
new file mode 100644
index 0000000..4a9f46d
--- /dev/null
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/SyntheticFUFieldsTest.kt
@@ -0,0 +1,21 @@
+package kotlinx.atomicfu.test
+
+import bytecode_test.PrivateFieldAccessFromInnerClassReflectonTest
+import java.lang.reflect.Modifier.*
+import kotlin.test.Test
+
+private const val AFU_TYPE = "java.util.concurrent.atomic.AtomicIntegerFieldUpdater"
+
+/**
+ * Checks that generated FU and VH field updaters are marked as synthetic
+ */
+class SyntheticFUFieldsTest : ReflectionTestBase() {
+    @Test
+    fun testPrivateFieldAccessFromInnerClass() {
+        checkDeclarations(PrivateFieldAccessFromInnerClassReflectonTest::class.java, listOf(
+                FieldDesc(VOLATILE, true, "int", "state"),
+                FieldDesc(STATIC or FINAL, true, AFU_TYPE, "state\$FU")
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TopLevelGeneratedDeclarationsReflectionTest.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TopLevelGeneratedDeclarationsReflectionTest.kt
new file mode 100644
index 0000000..a104c3c
--- /dev/null
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TopLevelGeneratedDeclarationsReflectionTest.kt
@@ -0,0 +1,89 @@
+package kotlinx.atomicfu.test
+
+import kotlin.test.Test
+import java.lang.reflect.Modifier.*
+
+private const val REF_VOLATILE = "RefVolatile"
+private const val BYTECODE_PACKAGE = "bytecode_test"
+private const val AFU_TYPE = "java.util.concurrent.atomic.AtomicIntegerFieldUpdater"
+
+/**
+ * Checks access modifiers, name and type for generated declarations.
+ */
+class TopLevelGeneratedDeclarationsReflectionTest : ReflectionTestBase() {
+
+    /**
+     * Test [bytecode_test.NoAccessPrivateTopLevel]
+     */
+    @Test
+    fun testNoAccessPrivateTopLevel() {
+        val javaClass = Class.forName("$BYTECODE_PACKAGE.NoAccessPrivateTopLevel")
+        checkDeclarations(javaClass, listOf(
+                FieldDesc(PRIVATE or STATIC or FINAL, true, "$BYTECODE_PACKAGE.NoAccessPrivateTopLevel\$A$REF_VOLATILE", "noAccessPrivateTopLevel\$A$REF_VOLATILE")
+            )
+        )
+        val refVolatileClass = Class.forName("$BYTECODE_PACKAGE.NoAccessPrivateTopLevel\$A$REF_VOLATILE")
+        checkClassModifiers(refVolatileClass, 0, true)
+        checkDeclarations(refVolatileClass, listOf(
+                FieldDesc(VOLATILE, false, "int", "a")
+            )
+        )
+    }
+
+    /**
+     * Test [bytecode_test.PrivateTopLevel]
+     */
+    @Test
+    fun testPrivateTopLevel() {
+        val javaClass = Class.forName("$BYTECODE_PACKAGE.PrivateTopLevel")
+        checkDeclarations(javaClass, listOf(
+                FieldDesc(STATIC or FINAL, true, "$BYTECODE_PACKAGE.PrivateTopLevel\$B$REF_VOLATILE", "privateTopLevel\$B$REF_VOLATILE"),
+                FieldDesc(STATIC or FINAL, true, AFU_TYPE, "b\$FU")
+            )
+        )
+        val refVolatileClass = Class.forName("$BYTECODE_PACKAGE.PrivateTopLevel\$B$REF_VOLATILE")
+        checkClassModifiers(refVolatileClass, 0, true)
+        checkDeclarations(refVolatileClass, listOf(
+                FieldDesc(VOLATILE, false, "int", "b")
+            )
+        )
+    }
+
+    /**
+     * Test [bytecode_test.PublicTopLevel]
+     */
+    @Test
+    fun testPublicTopLevelReflectionTest() {
+        val javaClass = Class.forName("$BYTECODE_PACKAGE.PublicTopLevel")
+        checkDeclarations(javaClass, listOf(
+                FieldDesc(PUBLIC or STATIC or FINAL, true, "$BYTECODE_PACKAGE.PublicTopLevel\$C$REF_VOLATILE", "publicTopLevel\$C$REF_VOLATILE"),
+                FieldDesc(PUBLIC or STATIC or FINAL, true, AFU_TYPE, "c\$FU\$internal")
+            )
+        )
+        val refVolatileClass = Class.forName("$BYTECODE_PACKAGE.PublicTopLevel\$C$REF_VOLATILE")
+        checkClassModifiers(refVolatileClass, PUBLIC, true)
+        checkDeclarations(refVolatileClass, listOf(
+                FieldDesc(PUBLIC or VOLATILE, false, "int", "c\$internal")
+            )
+        )
+    }
+
+    /**
+     * Test [bytecode_test.PackagePrivateTopLevel]
+     */
+    @Test
+    fun testPackagePrivateTopLevelReflectionTest() {
+        val javaClass = Class.forName("$BYTECODE_PACKAGE.PackagePrivateTopLevel")
+        checkDeclarations(javaClass, listOf(
+                FieldDesc(STATIC or FINAL, true, "$BYTECODE_PACKAGE.PackagePrivateTopLevel\$D$REF_VOLATILE", "packagePrivateTopLevel\$D$REF_VOLATILE"),
+                FieldDesc(STATIC or FINAL, true, AFU_TYPE, "d\$FU")
+        )
+        )
+        val refVolatileClass = Class.forName("$BYTECODE_PACKAGE.PackagePrivateTopLevel\$D$REF_VOLATILE")
+        checkClassModifiers(refVolatileClass, 0, true)
+        checkDeclarations(refVolatileClass, listOf(
+                FieldDesc(VOLATILE, false, "int", "d")
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TraceLFTest.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TraceLFTest.kt
new file mode 100644
index 0000000..32ea8d8
--- /dev/null
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TraceLFTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.atomicfu.test
+
+import kotlinx.atomicfu.*
+import kotlin.test.Test
+
+class Counter {
+    private val t = Trace(64, TraceFormat { index, text ->
+        "$index: [${Thread.currentThread().name}] $text"
+    })
+    private val a = atomic(0, t)
+
+    fun inc(): Int {
+        t { "inc() invoked" }
+        val x = a.incrementAndGet()
+        t { "inc() = $x" }
+        return x
+    }
+
+    internal fun get() = a.value
+}
+
+class CounterDefaultAtomic {
+    private val a = atomic(0)
+    private val trace = Trace(64)
+
+    fun inc(): Int {
+        trace { "inc() invoked" }
+        val x = a.incrementAndGet()
+        trace { "inc() = $x" }
+        return x
+    }
+
+    internal fun get() = a.value
+}
+
+class CounterLFTest : LockFreedomTestEnvironment("CounterLFTest") {
+    private val c = Counter()
+    private val c1 = CounterDefaultAtomic()
+
+    @Test
+    fun testCounterDefault() {
+        repeat(10) { id ->
+            testThread ("Inc-$id")  {
+                c1.inc()
+            }
+        }
+        repeat(2) { id ->
+            testThread("Get-$id") {
+                c1.get()
+            }
+        }
+        performTest(10)
+        println(c1.get())
+    }
+
+    @Test
+    fun testLockFreedom() {
+        repeat(10) { id ->
+            testThread("Inc-$id") {
+                c.inc()
+            }
+        }
+        repeat(2) { id ->
+            testThread("Get-$id") {
+                c.get()
+            }
+        }
+        performTest(10)
+        println(c.get())
+    }
+}
+
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TraceToStringTest.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TraceToStringTest.kt
new file mode 100644
index 0000000..5d74ccf
--- /dev/null
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/TraceToStringTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.atomicfu.test
+
+import kotlinx.atomicfu.*
+import kotlin.test.*
+
+class TraceToStringTest {
+    private val aTrace = Trace(format = TraceFormat { i, text -> "[$i: $text]" })
+    private val a = atomic(0, aTrace)
+
+    private val shortTrace = Trace(4)
+    private val s = atomic(0, shortTrace.named("s"))
+
+    @Test
+    fun testTraceFormat() {
+        repeat(3) { i ->
+            aTrace { "Iteration $i started" }
+            a.lazySet(i)
+            aTrace { "Iteration $i ended" }
+        }
+        val expected = buildString {
+            repeat(3) { i ->
+                if (i > 0) append('\n')
+                append("""
+                    [${i * 3 + 0}: Iteration $i started]
+                    [${i * 3 + 1}: lazySet($i)]
+                    [${i * 3 + 2}: Iteration $i ended]
+                    """.trimIndent()
+                )
+            }
+        }
+        assertEquals(expected, a.trace.toString())
+    }
+
+    @Test
+    fun testTraceSequence() {
+        s.value = 5
+        s.compareAndSet(5, -2)
+        assertEquals("""
+            0: s.set(5)
+            1: s.CAS(5, -2)
+            """.trimIndent(), s.trace.toString()
+        )
+        s.lazySet(3)
+        s.getAndIncrement()
+        s.getAndAdd(7)
+        assertEquals("""
+            1: s.CAS(5, -2)
+            2: s.lazySet(3)
+            3: s.getAndInc():3
+            4: s.getAndAdd(7):4
+            """.trimIndent(), s.trace.toString()
+        )
+        s.getAndAdd(8)
+        s.getAndAdd(9)
+        assertEquals("""
+            3: s.getAndInc():3
+            4: s.getAndAdd(7):4
+            5: s.getAndAdd(8):11
+            6: s.getAndAdd(9):19
+            """.trimIndent(), s.trace.toString()
+        )
+        s.lazySet(3)
+        assertEquals("""
+            4: s.getAndAdd(7):4
+            5: s.getAndAdd(8):11
+            6: s.getAndAdd(9):19
+            7: s.lazySet(3)
+            """.trimIndent(), s.trace.toString()
+        )
+        s.getAndIncrement()
+        s.getAndAdd(7)
+        assertEquals("""
+            6: s.getAndAdd(9):19
+            7: s.lazySet(3)
+            8: s.getAndInc():3
+            9: s.getAndAdd(7):4
+            """.trimIndent(), s.trace.toString()
+        )
+    }
+
+    private enum class Status { START, END }
+
+    @Test
+    fun testMultipleAppend() {
+        val i = 7
+        aTrace.append(i, Status.START)
+        a.lazySet(i)
+        aTrace.append(i, Status.END)
+        assertEquals("""
+            [0: $i]
+            [1: START]
+            [2: lazySet($i)]
+            [3: $i]
+            [4: END]
+            """.trimIndent(), aTrace.toString()
+        )
+    }
+}
\ No newline at end of file
diff --git a/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/AtomicFU.kt b/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
index 0980ecb..55ed452 100644
--- a/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
+++ b/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/AtomicFU.kt
@@ -11,11 +11,17 @@
 import kotlin.native.concurrent.FreezableAtomicReference as KAtomicRef
 import kotlin.native.concurrent.isFrozen
 import kotlin.native.concurrent.freeze
+import kotlin.reflect.KProperty
+import kotlinx.atomicfu.TraceBase.None
 
-public actual fun <T> atomic(initial: T): AtomicRef<T> = AtomicRef<T>(KAtomicRef(initial))
-public actual fun atomic(initial: Int): AtomicInt = AtomicInt(KAtomicInt(initial))
-public actual fun atomic(initial: Long): AtomicLong = AtomicLong(KAtomicLong(initial))
-public actual fun atomic(initial: Boolean): AtomicBoolean = AtomicBoolean(KAtomicInt(if (initial) 1 else 0))
+public actual fun <T> atomic(initial: T, trace: TraceBase): AtomicRef<T> = AtomicRef<T>(KAtomicRef(initial))
+public actual fun <T> atomic(initial: T): AtomicRef<T> = atomic(initial, None)
+public actual fun atomic(initial: Int, trace: TraceBase): AtomicInt = AtomicInt(KAtomicInt(initial))
+public actual fun atomic(initial: Int): AtomicInt = atomic(initial, None)
+public actual fun atomic(initial: Long, trace: TraceBase): AtomicLong = AtomicLong(KAtomicLong(initial))
+public actual fun atomic(initial: Long): AtomicLong = atomic(initial, None)
+public actual fun atomic(initial: Boolean, trace: TraceBase): AtomicBoolean = AtomicBoolean(KAtomicInt(if (initial) 1 else 0))
+public actual fun atomic(initial: Boolean): AtomicBoolean = atomic(initial, None)
 
 // ==================================== AtomicRef ====================================
 
@@ -28,9 +34,13 @@
             a.value = value
         }
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { this.value = value }
+
     public actual inline fun lazySet(value: T) {
         if (a.isFrozen) value.freeze()
-        a.value = value 
+        a.value = value
     }
 
     public actual inline fun compareAndSet(expect: T, update: T): Boolean {
@@ -58,6 +68,10 @@
         get() = a.value != 0
         set(value) { a.value = if (value) 1 else 0 }
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { this.value = value }
+
     public actual inline fun lazySet(value: Boolean) { this.value = value }
 
     public actual fun compareAndSet(expect: Boolean, update: Boolean): Boolean {
@@ -86,6 +100,10 @@
         get() = a.value
         set(value) { a.value = value }
 
+    actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { this.value = value }
+
     public actual inline fun lazySet(value: Int) { a.value = value }
 
     public actual inline fun compareAndSet(expect: Int, update: Int): Boolean =
@@ -120,6 +138,10 @@
         get() = a.value
         set(value) { a.value = value }
 
+    public actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Long = value
+
+    public actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) { this.value = value }
+
     public actual inline fun lazySet(value: Long) { a.value = value }
 
     public actual inline fun compareAndSet(expect: Long, update: Long): Boolean =
diff --git a/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/Trace.kt b/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/Trace.kt
new file mode 100644
index 0000000..9d8dcbf
--- /dev/null
+++ b/atomicfu/src/nativeMain/kotlin/kotlinx/atomicfu/Trace.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.atomicfu
+
+@Suppress("FunctionName")
+public actual fun Trace(size: Int, format: TraceFormat): TraceBase = TraceBase.None
+
+public actual fun TraceBase.named(name: String): TraceBase = TraceBase.None
+
+public actual val traceFormatDefault: TraceFormat = TraceFormat()
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index d399768..12f531d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -25,10 +25,15 @@
             maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
         }
     }
+    // These two flags are enabled in train builds for JVM IR compiler testing
+    ext.jvm_ir_enabled = rootProject.properties['enable_jvm_ir'] != null
+    ext.native_targets_enabled = rootProject.properties['disable_native_targets'] == null
 
     repositories {
         jcenter()
         maven { url "https://plugins.gradle.org/m2/" }
+        // Future replacement for kotlin-dev, with cache redirector
+        maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
         maven {
             url "https://kotlin.bintray.com/kotlin-dev"
             credentials {
@@ -38,6 +43,7 @@
         }
         maven { url "https://kotlin.bintray.com/kotlin-eap" }
         maven { url "https://jetbrains.bintray.com/kotlin-native-dependencies" }
+        maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
     }
     
     dependencies {
@@ -61,6 +67,8 @@
     repositories {
         jcenter()
         maven { url "https://kotlin.bintray.com/kotlin-eap" }
+        // Future replacement for kotlin-dev, with cache redirector
+        maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
         maven {
             url "https://kotlin.bintray.com/kotlin-dev"
             credentials {
@@ -69,6 +77,7 @@
             }
         }
         maven { url "https://kotlin.bintray.com/kotlinx" }
+        maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
     }
 
     def deployVersion = properties['DeployVersion']
@@ -76,7 +85,7 @@
 
     // 'atomicfu-native' check is a kludge so that existing YouTrack config works, todo: remove
     if (project != rootProject && project.name != 'atomicfu-native') {
-        apply from: rootProject.file("gradle/publish-bintray.gradle")
+        apply from: rootProject.file("gradle/publishing.gradle")
     }
 
     // This fixes "org.gradle.jvm.version" in Gradle metadata
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
new file mode 100644
index 0000000..8b6b358
--- /dev/null
+++ b/buildSrc/build.gradle.kts
@@ -0,0 +1,14 @@
+import org.jetbrains.kotlin.gradle.plugin.*
+import java.util.*
+
+plugins {
+    `kotlin-dsl`
+}
+
+repositories {
+    jcenter()
+}
+
+kotlinDslPluginOptions {
+    experimentalWarning.set(false)
+}
diff --git a/buildSrc/src/main/kotlin/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt
new file mode 100644
index 0000000..6844ad7
--- /dev/null
+++ b/buildSrc/src/main/kotlin/Publishing.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+import org.gradle.api.*
+import org.gradle.api.artifacts.dsl.*
+import org.gradle.api.provider.*
+import org.gradle.api.publish.maven.*
+import org.gradle.plugins.signing.*
+import java.net.*
+
+// Pom configuration
+
+infix fun <T> Property<T>.by(value: T) {
+    set(value)
+}
+
+fun MavenPom.configureMavenCentralMetadata(project: Project) {
+    name by project.name
+    description by "AtomicFU utilities"
+    url by "https://github.com/Kotlin/kotlinx.atomicfu"
+
+    licenses {
+        license {
+            name by "The Apache Software License, Version 2.0"
+            url by "https://www.apache.org/licenses/LICENSE-2.0.txt"
+            distribution by "repo"
+        }
+    }
+
+    developers {
+        developer {
+            id by "JetBrains"
+            name by "JetBrains Team"
+            organization by "JetBrains"
+            organizationUrl by "https://www.jetbrains.com"
+        }
+    }
+
+    scm {
+        url by "https://github.com/Kotlin/kotlinx.atomicfu"
+    }
+}
+
+fun configureBintrayPublication(rh: RepositoryHandler, project: Project) {
+    rh.maven {
+        val user = "kotlin"
+        val repo = "kotlinx"
+        val name = "kotlinx.atomicfu"
+        url = URI("https://api.bintray.com/maven/$user/$repo/$name/;publish=0;override=0")
+
+        credentials {
+            username = project.findProperty("bintrayUser") as? String ?: System.getenv("BINTRAY_USER")
+            password = project.findProperty("bintrayApiKey") as? String ?: System.getenv("BINTRAY_API_KEY")
+        }
+    }
+}
+
+fun mavenRepositoryUri(): URI {
+    // TODO -SNAPSHOT detection can be made here as well
+    val repositoryId: String? = System.getenv("libs.repository.id")
+    return if (repositoryId == null) {
+        // Using implicitly created staging, for MPP it's likely to be a mistake because
+        // publication on TeamCity will create 3 independent staging repositories
+        System.err.println("Warning: using an implicitly created staging for atomicfu")
+        URI("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
+    } else {
+        URI("https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId")
+    }
+}
+
+fun configureMavenPublication(rh: RepositoryHandler, project: Project) {
+    rh.maven {
+        url = mavenRepositoryUri()
+        credentials {
+            username = project.getSensitiveProperty("libs.sonatype.user")
+            password = project.getSensitiveProperty("libs.sonatype.password")
+        }
+    }
+}
+
+fun signPublicationIfKeyPresent(project: Project, publication: MavenPublication) {
+    val keyId = project.getSensitiveProperty("libs.sign.key.id")
+    val signingKey = project.getSensitiveProperty("libs.sign.key.private")
+    val signingKeyPassphrase = project.getSensitiveProperty("libs.sign.passphrase")
+    if (!signingKey.isNullOrBlank()) {
+        project.extensions.configure<SigningExtension>("signing") {
+            useInMemoryPgpKeys(keyId, signingKey, signingKeyPassphrase)
+            sign(publication)
+        }
+    }
+}
+
+private fun Project.getSensitiveProperty(name: String): String? {
+    return project.findProperty(name) as? String ?: System.getenv(name)
+}
diff --git a/gradle.properties b/gradle.properties
index 8a16358..ce4f84c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,13 +2,14 @@
 # Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 #
 
-version=0.14.4-SNAPSHOT
+version=0.16.0-SNAPSHOT
 group=org.jetbrains.kotlinx
 
-kotlin_version=1.4.0
+kotlin_version=1.5.0
 asm_version=7.2
 slf4j_version=1.8.0-alpha2
 junit_version=4.12
+kotlinx_metadata_version=0.2.0
 
 maven_version=3.5.3
 
diff --git a/gradle/compile-options.gradle b/gradle/compile-options.gradle
index fa387b1..e6b4432 100644
--- a/gradle/compile-options.gradle
+++ b/gradle/compile-options.gradle
@@ -3,11 +3,26 @@
  * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
-ext.configureKotlin = { type ->
-    tasks.withType(type).all {
-        kotlinOptions {
-            apiVersion = "1.3"
-            languageVersion = "1.3"
+ext.configureKotlin = { isMultiplatform ->
+    if (rootProject.ext.jvm_ir_enabled) {
+        println "Using JVM IR compiler for project $project.name"
+        if (isMultiplatform) {
+            kotlin.jvm().compilations.all {
+                kotlinOptions.useIR = true
+            }
+        } else {
+            kotlin.target.compilations.all {
+                kotlinOptions.useIR = true
+            }
+        }
+    }
+
+    kotlin.sourceSets.all {
+        languageSettings {
+            apiVersion = "1.4"
+            languageVersion = "1.4"
+            useExperimentalAnnotation("kotlin.Experimental")
+            useExperimentalAnnotation("kotlin.ExperimentalStdlibApi")
         }
     }
 }
diff --git a/gradle/maven-central.gradle b/gradle/maven-central.gradle
deleted file mode 100644
index fbd7f72..0000000
--- a/gradle/maven-central.gradle
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-// ------- metadata for Maven Central
-
-def pomConfig = {
-    licenses {
-        license {
-            name "The Apache Software License, Version 2.0"
-            url "https://www.apache.org/licenses/LICENSE-2.0.txt"
-            distribution "repo"
-        }
-    }
-    developers {
-        developer {
-            id "JetBrains"
-            name "JetBrains Team"
-            organization "JetBrains"
-            organizationUrl "https://www.jetbrains.com"
-        }
-    }
-    scm {
-        url "https://github.com/Kotlin/atomicfu"
-    }
-}
-
-project.ext.configureMavenCentralMetadata = {
-    def root = it.asNode()
-    // NOTE: Don't try to move top-level things (especially "description") to the pomConfig block
-    //       because they would resolve incorrectly to top-level project properties in Gradle/Groovy
-    root.appendNode('name', project.name)
-    root.appendNode('description', 'AtomicFU utilities')
-    root.appendNode('url', 'https://github.com/Kotlin/atomicfu')
-    root.children().last() + pomConfig
-}
-
diff --git a/gradle/publish-mpp-root-module-in-platform.gradle b/gradle/publish-mpp-root-module-in-platform.gradle
index 38b7f89..94920bd 100644
--- a/gradle/publish-mpp-root-module-in-platform.gradle
+++ b/gradle/publish-mpp-root-module-in-platform.gradle
@@ -5,28 +5,33 @@
 
 /** Publish the platform JAR and POM so that consumers who depend on this module and can't read Gradle module
    metadata can still get the platform artifact and transitive dependencies from the POM: */
-project.ext.publishPlatformArtifactsInRootModule = { platformPublication ->
+project.ext.publishPlatformArtifactsInRootModule = { MavenPublication platformPublication ->
     afterEvaluate {
-        def platformPomBuilder = null
+        XmlProvider platformXml = null
 
-        platformPublication.pom.withXml { platformPomBuilder = asString() }
+        platformPublication.pom.withXml { platformXml = it }
 
         publishing.publications.kotlinMultiplatform {
-            platformPublication.artifacts.forEach {
-                artifact(it)
-            }
-
             pom.withXml {
-                def pomStringBuilder = asString()
-                pomStringBuilder.setLength(0)
-                // The platform POM needs its artifact ID replaced with the artifact ID of the root module:
-                def platformPomString = platformPomBuilder.toString()
-                platformPomString.eachLine { line ->
-                    if (!line.contains("<!--")) { // Remove the Gradle module metadata marker as it will be added anew
-                        pomStringBuilder.append(line.replace(platformPublication.artifactId, artifactId))
-                        pomStringBuilder.append("\n")
-                    }
-                }
+                Node root = asNode()
+                // Remove the original content and add the content from the platform POM:
+                root.children().toList().each { root.remove(it as Node) }
+                platformXml.asNode().children().each { root.append(it as Node) }
+
+                // Adjust the self artifact ID, as it should match the root module's coordinates:
+                ((root.get("artifactId") as NodeList).get(0) as Node).setValue(artifactId)
+
+                // Set packaging to POM to indicate that there's no artifact:
+                root.appendNode("packaging", "pom")
+
+                // Remove the original platform dependencies and add a single dependency on the platform module:
+                Node dependencies = (root.get("dependencies") as NodeList).get(0) as Node
+                dependencies.children().toList().each { dependencies.remove(it as Node) }
+                Node singleDependency = dependencies.appendNode("dependency")
+                singleDependency.appendNode("groupId", platformPublication.groupId)
+                singleDependency.appendNode("artifactId", platformPublication.artifactId)
+                singleDependency.appendNode("version", platformPublication.version)
+                singleDependency.appendNode("scope", "compile")
             }
         }
 
diff --git a/gradle/publish-bintray.gradle b/gradle/publishing.gradle
similarity index 69%
rename from gradle/publish-bintray.gradle
rename to gradle/publishing.gradle
index 50f2590..0ca4788 100644
--- a/gradle/publish-bintray.gradle
+++ b/gradle/publishing.gradle
@@ -1,13 +1,12 @@
 /*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 // Configures publishing of Maven artifacts to Bintray
 
 apply plugin: 'maven'
 apply plugin: 'maven-publish'
-
-apply from: project.rootProject.file('gradle/maven-central.gradle')
+apply plugin: 'signing'
 
 // todo: figure out how we can check it in a generic way
 def isMultiplatform = project.name == 'atomicfu'
@@ -28,18 +27,14 @@
     archiveClassifier = 'javadoc'
 }
 
-publishing {
-    repositories {
-        maven {
-            def user = 'kotlin'
-            def repo = 'kotlinx'
-            def name = 'kotlinx.atomicfu'
-            url = "https://api.bintray.com/maven/$user/$repo/$name/;publish=0;override=0"
+def bintrayUpload = System.getenv("libs.bintray.upload") != null
 
-            credentials {
-                username = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER')
-                password = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY')
-            }
+publishing {
+    repositories { // this: closure
+        if (bintrayUpload) {
+            PublishingKt.configureBintrayPublication(delegate, project)
+        } else {
+            PublishingKt.configureMavenPublication(delegate, project)
         }
     }
     
@@ -65,7 +60,10 @@
     }
 
     publications.all {
-        pom.withXml(configureMavenCentralMetadata)
+        PublishingKt.configureMavenCentralMetadata(pom, project)
+        if (!bintrayUpload) {
+            PublishingKt.signPublicationIfKeyPresent(project, it)
+        }
 
         // add empty javadocs
         if (it.name != "kotlinMultiplatform") { // The root module gets the JVM's javadoc JAR
diff --git a/settings.gradle b/settings.gradle
index 1fbbc0c..ab0e0a9 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,7 +1,6 @@
 enableFeaturePreview('GRADLE_METADATA')
 
 include 'atomicfu'
-
 include 'atomicfu-transformer'
 include 'atomicfu-gradle-plugin'
 include 'atomicfu-maven-plugin'