Members starting with underscore are not visible through Kotlin reflection

While working with Kotlin coroutines, I’ve stumbled upon behavior I didn’t find documentation for.

EventLoopImplBase, for example, has two private members with names starting with underscore:

Those members doesn’t appear when looking through memberProperties:

fun horribleReflectionOnCoroutineScope(coroutineScope: CoroutineScope) {
    val contextProperty = coroutineScope::class.memberProperties.single { it.name == "context"}
    val context = contextProperty.call(coroutineScope)
    if (context != null) {
        val elementProperty = context::class.memberProperties.single { it.name == "element"}
        elementProperty.isAccessible = true
        val element = elementProperty.call(context)

        if (element != null) {
            val superclass = element::class.superclasses.first()
            superclass.memberProperties.filter { it.name.startsWith("_") } // size 0
       }
    }
    // More code here
}

And the only way to access them is through Java reflection API:

superclass.java.declaredFields.single { it.name == "_queue" }

The only mention of members starting with underscores I’ve found was in backing properties:
https://kotlinlang.org/docs/reference/coding-conventions.html#property-names

But it doesn’t mention anything about affecting reflection.

So, is there some kind of Kotlin reflection rule that ignores properties starting with underscore, or is it something else?

I think this has less to do with underscores and more to do with the fact that those properties you mention are atomic using the kotlinx.atomicfu library/compiler plugin. Seems to me that some compiler magic is going on to ensure that the usage of atomicfu is thoroughly encapsulated.

2 Likes

There are no reasonable limitations here. My best guess is that the class you are inspecting is not actually the one you are expecting to find. You can try to check its name, for example.

As a side not I can say that the entire approach seems to be quite vulnerable. Starting from ignoring static typing virue and finishing with relying on internal types structure. Usually things are made invisible for a reason. I would advide to try to find a more conventional way to do what is really wanted.

Classes seem to be correct, so I’m inclined to accept @Varia’s suggestion.

The code itself is something that will not travel beyond my laptop, so don’t be concerned about its safety.