Function literals and the JNI

I am currently writing bindings between C++ and Kotlin. Works well so far. I was also able to get Kotlin callbacks in C++ working, but currently, this requires an intermediate callback class, like this:

package example

class ExtraCallbackWrapper(vall callback: (foo: Int) -> Long) {
    fun call(foo: Int): Long = callback(foo)
}

class CppInterface {
    fun invokeCallback(bar: Int, callback: (foo: Int) -> Long) = invokeCallbackImpl(foo, ExtraCallbackWrapper(callback))
    private external fun invokeCallbackImpl(bar: Int, callback: ExtraCallbackWrapper): Long
}

The reason why I need ExtraCallbackWrapper here is that I can access this class in C++ via the JNI using FindClass, like this:

env->FindClass("example/ExtraCallbackWrapper");

“example/ExtraCallbackWrapper” is the fully qualified class name. However, I do not know what the class name would be in the case of a function literal. If instead of having invokeCallbackImpl above, invokeCallback itself were an external function, what would I have to use as class name to be able to work with the “callback” argument from JNI? I know that Kotlin probably turns function literals into classes similar to ExtraCallbackWrapper. Is it possible to get that class name somehow? Or is this something purely internal that isn’t part of a stable ABI and thus can change unexpectedly? If so, any other ideas on how to achieve this without having to use an intermediate class?

EDIT: This guy almost got it working. But he too ran into the problem of an unknown type signature / class name.

Sorry for the dumb question, but I think it needs to be clarified : Why not using Kotlin Native ?

Do you require Java interop ?
Are you woirking with Android NDK ?

Any context inormation to your problem is helpful to get a better understanding of your question and what your trying to achieve.

Otherwise, for this particular problem, I’m wondering if it is possible to make a workaround using SAM conversion. I have no clue if env->FindClass("...") accepts interface as parameter though (but I don’t see any limitation mentioned in official documentation).

Example:

typealias IntToLongFunction = (Int) -> Long

class CppInterface {
    external fun invokeCallback(bar: Int, callback: IntToLongFunction)
}

env->FindClass("example/IntToLongFunction")

The project also uses some Java libraries, which is why I have to use the JNI.

And, unfortunately it seems that the typealias doesn’t work. I get a java.lang.NoSuchMethodError exception.

1 Like

If you use IntelliJ IDE, you can look at the generated bytecode by selecting
Tools > Kotlin > Show Kotlin Bytecode
in the menu. A declaration

external fun invokeCallback(bar: Int, callback: (foo: Int) -> Long)

is shown in the bytecode as

public final native invokeCallback(ILkotlin/jvm/functions/Function1;)V

hence the class you are looking for is kotlin.jvm.functions.Function1.

The approach by typealias cannot work because typealiases have no equivalent in the JVM. The Kotlin compiler replaces any occurrence of a typealias by its definiton way before bytecode generation.

Alright. And I suppose the “1” in Function1 refers to the number of arguments?

From what I see, “kotlin.jvm.functions.Function1” is a Kotlin-internal type, and it is not advisable to rely on its existence or structure in JNI code, right?

2 Likes