Adjust kotlin.Metadata in JVM classes to remove atomicfu references

References to transformed fields and methods are completely removed
from kotlin.Metadata annotations, thus no references to atomicfu remain
in the class class files after transformation.
diff --git a/atomicfu-maven-plugin/build.gradle b/atomicfu-maven-plugin/build.gradle
index edf972e..8462877 100644
--- a/atomicfu-maven-plugin/build.gradle
+++ b/atomicfu-maven-plugin/build.gradle
@@ -53,6 +53,11 @@
                             appendNode('url', 'https://kotlin.bintray.com/kotlin-dev')
                         }
 
+                        appendNode('repository').with {
+                            appendNode('id', 'kotlinx')
+                            appendNode('url', 'https://kotlin.bintray.com/kotlinx')
+                        }
+
                         if (buildSnapshots) {
                             appendNode('repository').with {
                                 appendNode('id', 'kotlin-snapshots')
diff --git a/atomicfu-transformer/build.gradle b/atomicfu-transformer/build.gradle
index 4f69a62..0a3aeb2 100644
--- a/atomicfu-transformer/build.gradle
+++ b/atomicfu-transformer/build.gradle
@@ -17,4 +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.0.5.1"
 }
\ No newline at end of file
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 2a218b0..dbba255 100644
--- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformer.kt
+++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformer.kt
@@ -96,7 +96,7 @@
     return node.instructions
 }
 
-data class FieldId(val owner: String, val name: String) {
+data class FieldId(val owner: String, val name: String, val desc: String) {
     override fun toString(): String = "${owner.prettyStr()}::$name"
 }
 
@@ -149,21 +149,30 @@
 
     override fun transform() {
         info("Analyzing in $inputDir")
-        val succ = analyzeFiles()
+        val files = inputDir.walk().filter { it.isFile }.toList()
+        val needTransform = analyzeFiles(files)
         if (hasErrors) throw Exception("Encountered errors while collecting fields")
-        if (succ || outputDir == inputDir) {
+        if (needTransform || outputDir == inputDir) {
+            // visit method bodies for external references to fields
+            analyzeExternalRefs(files)
             // perform transformation
             info("Transforming to $outputDir")
             val vh = variant == Variant.VH
-            inputDir.walk().filter { it.isFile }.forEach { file ->
+            files.forEach { file ->
                 val bytes = file.readBytes()
                 val outBytes = if (file.isClassFile()) transformFile(file, bytes, vh) else bytes
-                val outFile = file.toOutputFile()
-                outFile.mkdirsAndWrite(outBytes)
-                if (variant == Variant.BOTH && outBytes !== bytes) {
-                    val vhBytes = transformFile(file, bytes, true)
-                    val vhFile = outputDir / "META-INF" / "versions" / "9" / file.relativeTo(inputDir).toString()
-                    vhFile.mkdirsAndWrite(vhBytes)
+                if (outBytes != null) {
+                    // write output only if successfully transformed, so that it retries again on restart
+                    val outFile = file.toOutputFile()
+                    outFile.mkdirsAndWrite(outBytes)
+                    if (variant == Variant.BOTH && outBytes !== bytes) {
+                        val vhBytes = transformFile(file, bytes, true)
+                        if (vhBytes != null) {
+                            val vhFile =
+                                outputDir / "META-INF" / "versions" / "9" / file.relativeTo(inputDir).toString()
+                            vhFile.mkdirsAndWrite(vhBytes)
+                        }
+                    }
                 }
             }
             if (hasErrors) throw Exception("Encountered errors while transforming")
@@ -172,21 +181,24 @@
         }
     }
 
-    private fun analyzeFiles(): Boolean {
-        var inpFilesTime = 0L
-        var outFilesTime = 0L
-        val files = inputDir.walk().filter { it.isFile }.toList()
-        // 1 phase: visit methods and fields, register all accessors
+    // returns 'true' if files need to be transformed
+    // 1 phase: visit methods and fields, register all accessors, collect times
+    private fun analyzeFiles(files: List<File>): Boolean {
+        var needTransform = false
         files.forEach { file ->
-            inpFilesTime = inpFilesTime.coerceAtLeast(file.lastModified())
+            val inpTime = file.lastModified()
+            val outTime = file.toOutputFile().lastModified()
+            if (inpTime > outTime) needTransform = true
             if (file.isClassFile()) analyzeFile(file)
         }
-        // 2 phase: visit method bodies for external references to fields
+        return needTransform
+    }
+
+    // 2 phase: visit method bodies for external references to fields
+    private fun analyzeExternalRefs(files: List<File>) {
         files.forEach { file ->
             if (file.isClassFile()) analyzeExternalRefs(file)
-            outFilesTime = outFilesTime.coerceAtLeast(file.toOutputFile().lastModified())
         }
-        return inpFilesTime > outFilesTime
     }
 
     private fun analyzeFile(file: File) {
@@ -197,7 +209,7 @@
         file.inputStream().use { ClassReader(it).accept(ReferencesCollectorCV(), SKIP_FRAMES) }
     }
 
-    private fun transformFile(file: File, bytes: ByteArray, vh: Boolean): ByteArray {
+    private fun transformFile(file: File, bytes: ByteArray, vh: Boolean): ByteArray? {
         transformed = false
         val cw = CW()
         val cv = TransformerCV(cw, vh)
@@ -210,6 +222,8 @@
         } catch (e: Exception) {
             error("Failed to transform: $e", cv.sourceInfo)
             e.printStackTrace(System.out)
+            hasErrors = true
+            return null // signal failure
         }
         return bytes
     }
@@ -246,7 +260,7 @@
         ): FieldVisitor? {
             val fieldType = getType(desc)
             if (fieldType.sort == OBJECT && fieldType.internalName in AFU_CLASSES) {
-                val field = FieldId(className, name)
+                val field = FieldId(className, name, desc)
                 info("$field field found")
                 if (ACC_PUBLIC in access) error("$field field cannot be public")
                 if (ACC_FINAL !in access) error("$field field must be final")
@@ -292,7 +306,7 @@
                 val isStatic = insns.size == 2
                 val fi = (if (isStatic) insns[0] else insns[1]) as FieldInsnNode
                 val fieldName = fi.name
-                val field = FieldId(className, fieldName)
+                val field = FieldId(className, fieldName, fi.desc)
                 val fieldType = getType(fi.desc)
                 val accessorMethod = MethodId(className, name, desc, accessToInvokeOpcode(access))
                 info("$field accessor $name found")
@@ -358,6 +372,8 @@
         private var source: String? = null
         var sourceInfo: SourceInfo? = null
 
+        private var metadata: AnnotationNode? = null
+
         private var originalClinit: MethodNode? = null
         private var newClinit: MethodNode? = null
 
@@ -378,7 +394,7 @@
         ): FieldVisitor? {
             val fieldType = getType(desc)
             if (fieldType.sort == OBJECT && fieldType.internalName in AFU_CLASSES) {
-                val fieldId = FieldId(className, name)
+                val fieldId = FieldId(className, name, desc)
                 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)
@@ -502,7 +518,25 @@
             return mv
         }
 
+        override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor {
+            if (desc == KOTLIN_METADATA_DESC) {
+                check(visible) { "Expected run-time visible $KOTLIN_METADATA_DESC annotation" }
+                check(metadata == null) { "Only one $KOTLIN_METADATA_DESC annotation is expected" }
+                return AnnotationNode(desc).also { metadata = it }
+            }
+            return super.visitAnnotation(desc, visible)
+        }
+
         override fun visitEnd() {
+            // remove unused methods from metadata
+            metadata?.let {
+                val mt = MetadataTransformer(
+                    removeFields = fields.keys,
+                    removeMethods = accessors.keys + removeMethods
+                )
+                if (mt.transformMetadata(it)) transformed = true
+                it.accept(cv.visitAnnotation(KOTLIN_METADATA_DESC, true))
+            }
             // collect class initialization
             if (originalClinit != null || newClinit != null) {
                 val newClinit = newClinit
@@ -566,7 +600,7 @@
 
         private fun FieldInsnNode.checkPutFieldOrPutStatic(): FieldId? {
             if (opcode != PUTFIELD && opcode != PUTSTATIC) return null
-            val fieldId = FieldId(owner, name)
+            val fieldId = FieldId(owner, name, desc)
             return if (fieldId in fields) fieldId else null
         }
 
@@ -927,7 +961,7 @@
                     }
                 }
                 is FieldInsnNode -> {
-                    val fieldId = FieldId(i.owner, i.name)
+                    val fieldId = FieldId(i.owner, i.name, i.desc)
                     if ((i.opcode == GETFIELD || i.opcode == GETSTATIC) && fieldId in fields) {
                         // Convert GETFIELD to GETSTATIC on var handle / field updater
                         val f = fields[fieldId]!!
diff --git a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt
new file mode 100644
index 0000000..28b58cc
--- /dev/null
+++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/MetadataTransformer.kt
@@ -0,0 +1,516 @@
+/*
+ * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.atomicfu.transformer
+
+import kotlinx.metadata.*
+import kotlinx.metadata.jvm.*
+import org.objectweb.asm.tree.*
+
+const val KOTLIN_METADATA_DESC = "Lkotlin/Metadata;"
+
+class MetadataTransformer(
+    removeFields: Set<FieldId>,
+    removeMethods: Set<MethodId>
+) {
+    private val removeFieldSignatures: List<JvmFieldSignature> =
+        removeFields.map { JvmFieldSignature(it.name, it.desc) }
+    private val removeMethodSignatures: List<JvmMethodSignature> =
+        removeMethods.map { JvmMethodSignature(it.name, it.desc) }
+    private var transformed = false
+
+    @Suppress("UNCHECKED_CAST")
+    fun transformMetadata(metadataAnnotation: AnnotationNode): Boolean {
+        val map = metadataAnnotation.asMap()
+        val hdr = KotlinClassHeader(
+            kind = map["k"] as Int?,
+            metadataVersion = (map["mv"] as? List<Int>)?.toIntArray(),
+            bytecodeVersion = (map["bv"] as? List<Int>)?.toIntArray(),
+            data1 = (map["d1"] as? List<String>)?.toTypedArray(),
+            data2 = (map["d2"] as? List<String>)?.toTypedArray(),
+            extraString = map["xs"] as String?,
+            packageName = map["pn"] as String?,
+            extraInt = map["xi"] as Int?
+        )
+        val result = when (val metadata = KotlinClassMetadata.read(hdr)) {
+            is KotlinClassMetadata.Class -> {
+                val w = KotlinClassMetadata.Class.Writer()
+                metadata.accept(object : KmClassVisitor(w) {
+                    override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor? =
+                        PropertyFilterNode(super.visitProperty(flags, name, getterFlags, setterFlags)!!)
+                    override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor =
+                        FunctionFilterNode(super.visitFunction(flags, name)!!)
+                })
+                w.write(hdr.metadataVersion, hdr.bytecodeVersion, hdr.extraInt)
+            }
+            is KotlinClassMetadata.FileFacade -> {
+                val w = KotlinClassMetadata.FileFacade.Writer()
+                metadata.accept(object : KmPackageVisitor(w) {
+                    override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor? =
+                        PropertyFilterNode(super.visitProperty(flags, name, getterFlags, setterFlags)!!)
+                    override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor =
+                        FunctionFilterNode(super.visitFunction(flags, name)!!)
+                })
+                w.write(hdr.metadataVersion, hdr.bytecodeVersion, hdr.extraInt)
+            }
+            is KotlinClassMetadata.MultiFileClassPart -> {
+                val w = KotlinClassMetadata.MultiFileClassPart.Writer()
+                metadata.accept(object : KmPackageVisitor(w) {
+                    override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor? =
+                        PropertyFilterNode(super.visitProperty(flags, name, getterFlags, setterFlags)!!)
+                    override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor =
+                        FunctionFilterNode(super.visitFunction(flags, name)!!)
+                })
+                w.write(metadata.facadeClassName, hdr.metadataVersion, hdr.bytecodeVersion, hdr.extraInt)
+            }
+            else -> return false // not transformed
+        }
+        if (!transformed) return false
+        result.apply {
+            with (metadataAnnotation) {
+                // read resulting header & update annotation data
+                setKey("d1", header.data1.toList())
+                setKey("d2", header.data2.toList())
+            }
+        }
+        return true // transformed
+    }
+
+    private interface VersionRequirementMember {
+        fun accept(v: KmVersionRequirementVisitor)
+    }
+
+    private class Version1(
+        val kind: KmVersionRequirementVersionKind,
+        val level: KmVersionRequirementLevel,
+        val errorCode: Int?,
+        val message: String?
+    ) : VersionRequirementMember {
+        override fun accept(v: KmVersionRequirementVisitor) {
+            v.visit(kind, level, errorCode, message)
+        }
+    }
+
+    private class Version2(
+        val major: Int,
+        val minor: Int,
+        val patch: Int
+    ) : VersionRequirementMember {
+        override fun accept(v: KmVersionRequirementVisitor) {
+            v.visitVersion(major, minor, patch)
+        }
+    }
+
+    private class VersionRequirementNode : KmVersionRequirementVisitor() {
+        private val m = ArrayList<VersionRequirementMember>()
+
+        override fun visit(
+            kind: KmVersionRequirementVersionKind,
+            level: KmVersionRequirementLevel,
+            errorCode: Int?,
+            message: String?
+        ) {
+            m += Version1(kind, level, errorCode, message)
+        }
+
+        override fun visitVersion(major: Int, minor: Int, patch: Int) {
+            m += Version2(major, minor, patch)
+        }
+
+        fun accept(v: KmVersionRequirementVisitor) {
+            m.forEach { it.accept(v) }
+            v.visitEnd()
+        }
+    }
+
+    private class TypeParameterExtensionNode : JvmTypeParameterExtensionVisitor() {
+        private val annotations = ArrayList<KmAnnotation>()
+
+        override fun visitAnnotation(annotation: KmAnnotation) { annotations += annotation }
+
+        fun accept(v: JvmTypeParameterExtensionVisitor) {
+            annotations.forEach { v.visitAnnotation(it) }
+            v.visitEnd()
+        }
+    }
+
+    private class TypeParameterNode(
+        val flags: Flags,
+        val name: String,
+        val id: Int,
+        val variance: KmVariance
+    ) : KmTypeParameterVisitor() {
+        private val upperBounds = ArrayList<FlagsTypeNode>()
+        private var extension: TypeParameterExtensionNode? = null
+
+        override fun visitUpperBound(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { upperBounds += it }
+
+        override fun visitExtensions(type: KmExtensionType): KmTypeParameterExtensionVisitor? {
+            check(type == JvmTypeParameterExtensionVisitor.TYPE)
+            check(extension == null)
+            return TypeParameterExtensionNode().also { extension = it }
+        }
+
+        fun accept(v: KmTypeParameterVisitor) {
+            upperBounds.forEach { it.accept(v.visitUpperBound(it.flags)!!) }
+            extension?.accept(v.visitExtensions(JvmTypeParameterExtensionVisitor.TYPE) as JvmTypeParameterExtensionVisitor)
+            v.visitEnd()
+        }
+    }
+
+    private class ValueParameterNode(
+        val flags: Flags,
+        val name: String
+    ) : KmValueParameterVisitor() {
+        private val types = ArrayList<FlagsTypeNode>()
+        private val varargs = ArrayList<FlagsTypeNode>()
+
+        override fun visitType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { types += it }
+
+        override fun visitVarargElementType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { varargs += it }
+
+        fun accept(v: KmValueParameterVisitor) {
+            types.forEach { it.accept(v.visitType(it.flags)!!) }
+            varargs.forEach { it.accept(v.visitVarargElementType(it.flags)!!) }
+            v.visitEnd()
+        }
+    }
+
+    private class TypeExtensionNode : JvmTypeExtensionVisitor() {
+        private var isRaw: Boolean? = null
+        private val annotations = ArrayList<KmAnnotation>()
+
+        override fun visit(isRaw: Boolean) { this.isRaw = isRaw }
+        override fun visitAnnotation(annotation: KmAnnotation) { annotations += annotation }
+
+        fun accept(v: JvmTypeExtensionVisitor) {
+            isRaw?.let { v.visit(it) }
+            annotations.forEach { v.visitAnnotation(it) }
+            v.visitEnd()
+        }
+    }
+
+    private class FlagsTypeNode(
+        val flags: Flags
+    ) : TypeNode()
+
+    private class FlagsVarianceTypeNode(
+        val flags: Flags,
+        val variance: KmVariance
+    ) : TypeNode()
+
+    private class FlagsStringTypeNode(
+        val flags: Flags,
+        val string: String?
+    ) : TypeNode()
+
+    private open class TypeNode : KmTypeVisitor() {
+        private val classNames = ArrayList<ClassName>()
+        private val typeAliases = ArrayList<ClassName>()
+        private val typeParameters = ArrayList<Int>()
+        private val arguments = ArrayList<FlagsVarianceTypeNode>()
+        private var startProjection = false
+        private val abbreviatedTypes = ArrayList<FlagsTypeNode>()
+        private val outerTypes = ArrayList<FlagsTypeNode>()
+        private val flexibleTypeUpperBounds = ArrayList<FlagsStringTypeNode>()
+        private var extension: TypeExtensionNode? = null
+
+        override fun visitClass(name: ClassName) {
+            classNames += name
+        }
+
+        override fun visitTypeAlias(name: ClassName) {
+            typeAliases += name
+        }
+
+        override fun visitTypeParameter(id: Int) {
+            typeParameters += id
+        }
+
+        override fun visitArgument(flags: Flags, variance: KmVariance): KmTypeVisitor? =
+            FlagsVarianceTypeNode(flags, variance).also { arguments += it }
+
+        override fun visitStarProjection() {
+            startProjection = true
+        }
+
+        override fun visitAbbreviatedType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { abbreviatedTypes += it }
+
+        override fun visitOuterType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { outerTypes += it }
+
+        override fun visitFlexibleTypeUpperBound(flags: Flags, typeFlexibilityId: String?): KmTypeVisitor? =
+            FlagsStringTypeNode(flags, typeFlexibilityId).also { flexibleTypeUpperBounds += it }
+
+        override fun visitExtensions(type: KmExtensionType): KmTypeExtensionVisitor? {
+            check(type == JvmTypeExtensionVisitor.TYPE)
+            check(extension == null)
+            return TypeExtensionNode().also { extension = it }
+        }
+
+        fun accept(v: KmTypeVisitor) {
+            classNames.forEach { v.visitClass(it) }
+            typeAliases.forEach { v.visitTypeAlias(it) }
+            typeParameters.forEach { v.visitTypeParameter(it) }
+            arguments.forEach { it.accept(v.visitArgument(it.flags, it.variance)!!) }
+            if (startProjection) v.visitStarProjection()
+            abbreviatedTypes.forEach { it.accept(v.visitAbbreviatedType(it.flags)!!) }
+            outerTypes.forEach { it.accept(v.visitOuterType(it.flags)!!) }
+            flexibleTypeUpperBounds.forEach { it.accept(v.visitFlexibleTypeUpperBound(it.flags, it.string)!!) }
+            extension?.accept(v.visitExtensions(JvmTypeExtensionVisitor.TYPE) as JvmTypeExtensionVisitor)
+            v.visitEnd()
+        }
+    }
+
+    private class FlagsInt(
+        val flags: Flags,
+        val int: Int?
+    )
+
+    private class EffectExpressionNode : KmEffectExpressionVisitor() {
+        private val data = ArrayList<FlagsInt>()
+        private val andArgs = ArrayList<EffectExpressionNode>()
+        private val orArgs = ArrayList<EffectExpressionNode>()
+        private val consts = ArrayList<Any?>()
+        private val types = ArrayList<FlagsTypeNode>()
+
+        override fun visit(flags: Flags, parameterIndex: Int?) {
+            data += FlagsInt(flags, parameterIndex)
+        }
+
+        override fun visitAndArgument(): KmEffectExpressionVisitor? =
+            EffectExpressionNode().also { andArgs += it }
+
+        override fun visitOrArgument(): KmEffectExpressionVisitor? =
+            EffectExpressionNode().also { orArgs += it }
+
+        override fun visitConstantValue(value: Any?) {
+            consts += value
+        }
+
+        override fun visitIsInstanceType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { types += it }
+
+        fun accept(v: KmEffectExpressionVisitor) {
+            data.forEach { v.visit(it.flags, it.int) }
+            andArgs.forEach { it.accept(v.visitAndArgument()!!) }
+            orArgs.forEach { it.accept(v.visitOrArgument()!!) }
+            consts.forEach { v.visitConstantValue(it) }
+            types.forEach { it.accept(v.visitIsInstanceType(it.flags)!!) }
+            v.visitEnd()
+        }
+    }
+
+    private class EffectNode(
+        val type: KmEffectType,
+        val invocationKind: KmEffectInvocationKind?
+    ) : KmEffectVisitor() {
+        private val conclusionOfConditionalEffects = ArrayList<EffectExpressionNode>()
+        private val constructorArguments = ArrayList<EffectExpressionNode>()
+
+        override fun visitConclusionOfConditionalEffect(): KmEffectExpressionVisitor? =
+            EffectExpressionNode().also { conclusionOfConditionalEffects += it }
+
+        override fun visitConstructorArgument(): KmEffectExpressionVisitor? =
+            EffectExpressionNode().also { constructorArguments += it }
+
+        fun accept(v: KmEffectVisitor) {
+            conclusionOfConditionalEffects.forEach { it.accept(v.visitConclusionOfConditionalEffect()!!) }
+            constructorArguments.forEach { it.accept(v.visitConstructorArgument()!!) }
+            v.visitEnd()
+        }
+    }
+
+    private class ContractNode : KmContractVisitor() {
+        private val effects = ArrayList<EffectNode>()
+
+        override fun visitEffect(type: KmEffectType, invocationKind: KmEffectInvocationKind?): KmEffectVisitor? =
+            EffectNode(type, invocationKind).also { effects += it }
+
+        fun accept(v: KmContractVisitor) {
+            effects.forEach { it.accept(v.visitEffect(it.type, it.invocationKind)!!) }
+            v.visitEnd()
+        }
+    }
+
+    private class PropertyExtensionNode : JvmPropertyExtensionVisitor() {
+        var fieldDesc: JvmFieldSignature? = null
+        private var getterDesc: JvmMethodSignature? = null
+        private var setterDesc: JvmMethodSignature? = null
+        private var syntheticMethodForAnnotationsDesc: JvmMethodSignature? = null
+
+        override fun visit(
+            fieldDesc: JvmFieldSignature?,
+            getterDesc: JvmMethodSignature?,
+            setterDesc: JvmMethodSignature?
+        ) {
+            check(this.fieldDesc == null && this.getterDesc == null && this.setterDesc == null)
+            this.fieldDesc = fieldDesc
+            this.getterDesc = getterDesc
+            this.setterDesc = setterDesc
+        }
+
+        override fun visitSyntheticMethodForAnnotations(desc: JvmMethodSignature?) {
+            check(syntheticMethodForAnnotationsDesc == null)
+            this.syntheticMethodForAnnotationsDesc = desc
+        }
+
+        fun accept(v : JvmPropertyExtensionVisitor) {
+            if (fieldDesc != null || getterDesc != null || setterDesc != null) {
+                v.visit(fieldDesc, getterDesc, setterDesc)
+            }
+            syntheticMethodForAnnotationsDesc?.let { v.visitSyntheticMethodForAnnotations(it) }
+            v.visitEnd()
+        }
+    }
+
+    private class FunctionExtensionNode : JvmFunctionExtensionVisitor() {
+        var desc: JvmMethodSignature? = null
+        private var originalInternalName: String? = null
+
+        override fun visit(desc: JvmMethodSignature?) {
+            check(this.desc == null)
+            this.desc = desc
+        }
+
+        override fun visitLambdaClassOriginName(internalName: String) {
+            check(originalInternalName == null)
+            originalInternalName = internalName
+        }
+
+        fun accept(v : JvmFunctionExtensionVisitor) {
+            desc?.let { v.visit(it) }
+            originalInternalName?.let { v.visitLambdaClassOriginName(it) }
+            v.visitEnd()
+        }
+    }
+
+    private inner class PropertyFilterNode(
+        private val v: KmPropertyVisitor
+    ) : KmPropertyVisitor() {
+        private val typeParameters = ArrayList<TypeParameterNode>()
+        private val receiverParameterTypes = ArrayList<FlagsTypeNode>()
+        private val returnTypes = ArrayList<FlagsTypeNode>()
+        private val valueParameters = ArrayList<ValueParameterNode>()
+        private val versionRequirements = ArrayList<VersionRequirementNode>()
+        private var extension: PropertyExtensionNode? = null
+
+        override fun visitTypeParameter(
+            flags: Flags, name: String, id: Int, variance: KmVariance
+        ): KmTypeParameterVisitor? =
+            TypeParameterNode(flags, name, id, variance).also { typeParameters += it }
+
+        override fun visitReceiverParameterType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { receiverParameterTypes += it }
+
+        override fun visitReturnType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { returnTypes += it }
+
+        override fun visitSetterParameter(flags: Flags, name: String): KmValueParameterVisitor? =
+            ValueParameterNode(flags, name).also { valueParameters += it }
+
+        override fun visitVersionRequirement(): KmVersionRequirementVisitor? =
+            VersionRequirementNode().also { versionRequirements += it }
+
+        override fun visitExtensions(type: KmExtensionType): KmPropertyExtensionVisitor? {
+            check(type == JvmPropertyExtensionVisitor.TYPE)
+            check(extension == null)
+            return PropertyExtensionNode().also { extension = it }
+        }
+
+        override fun visitEnd() {
+            if (extension?.fieldDesc in removeFieldSignatures) {
+                // remove this function
+                transformed = true
+                return
+            }
+            // keeping this function
+            typeParameters.forEach { it.accept(v.visitTypeParameter(it.flags, it.name, it.id, it.variance)!!) }
+            receiverParameterTypes.forEach { it.accept(v.visitReceiverParameterType(it.flags)!!) }
+            returnTypes.forEach { it.accept(v.visitReturnType(it.flags)!!) }
+            valueParameters.forEach { it.accept(v.visitSetterParameter(it.flags, it.name)!!) }
+            versionRequirements.forEach { it.accept(v.visitVersionRequirement()!!) }
+            extension?.accept(v.visitExtensions(JvmPropertyExtensionVisitor.TYPE) as JvmPropertyExtensionVisitor)
+            v.visitEnd()
+        }
+    }
+
+    private inner class FunctionFilterNode(
+        private val v: KmFunctionVisitor
+    ) : KmFunctionVisitor() {
+        private val typeParameters = ArrayList<TypeParameterNode>()
+        private val receiverParameterTypes = ArrayList<FlagsTypeNode>()
+        private val valueParameters = ArrayList<ValueParameterNode>()
+        private val returnTypes = ArrayList<FlagsTypeNode>()
+        private val contracts = ArrayList<ContractNode>()
+        private val versionRequirements = ArrayList<VersionRequirementNode>()
+        private var extension: FunctionExtensionNode? = null
+
+        override fun visitTypeParameter(
+            flags: Flags, name: String, id: Int, variance: KmVariance
+        ): KmTypeParameterVisitor? =
+            TypeParameterNode(flags, name, id, variance).also { typeParameters += it }
+
+        override fun visitReceiverParameterType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { receiverParameterTypes += it }
+
+        override fun visitValueParameter(flags: Flags, name: String): KmValueParameterVisitor? =
+            ValueParameterNode(flags, name).also { valueParameters += it }
+
+        override fun visitReturnType(flags: Flags): KmTypeVisitor? =
+            FlagsTypeNode(flags).also { returnTypes += it }
+
+        override fun visitContract(): KmContractVisitor? =
+            ContractNode().also { contracts += it }
+
+        override fun visitVersionRequirement(): KmVersionRequirementVisitor? =
+            VersionRequirementNode().also { versionRequirements += it }
+
+        override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? {
+            check(type == JvmFunctionExtensionVisitor.TYPE)
+            check(extension == null)
+            return FunctionExtensionNode().also { extension = it }
+        }
+
+        override fun visitEnd() {
+            if (extension?.desc in removeMethodSignatures) {
+                // remove this function
+                transformed = true
+                return
+            }
+            // keeping this function
+            typeParameters.forEach { it.accept(v.visitTypeParameter(it.flags, it.name, it.id, it.variance)!!) }
+            receiverParameterTypes.forEach { it.accept(v.visitReceiverParameterType(it.flags)!!) }
+            valueParameters.forEach { it.accept(v.visitValueParameter(it.flags, it.name)!!) }
+            returnTypes.forEach { it.accept(v.visitReturnType(it.flags)!!) }
+            contracts.forEach { it.accept(v.visitContract()!!) }
+            versionRequirements.forEach { it.accept(v.visitVersionRequirement()!!) }
+            extension?.accept(v.visitExtensions(JvmFunctionExtensionVisitor.TYPE) as JvmFunctionExtensionVisitor)
+            v.visitEnd()
+        }
+    }
+}
+
+@Suppress("UNCHECKED_CAST")
+private fun AnnotationNode.asMap(): Map<String, Any?> {
+    val result = HashMap<String, Any?>()
+    for (i in 0 until values.size step 2) {
+        result.put(values[i] as String, values[i + 1])
+    }
+    return result
+}
+
+private fun AnnotationNode.setKey(key: String, value: Any?) {
+    for (i in 0 until values.size step 2) {
+        if (values[i] == key) {
+            values[i + 1] = value
+            return
+        }
+    }
+    error("Annotation key '$key' is not found")
+}
\ No newline at end of file
diff --git a/atomicfu/build.gradle b/atomicfu/build.gradle
index e8dad01..9b622a0 100644
--- a/atomicfu/build.gradle
+++ b/atomicfu/build.gradle
@@ -60,6 +60,7 @@
         }
         jvmTest {
             dependencies {
+                implementation 'org.jetbrains.kotlin:kotlin-reflect'
                 implementation 'org.jetbrains.kotlin:kotlin-test'
                 implementation 'org.jetbrains.kotlin:kotlin-test-junit'
                 implementation "junit:junit:$junit_version"
diff --git a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/LockTest.kt b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/LockTest.kt
index 01a8d09..0195430 100644
--- a/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/LockTest.kt
+++ b/atomicfu/src/commonTest/kotlin/kotlinx/atomicfu/test/LockTest.kt
@@ -20,5 +20,10 @@
     }
 }
 
+// This function will be removed by transformer
 @Suppress("NOTHING_TO_INLINE")
 private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true)
+
+// This function is here to test if the Kotlin metadata still consistent after transform
+// It is used in ReflectionTest, DO NOT REMOVE
+fun <AA, BB : Number> String.reflectionTest(mapParam: Map<in AA, BB>): List<BB> = error("no impl")
diff --git a/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTest.kt b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTest.kt
new file mode 100644
index 0000000..22dbfe5
--- /dev/null
+++ b/atomicfu/src/jvmTest/kotlin/kotlinx/atomicfu/test/ReflectionTest.kt
@@ -0,0 +1,38 @@
+package kotlinx.atomicfu.test
+
+import org.junit.Test
+import kotlin.reflect.*
+import kotlin.reflect.full.*
+import kotlin.reflect.jvm.*
+import kotlin.test.*
+
+/**
+ * Make sure metadata is intact after transformation.
+ */
+class ReflectionTest {
+    @Test
+    fun testReflection() {
+        val f =
+            Class.forName("kotlinx.atomicfu.test.LockTestKt")
+                .methods
+                .single { it.name == "reflectionTest" }
+                .kotlinFunction ?: error("Kotlin function is not found")
+        assertEquals(2, f.typeParameters.size)
+        val tp0 = f.typeParameters[0]
+        assertEquals("AA", tp0.name)
+        val tp1 = f.typeParameters[1]
+        assertEquals("BB", tp1.name)
+        val r = f.extensionReceiverParameter
+            ?: error("extensionReceiverParameter not found")
+        assertEquals("kotlin.String", r.type.toString())
+        assertEquals(2, f.parameters.size)
+        val p = f.parameters[1]
+        assertEquals("mapParam", p.name)
+        assertEquals("class kotlin.collections.Map", p.type.classifier.toString())
+        assertEquals(2, p.type.arguments.size)
+        val ta0 = p.type.arguments[0]
+        assertEquals(KVariance.IN, ta0.variance)
+        val ta0c = ta0.type!!.classifier as KTypeParameter
+        assertEquals("AA", ta0c.name)
+    }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 98443d5..b2d3614 100644
--- a/build.gradle
+++ b/build.gradle
@@ -53,6 +53,7 @@
         jcenter()
         maven { url "https://kotlin.bintray.com/kotlin-eap" }
         maven { url "https://kotlin.bintray.com/kotlin-dev" }
+        maven { url "https://kotlin.bintray.com/kotlinx" }
     }
 
     def deployVersion = properties['DeployVersion']
diff --git a/gradle/compile-options.gradle b/gradle/compile-options.gradle
index 16441f0..fa387b1 100644
--- a/gradle/compile-options.gradle
+++ b/gradle/compile-options.gradle
@@ -6,8 +6,8 @@
 ext.configureKotlin = { type ->
     tasks.withType(type).all {
         kotlinOptions {
-            apiVersion = "1.2"
-            languageVersion = "1.2"
+            apiVersion = "1.3"
+            languageVersion = "1.3"
         }
     }
 }