Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding FfiType::Callback #1814

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,14 +274,19 @@ impl KotlinCodeOracle {
}
}

fn ffi_type_label_by_value(ffi_type: &FfiType) -> String {
/// Get the idiomatic Python rendering of an FFI callback function
fn ffi_callback_name(&self, nm: &str) -> String {
format!("Uniffi{}", nm.to_upper_camel_case())
}

fn ffi_type_label_by_value(&self, ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::RustBuffer(_) => format!("{}.ByValue", Self::ffi_type_label(ffi_type)),
_ => Self::ffi_type_label(ffi_type),
FfiType::RustBuffer(_) => format!("{}.ByValue", self.ffi_type_label(ffi_type)),
_ => self.ffi_type_label(ffi_type),
}
}

fn ffi_type_label(ffi_type: &FfiType) -> String {
fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
match ffi_type {
// Note that unsigned integers in Kotlin are currently experimental, but java.nio.ByteBuffer does not
// support them yet. Thus, we use the signed variants to represent both signed and unsigned
Expand All @@ -297,13 +302,11 @@ impl KotlinCodeOracle {
format!("RustBuffer{}", maybe_suffix.as_deref().unwrap_or_default())
}
FfiType::ForeignBytes => "ForeignBytes.ByValue".to_string(),
FfiType::Callback(name) => self.ffi_callback_name(name),
FfiType::ForeignCallback => "ForeignCallback".to_string(),
FfiType::ForeignExecutorHandle => "USize".to_string(),
FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback".to_string(),
FfiType::RustFutureHandle => "Pointer".to_string(),
FfiType::RustFutureContinuationCallback => {
"UniFffiRustFutureContinuationCallbackType".to_string()
}
FfiType::RustFutureContinuationData => "USize".to_string(),
}
}
Expand Down Expand Up @@ -469,11 +472,11 @@ pub mod filters {

/// Get the Kotlin syntax for representing a given low-level `FfiType`.
pub fn ffi_type_name(type_: &FfiType) -> Result<String, askama::Error> {
Ok(KotlinCodeOracle::ffi_type_label(type_))
Ok(KotlinCodeOracle.ffi_type_label(type_))
}

pub fn ffi_type_name_by_value(type_: &FfiType) -> Result<String, askama::Error> {
Ok(KotlinCodeOracle::ffi_type_label_by_value(type_))
Ok(KotlinCodeOracle.ffi_type_label_by_value(type_))
}

// Some FfiTypes have the same ffi_type_label - this makes a vec of them unique.
Expand All @@ -483,7 +486,7 @@ pub mod filters {
let mut seen = HashSet::new();
let mut result = Vec::new();
for t in types {
if seen.insert(KotlinCodeOracle::ffi_type_label(&t)) {
if seen.insert(KotlinCodeOracle.ffi_type_label(&t)) {
result.push(t)
}
}
Expand Down Expand Up @@ -523,6 +526,11 @@ pub mod filters {
.type_label())
}

/// Get the idiomatic Kotlin rendering of an FFI callback function name
pub fn ffi_callback_name(nm: &str) -> Result<String, askama::Error> {
Ok(KotlinCodeOracle.ffi_callback_name(nm))
}

pub fn error_canonical_name(as_type: &impl AsType) -> Result<String, askama::Error> {
Ok(KotlinCodeOracle
.find_as_error(&as_type.as_type())
Expand Down
16 changes: 8 additions & 8 deletions uniffi_bindgen/src/bindings/kotlin/templates/Async.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
// Async return type handlers

internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toShort()
internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toShort()
internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toByte()
internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toByte()

internal val uniffiContinuationHandleMap = UniFfiHandleMap<CancellableContinuation<Short>>()
internal val uniffiContinuationHandleMap = UniFfiHandleMap<CancellableContinuation<Byte>>()

// FFI type for Rust future continuations
internal object uniffiRustFutureContinuationCallback: UniFffiRustFutureContinuationCallbackType {
override fun callback(continuationHandle: USize, pollResult: Short) {
uniffiContinuationHandleMap.remove(continuationHandle)?.resume(pollResult)
internal object uniffiRustFutureContinuationCallback: UniffiRustFutureContinuationCallback {
override fun callback(data: USize, pollResult: Byte) {
uniffiContinuationHandleMap.remove(data)?.resume(pollResult)
}
}

internal suspend fun<T, F, E: Exception> uniffiRustCallAsync(
rustFuture: Pointer,
pollFunc: (Pointer, UniFffiRustFutureContinuationCallbackType, USize) -> Unit,
pollFunc: (Pointer, UniffiRustFutureContinuationCallback, USize) -> Unit,
completeFunc: (Pointer, RustCallStatus) -> F,
freeFunc: (Pointer) -> Unit,
liftFunc: (F) -> T,
errorHandler: CallStatusErrorHandler<E>
): T {
try {
do {
val pollResult = suspendCancellableCoroutine<Short> { continuation ->
val pollResult = suspendCancellableCoroutine<Byte> { continuation ->
pollFunc(
rustFuture,
uniffiRustFutureContinuationCallback,
Expand Down
5 changes: 0 additions & 5 deletions uniffi_bindgen/src/bindings/kotlin/templates/Helpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,3 @@ internal class UniFfiHandleMap<T: Any> {
return map.remove(handle)
}
}

// FFI type for Rust future continuations
internal interface UniFffiRustFutureContinuationCallbackType : com.sun.jna.Callback {
fun callback(continuationHandle: USize, pollResult: Short);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,22 @@ private inline fun <reified Lib : Library> loadIndirect(
return Native.load<Lib>(findLibraryName(componentName), Lib::class.java)
}

// Define FFI callback types
{%- for callback in ci.ffi_callback_definitions() %}
internal interface {{ callback.name()|ffi_callback_name }} : com.sun.jna.Callback {
fun callback(
{%- for arg in callback.arguments() -%}
{{ arg.name().borrow()|var_name }}: {{ arg.type_().borrow()|ffi_type_name }},
{%- endfor -%}
)
{%- match callback.return_type() %}
{%- when Some(return_type) %}: {{ return_type|ffi_type_name }},
{%- when None %}
{%- endmatch %}
}

{%- endfor %}

// A JNA Library to expose the extern-C FFI definitions.
// This is an implementation detail which will be called internally by the public API.

Expand Down
16 changes: 13 additions & 3 deletions uniffi_bindgen/src/bindings/python/gen_python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,12 @@ impl PythonCodeOracle {
fixup_keyword(nm.to_string().to_shouty_snake_case())
}

fn ffi_type_label(ffi_type: &FfiType) -> String {
/// Get the idiomatic Python rendering of an FFI callback function
fn ffi_callback_name(&self, nm: &str) -> String {
format!("UNIFFI_{}", nm.to_shouty_snake_case())
}

fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::Int8 => "ctypes.c_int8".to_string(),
FfiType::UInt8 => "ctypes.c_uint8".to_string(),
Expand All @@ -317,12 +322,12 @@ impl PythonCodeOracle {
None => "_UniffiRustBuffer".to_string(),
},
FfiType::ForeignBytes => "_UniffiForeignBytes".to_string(),
FfiType::Callback(name) => self.ffi_callback_name(name),
FfiType::ForeignCallback => "_UNIFFI_FOREIGN_CALLBACK_T".to_string(),
// Pointer to an `asyncio.EventLoop` instance
FfiType::ForeignExecutorHandle => "ctypes.c_size_t".to_string(),
FfiType::ForeignExecutorCallback => "_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T".to_string(),
FfiType::RustFutureHandle => "ctypes.c_void_p".to_string(),
FfiType::RustFutureContinuationCallback => "_UNIFFI_FUTURE_CONTINUATION_T".to_string(),
FfiType::RustFutureContinuationData => "ctypes.c_size_t".to_string(),
}
}
Expand Down Expand Up @@ -439,7 +444,7 @@ pub mod filters {

/// Get the Python syntax for representing a given low-level `FfiType`.
pub fn ffi_type_name(type_: &FfiType) -> Result<String, askama::Error> {
Ok(PythonCodeOracle::ffi_type_label(type_))
Ok(PythonCodeOracle.ffi_type_label(type_))
}

/// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
Expand All @@ -462,6 +467,11 @@ pub mod filters {
Ok(PythonCodeOracle.enum_variant_name(nm))
}

/// Get the idiomatic Python rendering of a FFI callback function name
pub fn ffi_callback_name(nm: &str) -> Result<String, askama::Error> {
Ok(PythonCodeOracle.ffi_callback_name(nm))
}

/// Get the idiomatic Python rendering of an individual enum variant.
pub fn object_names(obj: &Object) -> Result<(String, String), askama::Error> {
Ok(PythonCodeOracle.object_names(obj))
Expand Down
2 changes: 1 addition & 1 deletion uniffi_bindgen/src/bindings/python/templates/Async.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# Continuation callback for async functions
# lift the return value or error and resolve the future, causing the async function to resume.
@_UNIFFI_FUTURE_CONTINUATION_T
@UNIFFI_RUST_FUTURE_CONTINUATION_CALLBACK
def _uniffi_continuation_callback(future_ptr, poll_code):
(eventloop, future) = _UniffiContinuationPointerManager.release_pointer(future_ptr)
eventloop.call_soon_threadsafe(_uniffi_set_future_result, future, poll_code)
Expand Down
3 changes: 0 additions & 3 deletions uniffi_bindgen/src/bindings/python/templates/Helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,3 @@ def _uniffi_check_call_status(error_ffi_converter, call_status):
# Rust definition `fn(handle: u64, method: u32, args: _UniffiRustBuffer, buf_ptr: *mut _UniffiRustBuffer) -> int`
_UNIFFI_FOREIGN_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_ulonglong, ctypes.c_ulong, ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.POINTER(_UniffiRustBuffer))

# UniFFI future continuation
_UNIFFI_FUTURE_CONTINUATION_T = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_int8)

Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ def _uniffi_check_api_checksums(lib):
pass
{%- endfor %}


# Define FFI callback types
{%- for callback in ci.ffi_callback_definitions() %}
{{ callback.name()|ffi_callback_name }} = ctypes.CFUNCTYPE(
{%- match callback.return_type() %}
{%- when Some(return_type) %}{{ return_type|ffi_type_name }},
{%- when None %}None,
{%- endmatch %}
{%- for arg in callback.arguments() -%}
{{ arg.type_().borrow()|ffi_type_name }},
{%- endfor -%}
)
{%- endfor %}

# A ctypes library to expose the extern-C FFI definitions.
# This is an implementation detail which will be called internally by the public API.

Expand Down
5 changes: 2 additions & 3 deletions uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ mod filters {
FfiType::RustArcPtr(_) => ":pointer".to_string(),
FfiType::RustBuffer(_) => "RustBuffer.by_value".to_string(),
FfiType::ForeignBytes => "ForeignBytes".to_string(),
FfiType::Callback(_) => unimplemented!("FFI Callbacks not implemented"),
// Callback interfaces are not yet implemented, but this needs to return something in
// order for the coverall tests to pass.
FfiType::ForeignCallback => ":pointer".to_string(),
Expand All @@ -162,9 +163,7 @@ mod filters {
FfiType::ForeignExecutorHandle => {
unimplemented!("Foreign executors are not implemented")
}
FfiType::RustFutureHandle
| FfiType::RustFutureContinuationCallback
| FfiType::RustFutureContinuationData => {
FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
unimplemented!("Async functions are not implemented")
}
})
Expand Down
19 changes: 14 additions & 5 deletions uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,11 @@ impl SwiftCodeOracle {
nm.to_string().to_lower_camel_case()
}

/// Get the idiomatic Python rendering of an FFI callback function
fn ffi_callback_name(&self, nm: &str) -> String {
format!("Uniffi{}", nm.to_upper_camel_case())
}

fn ffi_type_label_raw(&self, ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::Int8 => "Int8".into(),
Expand All @@ -458,10 +463,10 @@ impl SwiftCodeOracle {
FfiType::RustArcPtr(_) => "UnsafeMutableRawPointer".into(),
FfiType::RustBuffer(_) => "RustBuffer".into(),
FfiType::ForeignBytes => "ForeignBytes".into(),
FfiType::Callback(name) => self.ffi_callback_name(name),
FfiType::ForeignCallback => "ForeignCallback".into(),
FfiType::ForeignExecutorHandle => "Int".into(),
FfiType::ForeignExecutorCallback => "ForeignExecutorCallback".into(),
FfiType::RustFutureContinuationCallback => "UniFfiRustFutureContinuation".into(),
FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
"UnsafeMutableRawPointer".into()
}
Expand All @@ -473,7 +478,6 @@ impl SwiftCodeOracle {
FfiType::ForeignCallback
| FfiType::ForeignExecutorCallback
| FfiType::RustFutureHandle
| FfiType::RustFutureContinuationCallback
| FfiType::RustFutureContinuationData => {
format!("{} _Nonnull", self.ffi_type_label_raw(ffi_type))
}
Expand Down Expand Up @@ -574,12 +578,12 @@ pub mod filters {
FfiType::RustArcPtr(_) => "void*_Nonnull".into(),
FfiType::RustBuffer(_) => "RustBuffer".into(),
FfiType::ForeignBytes => "ForeignBytes".into(),
FfiType::Callback(name) => {
format!("{} _Nonnull", SwiftCodeOracle.ffi_callback_name(name))
}
FfiType::ForeignCallback => "ForeignCallback _Nonnull".into(),
FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback _Nonnull".into(),
FfiType::ForeignExecutorHandle => "size_t".into(),
FfiType::RustFutureContinuationCallback => {
"UniFfiRustFutureContinuation _Nonnull".into()
}
FfiType::RustFutureHandle | FfiType::RustFutureContinuationData => {
"void* _Nonnull".into()
}
Expand Down Expand Up @@ -617,6 +621,11 @@ pub mod filters {
Ok(oracle().enum_variant_name(nm))
}

/// Get the idiomatic Swift rendering of an ffi callback function name
pub fn ffi_callback_name(nm: &str) -> Result<String, askama::Error> {
Ok(oracle().ffi_callback_name(nm))
}

pub fn error_handler(result: &ResultType) -> Result<String, askama::Error> {
Ok(match &result.throws_type {
Some(t) => format!("{}.lift", ffi_converter_name(t)?),
Expand Down
2 changes: 1 addition & 1 deletion uniffi_bindgen/src/bindings/swift/templates/Async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ private let UNIFFI_RUST_FUTURE_POLL_MAYBE_READY: Int8 = 1

fileprivate func uniffiRustCallAsync<F, T>(
rustFutureFunc: () -> UnsafeMutableRawPointer,
pollFunc: (UnsafeMutableRawPointer, @escaping UniFfiRustFutureContinuation, UnsafeMutableRawPointer) -> (),
pollFunc: (UnsafeMutableRawPointer, @escaping UniffiRustFutureContinuationCallback, UnsafeMutableRawPointer) -> (),
completeFunc: (UnsafeMutableRawPointer, UnsafeMutablePointer<RustCallStatus>) -> F,
freeFunc: (UnsafeMutableRawPointer) -> (),
liftFunc: (F) throws -> T,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,17 @@ typedef struct RustCallStatus {
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
#endif // def UNIFFI_SHARED_H

// Continuation callback for UniFFI Futures
typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
// Define FFI callback types
{%- for callback in ci.ffi_callback_definitions() %}
typedef
{%- match callback.return_type() %}{% when Some(return_type) %} {{ return_type|ffi_type_name }} {% when None %} void {% endmatch -%}
(*{{ callback.name()|ffi_callback_name }})(
{%- for arg in callback.arguments() -%}
{{ arg.type_().borrow()|header_ffi_type_name }}
{%- if !loop.last %}, {% endif %}
{%- endfor -%}
);
{%- endfor %}

// Scaffolding functions
{%- for func in ci.iter_ffi_function_definitions() %}
Expand Down
Loading