I noticed an inconsistent behavior in reflection methods when trying to access some static properties of types in java.lang
via reflection.
I’ll provide further details below.
The main question here is: am I missing something or is there some bug in the reflection library?
Details
On Kotlin JVM Kotlin, some basic types (e.g. Long
, Int
, etc) come with a companion object letting developers access static properties and methods of the corresponding JVM types.
Accordingly, Kotlin reflection lets developers access such a companion object, as it would for any Kotlin class exposing a companion object.
However, when accessing some properties, exceptions are thrown unexpectedly.
Consider for instance the case of Int.MAX_VALUE
:
val member = Int::class.companionObject?.members?.firstOrNull { it.name == "MAX_VALUE" }
assertEquals(Int.MAX_VALUE, member?.call()) // throws kotlin.reflect.jvm.internal.KotlinReflectionInternalError:
// No accessor found for property val kotlin.Int.Companion.MAX_VALUE: kotlin.Int
A similar issue occurs when accessing Int.MIN_VALUE
as a property:
val property = Int::class.companionObject?.memberProperties?.firstOrNull { it.name == "MIN_VALUE" }
assertEquals(Int.MIN_VALUE, property?.call()) // throws kotlin.reflect.jvm.internal.KotlinReflectionInternalError:
// No accessor found for property val kotlin.Int.Companion.MIN_VALUE: kotlin.Int
Notably, a similar issue occurs when accessing Array.size
, even if it does not corresponds to a static method:
val member = Array::class.members.firstOrNull { it.name == "size" }
val instance = arrayOf("A")
assertEquals(instance.size, member?.call(instance)) // kotlin.reflect.jvm.internal.KotlinReflectionInternalError:
// No accessor found for property val kotlin.Array<T>.size: kotlin.Int
1 Like
While reproducing this and trying to change the code minimally I stumbled upon the following few examples with varying success.
Array<T>::size.get(instance: Array<T>)
breaking on the JVM exclusively with a NoSuchMethodError
:
println(Array<String>::lastIndex.get(arrayOf("A")))
println(Array<String>::size.get(arrayOf("A")))
Running this in the JVM on the Kotlin Playground (or locally) produces the following output:
0
Exception in thread "main" java.lang.NoSuchMethodError: [Ljava.lang.Object;.getSize()I
at FileKt$main$2.get (File.kt:8)
at FileKt.main (File.kt:8)
at FileKt.main (File.kt:-1)
Running this in JS on the Kotlin Playground works as expected.
Calling the getters for Int.Companion.MIN_VALUE
and Int.Companion.MAX_VALUE
works by calling KProperty0<T>.get()
, but not by KProperty0<T>.getter.call()
, which I would have assumed to produce identical output:
val minValue: KProperty0<Int> = Int.Companion::MIN_VALUE
println(minValue.get())
println(minValue.getter.call())
Running this in the JVM produces the following output:
-2147483648
kotlin.reflect.jvm.internal.KotlinReflectionInternalError: No accessor found for property val kotlin.Int.Companion.MIN_VALUE: kotlin.Int
at kotlin.reflect.jvm.internal.KPropertyImplKt.computeCallerForAccessor(KPropertyImpl.kt:273)
at kotlin.reflect.jvm.internal.KPropertyImplKt.access$computeCallerForAccessor(KPropertyImpl.kt:1)
at kotlin.reflect.jvm.internal.KPropertyImpl$Getter$caller$2.invoke(KPropertyImpl.kt:156)
at kotlin.reflect.jvm.internal.KPropertyImpl$Getter$caller$2.invoke(KPropertyImpl.kt:147)
at kotlin.reflect.jvm.internal.ReflectProperties$LazyVal.invoke(ReflectProperties.java:62)
at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
at kotlin.reflect.jvm.internal.KPropertyImpl$Getter.getCaller(KPropertyImpl.kt)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108)
...
I would assume a bug in either the compiler or the reflection library - have you created an issue yet?
1 Like