Fix JVM cinterop callbacks when loaded with custom classloader (#3411)

This commit is contained in:
SvyatoslavScherbina
2019-10-04 16:07:42 +03:00
committed by GitHub
parent 8cf66bb02e
commit 25820bd227
2 changed files with 24 additions and 28 deletions
+10 -6
View File
@@ -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
*/