Fix JVM cinterop callbacks when loaded with custom classloader (#3411)
This commit is contained in:
committed by
GitHub
parent
8cf66bb02e
commit
25820bd227
@@ -1,3 +1,4 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <jni.h>
|
||||
@@ -163,10 +164,12 @@ static void checkException(JNIEnv *env) {
|
||||
static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
|
||||
JNIEnv* env = getCurrentEnv();
|
||||
|
||||
static jmethodID ffiFunImpl0 = NULL;
|
||||
static jmethodID acceptFun = NULL;
|
||||
static jclass cls = NULL;
|
||||
if (ffiFunImpl0 == NULL) {
|
||||
jclass clsLocal = (*env)->FindClass(env, "kotlinx/cinterop/JvmCallbacksKt");
|
||||
if (acceptFun == NULL) {
|
||||
// Note: in some cases [FindClass] below may use a classloader different from the one loaded interop classes,
|
||||
// so stick to JVM-provided class:
|
||||
jclass clsLocal = (*env)->FindClass(env, "java/util/function/LongConsumer");
|
||||
checkException(env);
|
||||
assert(clsLocal != NULL);
|
||||
|
||||
@@ -174,12 +177,13 @@ static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
|
||||
checkException(env);
|
||||
assert(cls != NULL);
|
||||
|
||||
ffiFunImpl0 = (*env)->GetStaticMethodID(env, cls, "ffiFunImpl0", "(JJJLjava/lang/Object;)V");
|
||||
acceptFun = (*env)->GetMethodID(env, cls, "accept", "(J)V");
|
||||
checkException(env);
|
||||
assert(ffiFunImpl0 != NULL);
|
||||
assert(acceptFun != NULL);
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, cls, ffiFunImpl0, (jlong) cif, (jlong) ret, (jlong) args, (jobject) user_data);
|
||||
jlong retAndArgs[2] = { (jlong)ret, (jlong)args }; // Unpacked in [ffiClosureImpl].
|
||||
(*env)->CallVoidMethod(env, (jobject) user_data, acceptFun, (jlong)(intptr_t)&retAndArgs[0]);
|
||||
checkException(env);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package kotlinx.cinterop
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.function.LongConsumer
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KType
|
||||
@@ -275,8 +276,18 @@ private inline fun ffiClosureImpl(
|
||||
returnType: CType<Any?>,
|
||||
crossinline invoke: (args: CArrayPointer<COpaquePointerVar>) -> Any?
|
||||
): FfiClosureImpl {
|
||||
return { ret, args ->
|
||||
// Called through [ffi_fun] when a native function created with [ffiCreateClosure] is invoked.
|
||||
return LongConsumer { retAndArgsRaw ->
|
||||
val retAndArgs = retAndArgsRaw.toCPointer<CPointerVar<*>>()!!
|
||||
|
||||
// Pointer to memory to be filled with return value of the invoked native function:
|
||||
val ret = retAndArgs[0]!!
|
||||
|
||||
// Pointer to array of pointers to arguments passed to the invoked native function:
|
||||
val args = retAndArgs[1]!!.reinterpret<COpaquePointerVar>()
|
||||
|
||||
val result = invoke(args)
|
||||
|
||||
returnType.write(ret.rawValue, result)
|
||||
}
|
||||
}
|
||||
@@ -357,7 +368,7 @@ private class CEnumType(private val rawValueCType: CType<Any>) : CType<CEnum>(ra
|
||||
}
|
||||
}
|
||||
|
||||
private typealias FfiClosureImpl = (ret: COpaquePointer, args: CArrayPointer<COpaquePointerVar>)->Unit
|
||||
private typealias FfiClosureImpl = LongConsumer
|
||||
private typealias UserData = FfiClosureImpl
|
||||
|
||||
private fun loadCallbacksLibrary() {
|
||||
@@ -429,29 +440,10 @@ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_
|
||||
return interpretPointed(res)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER", "UNUSED")
|
||||
private fun ffiFunImpl0(ffiCif: Long, ret: Long, args: Long, userData: Any) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
ffiFunImpl(interpretCPointer(ret)!!,
|
||||
interpretCPointer(args)!!,
|
||||
userData as UserData)
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called from native code when a native function created with [ffiCreateClosure] is invoked.
|
||||
*
|
||||
* @param ret pointer to memory to be filled with return value of the invoked native function
|
||||
* @param args pointer to array of pointers to arguments passed to the invoked native function
|
||||
*/
|
||||
private fun ffiFunImpl(ret: COpaquePointer, args: CArrayPointer<COpaquePointerVar>, userData: UserData) {
|
||||
|
||||
userData.invoke(ret, args)
|
||||
}
|
||||
|
||||
private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
|
||||
|
||||
/**
|
||||
* Uses libffi to allocate a native function which will call [ffiFunImpl] when invoked.
|
||||
* Uses libffi to allocate a native function which will call [impl] when invoked.
|
||||
*
|
||||
* @param ffiCif describes the type of the function to create
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user