Skip to content

Commit

Permalink
Implement StableObjPtr for Kotlin N
Browse files Browse the repository at this point in the history
  • Loading branch information
SvyatoslavScherbina committed Apr 3, 2017
1 parent d07295d commit a50b38d
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 2 deletions.
37 changes: 37 additions & 0 deletions INTEROP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
20 changes: 19 additions & 1 deletion runtime/src/main/cpp/Interop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include <stdint.h>
#include <ffi.h>

#include "Memory.h"
#include "Types.h"

namespace {

typedef int FfiTypeKind;
Expand Down Expand Up @@ -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) {

Expand All @@ -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<void*>(any);
}

void Kotlin_Interop_disposeStablePointer(void* pointer) {
KRef ref = reinterpret_cast<KRef>(pointer);
::Release(ref->container());
}

OBJ_GETTER(Kotlin_Interop_derefStablePointer, void* pointer) {
KRef ref = reinterpret_cast<KRef>(pointer);
RETURN_OBJ(ref);
}

}

0 comments on commit a50b38d

Please sign in to comment.