From a50b38da5a4f136fb7921f26a8f6a520c4166ac0 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Thu, 30 Mar 2017 14:03:05 +0300 Subject: [PATCH] Implement `StableObjPtr` for Kotlin N --- INTEROP.md | 37 +++++++++++++++ .../kotlinx/cinterop/NativeCallbacks.kt | 46 +++++++++++++++++++ .../native/kotlin/kotlinx/cinterop/Varargs.kt | 2 +- runtime/src/main/cpp/Interop.cpp | 20 +++++++- 4 files changed, 103 insertions(+), 2 deletions(-) diff --git a/INTEROP.md b/INTEROP.md index 430f11a99db..cd337aa3558 100644 --- a/INTEROP.md +++ b/INTEROP.md @@ -261,6 +261,43 @@ Note that some function types are not supported currently. For example, it is not possible to get pointer to function that receives or returns structs by value. +#### Passing user data to callbacks #### + +Often C APIs allow passing some user data to callbacks. Such data is usually +provided by user when configuring the callback. It is passed to some C function +(or written to the struct) as e.g. `void*`. +However references to Kotlin objects can't be directly passed to C. +So they require wrapping before configuring callback and then unwrapping in +the callback itself, to safely swim from Kotlin to Kotlin through the C world. +Such wrapping is possible with `StableObjPtr` class. + +To wrap the reference: +``` +val stablePtr = StableObjPtr.create(kotlinReference) +val voidPtr = stablePtr.value +``` +where the `voidPtr` is `COpaquePointer` and can be passed to the C function. + +To unwrap the reference: + +``` +val stablePtr = StableObjPtr.fromValue(voidPtr) +val kotlinReference = stablePtr.get() +``` +where `kotlinReference` is the original wrapped reference (however it's type is +`Any` so it may require casting). + +The created `StableObjPtr` should eventually be manually disposed using +`.dispose()` method to prevent memory leaks: + +``` +stablePtr.dispose() +``` + +After that it becomes invalid, so `voidPtr` can't be unwrapped anymore. + +See `samples/libcurl` for more details. + ### Definition file hints ### The `.def` file supports several options for adjusting generated bindings. diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeCallbacks.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeCallbacks.kt index 96c4e71d772..df8acf07874 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeCallbacks.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeCallbacks.kt @@ -1,5 +1,51 @@ package kotlinx.cinterop +/** + * This class provides a way to create a stable handle to any Kotlin object. + * Its [value] can be safely passed to native code e.g. to be received in a Kotlin callback. + * + * Any [StableObjPtr] should be manually [disposed][dispose] + */ +data class StableObjPtr private constructor(val value: COpaquePointer) { + + companion object { + + /** + * Creates a handle for given object. + */ + fun create(any: Any) = fromValue(createStablePointer(any)) + + /** + * Creates [StableObjPtr] from given raw value. + * + * @param value must be a [value] of some [StableObjPtr] + */ + fun fromValue(value: COpaquePointer) = StableObjPtr(value) + } + + /** + * Disposes the handle. It must not be [used][get] after that. + */ + fun dispose() { + disposeStablePointer(value) + } + + /** + * Returns the object this handle was [created][create] for. + */ + fun get(): Any = derefStablePointer(value) + +} + +@SymbolName("Kotlin_Interop_createStablePointer") +private external fun createStablePointer(any: Any): COpaquePointer + +@SymbolName("Kotlin_Interop_disposeStablePointer") +private external fun disposeStablePointer(pointer: COpaquePointer) + +@SymbolName("Kotlin_Interop_derefStablePointer") +private external fun derefStablePointer(pointer: COpaquePointer): Any + /** * The type of C function which can be constructed from the appropriate Kotlin function without using any adapter. */ diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Varargs.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Varargs.kt index c1c3fa9a1cb..15ae7ec28df 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Varargs.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Varargs.kt @@ -107,7 +107,7 @@ fun callWithVarargs(codePtr: NativePtr, returnValuePtr: NativePtr, returnTypeKin fixedArguments.size, totalArgumentsNumber) } -@SymbolName("callWithVarargs") +@SymbolName("Kotlin_Interop_callWithVarargs") private external fun callWithVarargs(codePtr: NativePtr, returnValuePtr: NativePtr, returnTypeKind: FfiTypeKind, arguments: NativePtr, argumentTypeKinds: NativePtr, fixedArgumentsNumber: Int, totalArgumentsNumber: Int) \ No newline at end of file diff --git a/runtime/src/main/cpp/Interop.cpp b/runtime/src/main/cpp/Interop.cpp index e5c7850f3f2..9affd032205 100644 --- a/runtime/src/main/cpp/Interop.cpp +++ b/runtime/src/main/cpp/Interop.cpp @@ -19,6 +19,9 @@ #include #include +#include "Memory.h" +#include "Types.h" + namespace { typedef int FfiTypeKind; @@ -51,7 +54,7 @@ ffi_type* convertFfiTypeKindToType(FfiTypeKind typeKind) { extern "C" { -void callWithVarargs(void* codePtr, void* returnValuePtr, FfiTypeKind returnTypeKind, +void Kotlin_Interop_callWithVarargs(void* codePtr, void* returnValuePtr, FfiTypeKind returnTypeKind, void** arguments, intptr_t* argumentTypeKinds, int fixedArgumentsNumber, int totalArgumentsNumber) { @@ -70,4 +73,19 @@ void callWithVarargs(void* codePtr, void* returnValuePtr, FfiTypeKind returnType ffi_call(&cif, (void (*)())codePtr, returnValuePtr, arguments); } +void* Kotlin_Interop_createStablePointer(KRef any) { + ::AddRef(any->container()); + return reinterpret_cast(any); +} + +void Kotlin_Interop_disposeStablePointer(void* pointer) { + KRef ref = reinterpret_cast(pointer); + ::Release(ref->container()); +} + +OBJ_GETTER(Kotlin_Interop_derefStablePointer, void* pointer) { + KRef ref = reinterpret_cast(pointer); + RETURN_OBJ(ref); +} + }