1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 use proc_macro2::{Ident, Span, TokenStream};
6 use quote::{quote, quote_spanned};
7 use syn::{visit_mut::VisitMut, Item, Type};
10 mod callback_interface;
16 item::{ExportItem, ImplItem},
18 gen_constructor_scaffolding, gen_ffi_function, gen_fn_scaffolding, gen_method_scaffolding,
22 object::interface_meta_static_var,
23 util::{ident_to_string, mod_path, tagged_impl_header},
25 pub use attributes::ExportAttributeArguments;
26 pub use callback_interface::ffi_converter_callback_interface_impl;
27 use uniffi_meta::free_fn_symbol_name;
29 // TODO(jplatte): Ensure no generics, …
30 // TODO(jplatte): Aggregate errors instead of short-circuiting, wherever possible
32 pub(crate) fn expand_export(
34 args: ExportAttributeArguments,
36 ) -> syn::Result<TokenStream> {
37 let mod_path = mod_path()?;
38 // If the input is an `impl` block, rewrite any uses of the `Self` type
39 // alias to the actual type, so we don't have to special-case it in the
40 // metadata collection or scaffolding code generation (which generates
41 // new functions outside of the `impl`).
42 rewrite_self_type(&mut item);
44 let metadata = ExportItem::new(item, &args)?;
47 ExportItem::Function { sig } => gen_fn_scaffolding(sig, &args, udl_mode),
48 ExportItem::Impl { items, self_ident } => {
49 if let Some(rt) = &args.async_runtime {
52 .all(|item| !matches!(item, ImplItem::Method(sig) if sig.is_async))
54 return Err(syn::Error::new_spanned(
56 "no async methods in this impl block",
61 let item_tokens: TokenStream = items
63 .map(|item| match item {
64 ImplItem::Constructor(sig) => gen_constructor_scaffolding(sig, &args, udl_mode),
65 ImplItem::Method(sig) => gen_method_scaffolding(sig, &args, udl_mode),
67 .collect::<syn::Result<_>>()?;
68 Ok(quote_spanned! { self_ident.span() => #item_tokens })
73 callback_interface: false,
75 if let Some(rt) = args.async_runtime {
76 return Err(syn::Error::new_spanned(rt, "not supported for traits"));
79 let name = ident_to_string(&self_ident);
81 Ident::new(&free_fn_symbol_name(&mod_path, &name), Span::call_site());
83 let free_tokens = quote! {
86 pub extern "C" fn #free_fn_ident(
87 ptr: *const ::std::ffi::c_void,
88 call_status: &mut ::uniffi::RustCallStatus
90 uniffi::rust_call(call_status, || {
91 assert!(!ptr.is_null());
92 drop(unsafe { ::std::boxed::Box::from_raw(ptr as *mut std::sync::Arc<dyn #self_ident>) });
98 let impl_tokens: TokenStream = items
100 .map(|item| match item {
101 ImplItem::Method(sig) => {
103 return Err(syn::Error::new(
105 "async trait methods are not supported",
108 gen_method_scaffolding(sig, &args, udl_mode)
110 _ => unreachable!("traits have no constructors"),
112 .collect::<syn::Result<_>>()?;
114 let meta_static_var = (!udl_mode).then(|| {
115 interface_meta_static_var(&self_ident, true, &mod_path)
116 .unwrap_or_else(syn::Error::into_compile_error)
118 let ffi_converter_tokens = ffi_converter_trait_impl(&self_ident, false);
120 Ok(quote_spanned! { self_ident.span() =>
123 #ffi_converter_tokens
130 callback_interface: true,
132 let trait_name = ident_to_string(&self_ident);
133 let trait_impl_ident = Ident::new(
134 &format!("UniFFICallbackHandler{trait_name}"),
137 let internals_ident = Ident::new(
139 "UNIFFI_FOREIGN_CALLBACK_INTERNALS_{}",
140 trait_name.to_ascii_uppercase()
145 let trait_impl = callback_interface::trait_impl(
151 .unwrap_or_else(|e| e.into_compile_error());
152 let metadata_items = callback_interface::metadata_items(&self_ident, &items, &mod_path)
153 .unwrap_or_else(|e| vec![e.into_compile_error()]);
155 let init_ident = Ident::new(
156 &uniffi_meta::init_callback_fn_symbol_name(&mod_path, &trait_name),
162 static #internals_ident: ::uniffi::ForeignCallbackInternals = ::uniffi::ForeignCallbackInternals::new();
166 pub extern "C" fn #init_ident(callback: ::uniffi::ForeignCallback, _: &mut ::uniffi::RustCallStatus) {
167 #internals_ident.set_callback(callback);
180 utrait::expand_uniffi_trait_export(self_ident, uniffi_traits)
185 pub(crate) fn ffi_converter_trait_impl(trait_ident: &Ident, udl_mode: bool) -> TokenStream {
186 let impl_spec = tagged_impl_header("FfiConverterArc", "e! { dyn #trait_ident }, udl_mode);
187 let lift_ref_impl_spec = tagged_impl_header("LiftRef", "e! { dyn #trait_ident }, udl_mode);
188 let name = ident_to_string(trait_ident);
189 let mod_path = match mod_path() {
191 Err(e) => return e.into_compile_error(),
195 // All traits must be `Sync + Send`. The generated scaffolding will fail to compile
196 // if they are not, but unfortunately it fails with an unactionably obscure error message.
197 // By asserting the requirement explicitly, we help Rust produce a more scrutable error message
198 // and thus help the user debug why the requirement isn't being met.
199 uniffi::deps::static_assertions::assert_impl_all!(dyn #trait_ident: Sync, Send);
202 type FfiType = *const ::std::os::raw::c_void;
204 fn lower(obj: ::std::sync::Arc<Self>) -> Self::FfiType {
205 ::std::boxed::Box::into_raw(::std::boxed::Box::new(obj)) as *const ::std::os::raw::c_void
208 fn try_lift(v: Self::FfiType) -> ::uniffi::Result<::std::sync::Arc<Self>> {
209 let foreign_arc = ::std::boxed::Box::leak(unsafe { Box::from_raw(v as *mut ::std::sync::Arc<Self>) });
210 // Take a clone for our own use.
211 Ok(::std::sync::Arc::clone(foreign_arc))
214 fn write(obj: ::std::sync::Arc<Self>, buf: &mut Vec<u8>) {
215 ::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8);
216 ::uniffi::deps::bytes::BufMut::put_u64(
218 <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::lower(obj) as u64,
222 fn try_read(buf: &mut &[u8]) -> ::uniffi::Result<::std::sync::Arc<Self>> {
223 ::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8);
224 ::uniffi::check_remaining(buf, 8)?;
225 <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::try_lift(
226 ::uniffi::deps::bytes::Buf::get_u64(buf) as Self::FfiType)
229 const TYPE_ID_META: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::TYPE_INTERFACE)
230 .concat_str(#mod_path)
235 unsafe #lift_ref_impl_spec {
236 type LiftType = ::std::sync::Arc<dyn #trait_ident>;
241 /// Rewrite Self type alias usage in an impl block to the type itself.
246 /// impl some::module::Foo {
249 /// arg: Option<Bar<(), Self>>,
250 /// ) -> Result<Self, Error> {
256 /// will be rewritten to
259 /// impl some::module::Foo {
261 /// self: Arc<some::module::Foo>,
262 /// arg: Option<Bar<(), some::module::Foo>>,
263 /// ) -> Result<some::module::Foo, Error> {
268 pub fn rewrite_self_type(item: &mut Item) {
269 let item = match item {
274 struct RewriteSelfVisitor<'a>(&'a Type);
276 impl<'a> VisitMut for RewriteSelfVisitor<'a> {
277 fn visit_type_mut(&mut self, i: &mut Type) {
279 Type::Path(p) if p.qself.is_none() && p.path.is_ident("Self") => {
282 _ => syn::visit_mut::visit_type_mut(self, i),
287 let mut visitor = RewriteSelfVisitor(&item.self_ty);
288 for item in &mut item.items {
289 visitor.visit_impl_item_mut(item);