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 7587e466..ba08fbc3 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 @@ val AbstractInsnNode?.thisOrPrevUseful: AbstractInsnNode? 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 { 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 3263f2f8..59f42f72 100644 --- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformer.kt +++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformer.kt @@ -1320,11 +1320,15 @@ class AtomicFUTransformer( 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() } } } } @@ -1356,6 +1360,10 @@ class AtomicFUTransformer( 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) 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 00000000..3c267815 --- /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