AFU's JVM bytecode transformer assumes that if an atomic field was loaded and then stored into a local variable, then there should be a load from that local variable somewhere. This assumption is wrong an in some specific cases the store into a local variable is the last interaction with the atomic field. That happens, for example, when calling an inline extension function on the atomic field that does not use the receiver:
internal class Seg
internal class Head {
val tail = atomic(Seg())
fun bogus() {
tail.find { }
}
}
@Suppress("NOTHING_TO_INLINE")
internal inline fun AtomicRef<Seg>.find(noinline callback: () -> Unit) {
findInternal(callback)
}
internal fun findInternal(callback: () -> Unit): Unit {}
This function compiles down to:
Code:
stack=1, locals=4, args_size=1
0: aload_0
1: getfield #19 // Field tail:Lkotlinx/atomicfu/AtomicRef;
4: astore_1 // <= storing the atomic field into a local variable, but never use it afterwards
5: invokedynamic #45, 0 // InvokeDynamic #0:invoke:()Lkotlin/jvm/functions/Function0;
10: astore_2
11: iconst_0
12: istore_3
13: aload_2
14: invokestatic #51 // Method kotlinx/atomicfu/test/BogusCodeKt.findInternal:(Lkotlin/jvm/functions/Function0;)V
17: nop
18: return
The transformer is not ready for that, and fails with NPE while searching for a load following the store:
Exception in thread "main" java.lang.NullPointerException
at kotlinx.atomicfu.transformer.AtomicFUTransformer$DelegatedFieldAccessorCollectorMV.visitEnd(AtomicFUTransformer.kt:424)
at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1516)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:745)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:425)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.analyzeFileForFields(AtomicFUTransformer.kt:234)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.analyzeFilesForFields(AtomicFUTransformer.kt:227)
at kotlinx.atomicfu.transformer.AtomicFUTransformer.transform(AtomicFUTransformer.kt:196)
at kotlinx.atomicfu.transformer.AtomicFUTransformerKt.main(AtomicFUTransformer.kt:1549)
AFU's JVM bytecode transformer assumes that if an atomic field was loaded and then stored into a local variable, then there should be a load from that local variable somewhere. This assumption is wrong an in some specific cases the store into a local variable is the last interaction with the atomic field. That happens, for example, when calling an inline extension function on the atomic field that does not use the receiver:
This function compiles down to:
The transformer is not ready for that, and fails with NPE while searching for a load following the store: