Skip to content

Commit

Permalink
Always use uniffi_macros to generate callback interface scaffolding
Browse files Browse the repository at this point in the history
This makes it so the scaffolding templates use `export_for_udl` rather
than generating their own code.
  • Loading branch information
bendk committed Dec 12, 2023
1 parent c326efc commit 68b14c9
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 141 deletions.
34 changes: 0 additions & 34 deletions uniffi_bindgen/src/scaffolding/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,40 +72,6 @@ mod filters {
})
}

// Map a type to Rust code that specifies the FfiConverter implementation.
//
// This outputs something like `<MyStruct as Lift<crate::UniFfiTag>>`
pub fn ffi_trait(type_: &Type, trait_name: &str) -> Result<String, askama::Error> {
Ok(match type_ {
Type::External {
name,
kind: ExternalKind::Interface,
..
} => {
format!("<::std::sync::Arc<r#{name}> as ::uniffi::{trait_name}<crate::UniFfiTag>>")
}
_ => format!(
"<{} as ::uniffi::{trait_name}<crate::UniFfiTag>>",
type_rs(type_)?
),
})
}

pub fn return_type<T: Callable>(callable: &T) -> Result<String, askama::Error> {
let return_type = match callable.return_type() {
Some(t) => type_rs(&t)?,
None => "()".to_string(),
};
match callable.throws_type() {
Some(t) => type_rs(&t)?,
None => "()".to_string(),
};
Ok(match callable.throws_type() {
Some(e) => format!("::std::result::Result<{return_type}, {}>", type_rs(&e)?),
None => return_type,
})
}

// Turns a `crate-name` into the `crate_name` the .rs code needs to specify.
pub fn crate_name_rs(nm: &str) -> Result<String, askama::Error> {
Ok(format!("r#{}", nm.to_string().to_snake_case()))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,82 +1,17 @@
{#
// For each Callback Interface definition, we assume that there is a corresponding trait defined in Rust client code.
// If the UDL callback interface and Rust trait's methods don't match, the Rust compiler will complain.
// We generate:
// * an init function to accept that `ForeignCallback` from the foreign language, and stores it.
// * a holder for a `ForeignCallback`, of type `uniffi::ForeignCallbackInternals`.
// * a proxy `struct` which implements the `trait` that the Callback Interface corresponds to. This
// is the object that client code interacts with.
// - for each method, arguments will be packed into a `RustBuffer` and sent over the `ForeignCallback` to be
// unpacked and called. The return value is packed into another `RustBuffer` and sent back to Rust.
// - a `Drop` `impl`, which tells the foreign language to forget about the real callback object.
#}
{% let trait_name = cbi.name() -%}
{% let trait_impl = format!("UniFFICallbackHandler{}", trait_name) %}
{% let foreign_callback_internals = format!("foreign_callback_{}_internals", trait_name)|upper -%}

// Register a foreign callback for getting across the FFI.
#[doc(hidden)]
static {{ foreign_callback_internals }}: uniffi::ForeignCallbackInternals = uniffi::ForeignCallbackInternals::new();

#[doc(hidden)]
#[no_mangle]
pub extern "C" fn {{ cbi.ffi_init_callback().name() }}(callback: uniffi::ForeignCallback, _: &mut uniffi::RustCallStatus) {
{{ foreign_callback_internals }}.set_callback(callback);
// The call status should be initialized to CALL_SUCCESS, so no need to modify it.
}

// Make an implementation which will shell out to the foreign language.
#[doc(hidden)]
#[derive(Debug)]
struct {{ trait_impl }} {
handle: u64
}

impl {{ trait_impl }} {
fn new(handle: u64) -> Self {
Self { handle }
}
}

impl Drop for {{ trait_impl }} {
fn drop(&mut self) {
{{ foreign_callback_internals }}.invoke_callback::<(), crate::UniFfiTag>(
self.handle, uniffi::IDX_CALLBACK_FREE, Default::default()
)
}
}

uniffi::deps::static_assertions::assert_impl_all!({{ trait_impl }}: ::core::marker::Send);

impl r#{{ trait_name }} for {{ trait_impl }} {
#[::uniffi::export_for_udl(callback_interface)]
pub trait r#{{ cbi.name() }} {
{%- for meth in cbi.methods() %}

{#- Method declaration #}
fn r#{{ meth.name() -}}
({% call rs::arg_list_decl_with_prefix("&self", meth) %})
{%- match (meth.return_type(), meth.throws_type()) %}
{%- when (Some(return_type), None) %} -> {{ return_type.borrow()|type_rs }}
{%- when (Some(return_type), Some(err)) %} -> ::std::result::Result<{{ return_type.borrow()|type_rs }}, {{ err|type_rs }}>
{%- when (None, Some(err)) %} -> ::std::result::Result<(), {{ err|type_rs }}>
{% else -%}
{%- endmatch -%} {
{#- Method body #}

{#- Packing args into a RustBuffer #}
{% if meth.arguments().len() == 0 -%}
let args_buf = Vec::new();
{% else -%}
let mut args_buf = Vec::new();
{% endif -%}
fn r#{{ meth.name() }}(
{% if meth.takes_self_by_arc()%}self: Arc<Self>{% else %}&self{% endif %},
{%- for arg in meth.arguments() %}
{{ arg.as_type().borrow()|ffi_trait("Lower") }}::write(r#{{ arg.name() }}, &mut args_buf);
{%- endfor -%}
let args_rbuf = uniffi::RustBuffer::from_vec(args_buf);

{#- Calling into foreign code. #}
{{ foreign_callback_internals }}.invoke_callback::<{{ meth|return_type }}, crate::UniFfiTag>(self.handle, {{ loop.index }}, args_rbuf)
}
{%- endfor %}
r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
{%- endfor %}
)
{%- match (meth.return_type(), meth.throws_type()) %}
{%- when (Some(return_type), None) %} -> {{ return_type|type_rs }};
{%- when (Some(return_type), Some(error_type)) %} -> ::std::result::Result::<{{ return_type|type_rs }}, {{ error_type|type_rs }}>;
{%- when (None, Some(error_type)) %} -> ::std::result::Result::<(), {{ error_type|type_rs }}>;
{%- when (None, None) %};
{%- endmatch %}
{% endfor %}
}

::uniffi::scaffolding_ffi_converter_callback_interface!(r#{{ trait_name }}, {{ trait_impl }});
4 changes: 2 additions & 2 deletions uniffi_bindgen/src/scaffolding/templates/ObjectTemplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
#[::uniffi::export_for_udl]
pub trait r#{{ obj.name() }} {
{%- for meth in obj.methods() %}
fn {% if meth.is_async() %}async {% endif %}{{ meth.name() }}(
fn {% if meth.is_async() %}async {% endif %}r#{{ meth.name() }}(
{% if meth.takes_self_by_arc()%}self: Arc<Self>{% else %}&self{% endif %},
{%- for arg in meth.arguments() %}
{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
r#{{ arg.name() }}: {% if arg.by_ref() %}&{% endif %}{{ arg.as_type().borrow()|type_rs }},
{%- endfor %}
)
{%- match (meth.return_type(), meth.throws_type()) %}
Expand Down
11 changes: 7 additions & 4 deletions uniffi_macros/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,12 @@ pub(crate) fn expand_export(
let trait_impl_ident = callback_interface::trait_impl_ident(&trait_name);
let trait_impl = callback_interface::trait_impl(&mod_path, &self_ident, &items)
.unwrap_or_else(|e| e.into_compile_error());
let metadata_items =
callback_interface::metadata_items(&self_ident, &items, &mod_path, docstring)
.unwrap_or_else(|e| vec![e.into_compile_error()]);
let metadata_items = (!udl_mode).then(|| {
let items =
callback_interface::metadata_items(&self_ident, &items, &mod_path, docstring)
.unwrap_or_else(|e| vec![e.into_compile_error()]);
quote! { #(#items)* }
});
let ffi_converter_tokens =
ffi_converter_callback_interface_impl(&self_ident, &trait_impl_ident, udl_mode);

Expand All @@ -93,7 +96,7 @@ pub(crate) fn expand_export(

#ffi_converter_tokens

#(#metadata_items)*
#metadata_items
})
}
ExportItem::Struct {
Expand Down
22 changes: 0 additions & 22 deletions uniffi_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,6 @@ use self::{
record::expand_record,
};

struct IdentPair {
lhs: Ident,
rhs: Ident,
}

impl Parse for IdentPair {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let lhs = input.parse()?;
input.parse::<Token![,]>()?;
let rhs = input.parse()?;
Ok(Self { lhs, rhs })
}
}

struct CustomTypeInfo {
ident: Ident,
builtin: Path,
Expand Down Expand Up @@ -257,14 +243,6 @@ pub fn export_for_udl(attrs: TokenStream, input: TokenStream) -> TokenStream {
do_export(attrs, input, true)
}

/// Generate the FfiConverter implementation for an trait interface for the scaffolding code
#[doc(hidden)]
#[proc_macro]
pub fn scaffolding_ffi_converter_callback_interface(tokens: TokenStream) -> TokenStream {
let input: IdentPair = syn::parse_macro_input!(tokens);
export::ffi_converter_callback_interface_impl(&input.lhs, &input.rhs, true).into()
}

/// A helper macro to include generated component scaffolding.
///
/// This is a simple convenience macro to include the UniFFI component
Expand Down

0 comments on commit 68b14c9

Please sign in to comment.