Skip to content

JVM bytecode transformer makes incorrect assumptions about instructions following AFU field's load #582

Description

@fzhinkin

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)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions