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 <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <jni.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) {
|
static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
|
||||||
JNIEnv* env = getCurrentEnv();
|
JNIEnv* env = getCurrentEnv();
|
||||||
|
|
||||||
static jmethodID ffiFunImpl0 = NULL;
|
static jmethodID acceptFun = NULL;
|
||||||
static jclass cls = NULL;
|
static jclass cls = NULL;
|
||||||
if (ffiFunImpl0 == NULL) {
|
if (acceptFun == NULL) {
|
||||||
jclass clsLocal = (*env)->FindClass(env, "kotlinx/cinterop/JvmCallbacksKt");
|
// 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);
|
checkException(env);
|
||||||
assert(clsLocal != NULL);
|
assert(clsLocal != NULL);
|
||||||
|
|
||||||
@@ -174,12 +177,13 @@ static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
|
|||||||
checkException(env);
|
checkException(env);
|
||||||
assert(cls != NULL);
|
assert(cls != NULL);
|
||||||
|
|
||||||
ffiFunImpl0 = (*env)->GetStaticMethodID(env, cls, "ffiFunImpl0", "(JJJLjava/lang/Object;)V");
|
acceptFun = (*env)->GetMethodID(env, cls, "accept", "(J)V");
|
||||||
checkException(env);
|
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);
|
checkException(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package kotlinx.cinterop
|
package kotlinx.cinterop
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.function.LongConsumer
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KFunction
|
import kotlin.reflect.KFunction
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
@@ -275,8 +276,18 @@ private inline fun ffiClosureImpl(
|
|||||||
returnType: CType<Any?>,
|
returnType: CType<Any?>,
|
||||||
crossinline invoke: (args: CArrayPointer<COpaquePointerVar>) -> Any?
|
crossinline invoke: (args: CArrayPointer<COpaquePointerVar>) -> Any?
|
||||||
): FfiClosureImpl {
|
): 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)
|
val result = invoke(args)
|
||||||
|
|
||||||
returnType.write(ret.rawValue, result)
|
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 typealias UserData = FfiClosureImpl
|
||||||
|
|
||||||
private fun loadCallbacksLibrary() {
|
private fun loadCallbacksLibrary() {
|
||||||
@@ -429,29 +440,10 @@ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_
|
|||||||
return interpretPointed(res)
|
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
|
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
|
* @param ffiCif describes the type of the function to create
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user