summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_macros/src/export/callback_interface.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/uniffi_macros/src/export/callback_interface.rs204
1 files changed, 146 insertions, 58 deletions
diff --git a/third_party/rust/uniffi_macros/src/export/callback_interface.rs b/third_party/rust/uniffi_macros/src/export/callback_interface.rs
index 2f2561bbc2..fe145384ec 100644
--- a/third_party/rust/uniffi_macros/src/export/callback_interface.rs
+++ b/third_party/rust/uniffi_macros/src/export/callback_interface.rs
@@ -5,69 +5,137 @@
use crate::{
export::ImplItem,
fnsig::{FnKind, FnSignature, ReceiverArg},
- util::{create_metadata_items, ident_to_string, mod_path, tagged_impl_header},
+ util::{
+ create_metadata_items, derive_ffi_traits, ident_to_string, mod_path, tagged_impl_header,
+ },
};
use proc_macro2::{Span, TokenStream};
-use quote::quote;
+use quote::{format_ident, quote};
use std::iter;
use syn::Ident;
+/// Generate a trait impl that calls foreign callbacks
+///
+/// This generates:
+/// * A `repr(C)` VTable struct where each field is the FFI function for the trait method.
+/// * A FFI function for foreign code to set their VTable for the interface
+/// * An implementation of the trait using that VTable
pub(super) fn trait_impl(
- ident: &Ident,
+ mod_path: &str,
trait_ident: &Ident,
- internals_ident: &Ident,
items: &[ImplItem],
) -> syn::Result<TokenStream> {
- let trait_impl_methods = items
+ let trait_name = ident_to_string(trait_ident);
+ let trait_impl_ident = trait_impl_ident(&trait_name);
+ let vtable_type = format_ident!("UniFfiTraitVtable{trait_name}");
+ let vtable_cell = format_ident!("UNIFFI_TRAIT_CELL_{}", trait_name.to_uppercase());
+ let init_ident = Ident::new(
+ &uniffi_meta::init_callback_vtable_fn_symbol_name(mod_path, &trait_name),
+ Span::call_site(),
+ );
+ let methods = items
.iter()
.map(|item| match item {
- ImplItem::Method(sig) => gen_method_impl(sig, internals_ident),
- _ => unreachable!("traits have no constructors"),
+ ImplItem::Constructor(sig) => Err(syn::Error::new(
+ sig.span,
+ "Constructors not allowed in trait interfaces",
+ )),
+ ImplItem::Method(sig) => Ok(sig),
})
- .collect::<syn::Result<TokenStream>>()?;
- let ffi_converter_tokens = ffi_converter_callback_interface_impl(trait_ident, ident, false);
+ .collect::<syn::Result<Vec<_>>>()?;
+
+ let vtable_fields = methods.iter()
+ .map(|sig| {
+ let ident = &sig.ident;
+ let param_names = sig.scaffolding_param_names();
+ let param_types = sig.scaffolding_param_types();
+ let lift_return = sig.lift_return_impl();
+ if !sig.is_async {
+ quote! {
+ #ident: extern "C" fn(
+ uniffi_handle: u64,
+ #(#param_names: #param_types,)*
+ uniffi_out_return: &mut #lift_return::ReturnType,
+ uniffi_out_call_status: &mut ::uniffi::RustCallStatus,
+ ),
+ }
+ } else {
+ quote! {
+ #ident: extern "C" fn(
+ uniffi_handle: u64,
+ #(#param_names: #param_types,)*
+ uniffi_future_callback: ::uniffi::ForeignFutureCallback<#lift_return::ReturnType>,
+ uniffi_callback_data: u64,
+ uniffi_out_return: &mut ::uniffi::ForeignFuture,
+ ),
+ }
+ }
+ });
+
+ let trait_impl_methods = methods
+ .iter()
+ .map(|sig| gen_method_impl(sig, &vtable_cell))
+ .collect::<syn::Result<Vec<_>>>()?;
+ let has_async_method = methods.iter().any(|m| m.is_async);
+ let impl_attributes = has_async_method.then(|| quote! { #[::async_trait::async_trait] });
Ok(quote! {
- #[doc(hidden)]
+ struct #vtable_type {
+ #(#vtable_fields)*
+ uniffi_free: extern "C" fn(handle: u64),
+ }
+
+ static #vtable_cell: ::uniffi::UniffiForeignPointerCell::<#vtable_type> = ::uniffi::UniffiForeignPointerCell::<#vtable_type>::new();
+
+ #[no_mangle]
+ extern "C" fn #init_ident(vtable: ::std::ptr::NonNull<#vtable_type>) {
+ #vtable_cell.set(vtable);
+ }
+
#[derive(Debug)]
- struct #ident {
+ struct #trait_impl_ident {
handle: u64,
}
- impl #ident {
+ impl #trait_impl_ident {
fn new(handle: u64) -> Self {
Self { handle }
}
}
- impl ::std::ops::Drop for #ident {
- fn drop(&mut self) {
- #internals_ident.invoke_callback::<(), crate::UniFfiTag>(
- self.handle, uniffi::IDX_CALLBACK_FREE, Default::default()
- )
- }
- }
-
- ::uniffi::deps::static_assertions::assert_impl_all!(#ident: Send);
+ ::uniffi::deps::static_assertions::assert_impl_all!(#trait_impl_ident: ::core::marker::Send);
- impl #trait_ident for #ident {
- #trait_impl_methods
+ #impl_attributes
+ impl #trait_ident for #trait_impl_ident {
+ #(#trait_impl_methods)*
}
- #ffi_converter_tokens
+ impl ::std::ops::Drop for #trait_impl_ident {
+ fn drop(&mut self) {
+ let vtable = #vtable_cell.get();
+ (vtable.uniffi_free)(self.handle);
+ }
+ }
})
}
+pub fn trait_impl_ident(trait_name: &str) -> Ident {
+ Ident::new(
+ &format!("UniFFICallbackHandler{trait_name}"),
+ Span::call_site(),
+ )
+}
+
pub fn ffi_converter_callback_interface_impl(
trait_ident: &Ident,
trait_impl_ident: &Ident,
udl_mode: bool,
) -> TokenStream {
- let name = ident_to_string(trait_ident);
+ let trait_name = ident_to_string(trait_ident);
let dyn_trait = quote! { dyn #trait_ident };
let box_dyn_trait = quote! { ::std::boxed::Box<#dyn_trait> };
let lift_impl_spec = tagged_impl_header("Lift", &box_dyn_trait, udl_mode);
- let lift_ref_impl_spec = tagged_impl_header("LiftRef", &dyn_trait, udl_mode);
+ let derive_ffi_traits = derive_ffi_traits(&box_dyn_trait, udl_mode, &["LiftRef", "LiftReturn"]);
let mod_path = match mod_path() {
Ok(p) => p,
Err(e) => return e.into_compile_error(),
@@ -93,66 +161,85 @@ pub fn ffi_converter_callback_interface_impl(
::uniffi::metadata::codes::TYPE_CALLBACK_INTERFACE,
)
.concat_str(#mod_path)
- .concat_str(#name);
+ .concat_str(#trait_name);
}
- unsafe #lift_ref_impl_spec {
- type LiftType = #box_dyn_trait;
- }
+ #derive_ffi_traits
}
}
-fn gen_method_impl(sig: &FnSignature, internals_ident: &Ident) -> syn::Result<TokenStream> {
+/// Generate a single method for [trait_impl]. This implements a trait method by invoking a
+/// foreign-supplied callback.
+fn gen_method_impl(sig: &FnSignature, vtable_cell: &Ident) -> syn::Result<TokenStream> {
let FnSignature {
ident,
+ is_async,
return_ty,
kind,
receiver,
+ name,
+ span,
..
} = sig;
- let index = match kind {
- // Note: the callback index is 1-based, since 0 is reserved for the free function
- FnKind::TraitMethod { index, .. } => index + 1,
- k => {
- return Err(syn::Error::new(
- sig.span,
- format!(
- "Internal UniFFI error: Unexpected function kind for callback interface {k:?}"
- ),
- ));
- }
- };
+
+ if !matches!(kind, FnKind::TraitMethod { .. }) {
+ return Err(syn::Error::new(
+ *span,
+ format!(
+ "Internal UniFFI error: Unexpected function kind for callback interface {name}: {kind:?}",
+ ),
+ ));
+ }
let self_param = match receiver {
+ Some(ReceiverArg::Ref) => quote! { &self },
+ Some(ReceiverArg::Arc) => quote! { self: Arc<Self> },
None => {
return Err(syn::Error::new(
- sig.span,
+ *span,
"callback interface methods must take &self as their first argument",
));
}
- Some(ReceiverArg::Ref) => quote! { &self },
- Some(ReceiverArg::Arc) => quote! { self: Arc<Self> },
};
+
let params = sig.params();
- let buf_ident = Ident::new("uniffi_args_buf", Span::call_site());
- let write_exprs = sig.write_exprs(&buf_ident);
+ let lower_exprs = sig.args.iter().map(|a| {
+ let lower_impl = a.lower_impl();
+ let ident = &a.ident;
+ quote! { #lower_impl::lower(#ident) }
+ });
- Ok(quote! {
- fn #ident(#self_param, #(#params),*) -> #return_ty {
- #[allow(unused_mut)]
- let mut #buf_ident = ::std::vec::Vec::new();
- #(#write_exprs;)*
- let uniffi_args_rbuf = uniffi::RustBuffer::from_vec(#buf_ident);
+ let lift_return = sig.lift_return_impl();
- #internals_ident.invoke_callback::<#return_ty, crate::UniFfiTag>(self.handle, #index, uniffi_args_rbuf)
- }
- })
+ if !is_async {
+ Ok(quote! {
+ fn #ident(#self_param, #(#params),*) -> #return_ty {
+ let vtable = #vtable_cell.get();
+ let mut uniffi_call_status = ::uniffi::RustCallStatus::new();
+ let mut uniffi_return_value: #lift_return::ReturnType = ::uniffi::FfiDefault::ffi_default();
+ (vtable.#ident)(self.handle, #(#lower_exprs,)* &mut uniffi_return_value, &mut uniffi_call_status);
+ #lift_return::lift_foreign_return(uniffi_return_value, uniffi_call_status)
+ }
+ })
+ } else {
+ Ok(quote! {
+ async fn #ident(#self_param, #(#params),*) -> #return_ty {
+ let vtable = #vtable_cell.get();
+ ::uniffi::foreign_async_call::<_, #return_ty, crate::UniFfiTag>(move |uniffi_future_callback, uniffi_future_callback_data| {
+ let mut uniffi_foreign_future: ::uniffi::ForeignFuture = ::uniffi::FfiDefault::ffi_default();
+ (vtable.#ident)(self.handle, #(#lower_exprs,)* uniffi_future_callback, uniffi_future_callback_data, &mut uniffi_foreign_future);
+ uniffi_foreign_future
+ }).await
+ }
+ })
+ }
}
pub(super) fn metadata_items(
self_ident: &Ident,
items: &[ImplItem],
module_path: &str,
+ docstring: String,
) -> syn::Result<Vec<TokenStream>> {
let trait_name = ident_to_string(self_ident);
let callback_interface_items = create_metadata_items(
@@ -162,13 +249,14 @@ pub(super) fn metadata_items(
::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::CALLBACK_INTERFACE)
.concat_str(#module_path)
.concat_str(#trait_name)
+ .concat_long_str(#docstring)
},
None,
);
iter::once(Ok(callback_interface_items))
.chain(items.iter().map(|item| match item {
- ImplItem::Method(sig) => sig.metadata_items(),
+ ImplItem::Method(sig) => sig.metadata_items_for_callback_interface(),
_ => unreachable!("traits have no constructors"),
}))
.collect()