In function f
I want to sum two values a.x
and b.x
:
class C<T>(var x: T)
inline fun <reified T> f(a: C<T>, b: C<T>): T? {
val c = T::class
val members = c.members.groupBy { it.name }
for (m in members["plus"]!!) {
if (m.parameters[1].type == Int::class.createType()) {
return m.call(a.x, b.x) as T?
}
}
return null
}
fun main(args: Array<String>) {
val a = C(1)
val b = C(2)
println(f(a, b))
}
Error:
Exception in thread "main" kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: @kotlin.internal.IntrinsicConstEvaluation public final operator fun plus(other: kotlin.Int): kotlin.Int defined in kotlin.Int[DeserializedSimpleFunctionDescriptor@2f4205be] (member = null)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:98)
at kotlin.reflect.jvm.internal.KFunctionImpl$caller$2.invoke(KFunctionImpl.kt:64)
at kotlin.SafePublicationLazyImpl.getValue(LazyJVM.kt:107)
at kotlin.reflect.jvm.internal.KFunctionImpl.getCaller(KFunctionImpl.kt:64)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108)
at MainKt.main(Main.kt:66)
This is currently not possible. Int::plus
doesn't actually exist, in the sense that there is no JVM method corresponding to it. You cannot call Int::plus
.
When something like someInt + anotherInt
is compiled, it doesn't get translated to a call to Int::plus
. It's just translated to an iadd
JVM instruction directly.
At the end of the day, Kotlin reflection tries to find a corresponding JVM method, so when it cannot find one, it throws a KotlinReflectionInternalError
. See the implementation in KFunctionImpl
val member: Member? = when (val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)) {
// ...
is KotlinFunction -> {
// ...
container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc) as Member?
}
// ...
}
when (member) {
is Constructor<*> -> // ...
is Method -> // ...
else -> throw KotlinReflectionInternalError("Could not compute caller for function: $descriptor (member = $member)")
}.createValueClassAwareCallerIfNeeded(descriptor)
You can see that the Kotlin function is first mapped to its JVM signature, and then container.findMethodBySignature
is used to find a JVM method with that signature in Int
. Such a method doesn't exist, so container.findMethodBySignature
returns null, and the exception is thrown in the second when
.
There are already bug reports reporting this issue. See KT-13077 (and its related issues) for example. The specific case mentioned in KT-13077 (Int::toByte
) does have something sort of like a JVM counterpart (java.lang.Integer::byteValue
), and also they are using a callable reference expression, which makes the exception message a bit different from yours.
Either way, this is something Kotlin reflection cannot do yet.