Backed out 5 changesets (bug 1890092, bug 1888683) for causing build bustages & crash...
[gecko.git] / third_party / rust / uniffi_macros / src / export.rs
blobbbb16acf9019a644da842be6ad815fac7394c264
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};
9 mod attributes;
10 mod callback_interface;
11 mod item;
12 mod scaffolding;
13 mod utrait;
15 use self::{
16     item::{ExportItem, ImplItem},
17     scaffolding::{
18         gen_constructor_scaffolding, gen_ffi_function, gen_fn_scaffolding, gen_method_scaffolding,
19     },
21 use crate::{
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(
33     mut item: Item,
34     args: ExportAttributeArguments,
35     udl_mode: bool,
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)?;
46     match metadata {
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 {
50                 if items
51                     .iter()
52                     .all(|item| !matches!(item, ImplItem::Method(sig) if sig.is_async))
53                 {
54                     return Err(syn::Error::new_spanned(
55                         rt,
56                         "no async methods in this impl block",
57                     ));
58                 }
59             }
61             let item_tokens: TokenStream = items
62                 .into_iter()
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),
66                 })
67                 .collect::<syn::Result<_>>()?;
68             Ok(quote_spanned! { self_ident.span() => #item_tokens })
69         }
70         ExportItem::Trait {
71             items,
72             self_ident,
73             callback_interface: false,
74         } => {
75             if let Some(rt) = args.async_runtime {
76                 return Err(syn::Error::new_spanned(rt, "not supported for traits"));
77             }
79             let name = ident_to_string(&self_ident);
80             let free_fn_ident =
81                 Ident::new(&free_fn_symbol_name(&mod_path, &name), Span::call_site());
83             let free_tokens = quote! {
84                 #[doc(hidden)]
85                 #[no_mangle]
86                 pub extern "C" fn #free_fn_ident(
87                     ptr: *const ::std::ffi::c_void,
88                     call_status: &mut ::uniffi::RustCallStatus
89                 ) {
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>) });
93                         Ok(())
94                     });
95                 }
96             };
98             let impl_tokens: TokenStream = items
99                 .into_iter()
100                 .map(|item| match item {
101                     ImplItem::Method(sig) => {
102                         if sig.is_async {
103                             return Err(syn::Error::new(
104                                 sig.span,
105                                 "async trait methods are not supported",
106                             ));
107                         }
108                         gen_method_scaffolding(sig, &args, udl_mode)
109                     }
110                     _ => unreachable!("traits have no constructors"),
111                 })
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)
117             });
118             let ffi_converter_tokens = ffi_converter_trait_impl(&self_ident, false);
120             Ok(quote_spanned! { self_ident.span() =>
121                 #meta_static_var
122                 #free_tokens
123                 #ffi_converter_tokens
124                 #impl_tokens
125             })
126         }
127         ExportItem::Trait {
128             items,
129             self_ident,
130             callback_interface: true,
131         } => {
132             let trait_name = ident_to_string(&self_ident);
133             let trait_impl_ident = Ident::new(
134                 &format!("UniFFICallbackHandler{trait_name}"),
135                 Span::call_site(),
136             );
137             let internals_ident = Ident::new(
138                 &format!(
139                     "UNIFFI_FOREIGN_CALLBACK_INTERNALS_{}",
140                     trait_name.to_ascii_uppercase()
141                 ),
142                 Span::call_site(),
143             );
145             let trait_impl = callback_interface::trait_impl(
146                 &trait_impl_ident,
147                 &self_ident,
148                 &internals_ident,
149                 &items,
150             )
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),
157                 Span::call_site(),
158             );
160             Ok(quote! {
161                 #[doc(hidden)]
162                 static #internals_ident: ::uniffi::ForeignCallbackInternals = ::uniffi::ForeignCallbackInternals::new();
164                 #[doc(hidden)]
165                 #[no_mangle]
166                 pub extern "C" fn #init_ident(callback: ::uniffi::ForeignCallback, _: &mut ::uniffi::RustCallStatus) {
167                     #internals_ident.set_callback(callback);
168                 }
170                 #trait_impl
172                 #(#metadata_items)*
173             })
174         }
175         ExportItem::Struct {
176             self_ident,
177             uniffi_traits,
178         } => {
179             assert!(!udl_mode);
180             utrait::expand_uniffi_trait_export(self_ident, uniffi_traits)
181         }
182     }
185 pub(crate) fn ffi_converter_trait_impl(trait_ident: &Ident, udl_mode: bool) -> TokenStream {
186     let impl_spec = tagged_impl_header("FfiConverterArc", &quote! { dyn #trait_ident }, udl_mode);
187     let lift_ref_impl_spec = tagged_impl_header("LiftRef", &quote! { dyn #trait_ident }, udl_mode);
188     let name = ident_to_string(trait_ident);
189     let mod_path = match mod_path() {
190         Ok(p) => p,
191         Err(e) => return e.into_compile_error(),
192     };
194     quote! {
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);
201         unsafe #impl_spec {
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
206             }
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))
212             }
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(
217                     buf,
218                     <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::lower(obj) as u64,
219                 );
220             }
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)
227             }
229             const TYPE_ID_META: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::TYPE_INTERFACE)
230                 .concat_str(#mod_path)
231                 .concat_str(#name)
232                 .concat_bool(true);
233         }
235         unsafe #lift_ref_impl_spec {
236             type LiftType = ::std::sync::Arc<dyn #trait_ident>;
237         }
238     }
241 /// Rewrite Self type alias usage in an impl block to the type itself.
243 /// For example,
245 /// ```ignore
246 /// impl some::module::Foo {
247 ///     fn method(
248 ///         self: Arc<Self>,
249 ///         arg: Option<Bar<(), Self>>,
250 ///     ) -> Result<Self, Error> {
251 ///         todo!()
252 ///     }
253 /// }
254 /// ```
256 /// will be rewritten to
258 ///  ```ignore
259 /// impl some::module::Foo {
260 ///     fn method(
261 ///         self: Arc<some::module::Foo>,
262 ///         arg: Option<Bar<(), some::module::Foo>>,
263 ///     ) -> Result<some::module::Foo, Error> {
264 ///         todo!()
265 ///     }
266 /// }
267 /// ```
268 pub fn rewrite_self_type(item: &mut Item) {
269     let item = match item {
270         Item::Impl(i) => i,
271         _ => return,
272     };
274     struct RewriteSelfVisitor<'a>(&'a Type);
276     impl<'a> VisitMut for RewriteSelfVisitor<'a> {
277         fn visit_type_mut(&mut self, i: &mut Type) {
278             match i {
279                 Type::Path(p) if p.qself.is_none() && p.path.is_ident("Self") => {
280                     *i = self.0.clone();
281                 }
282                 _ => syn::visit_mut::visit_type_mut(self, i),
283             }
284         }
285     }
287     let mut visitor = RewriteSelfVisitor(&item.self_ty);
288     for item in &mut item.items {
289         visitor.visit_impl_item_mut(item);
290     }