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/. */
4 
5 use proc_macro2::TokenStream;
6 use quote::{format_ident, quote};
7 use syn::Result;
8 
9 use crate::util::mod_path;
10 use uniffi_meta::UNIFFI_CONTRACT_VERSION;
11 
setup_scaffolding(namespace: String) -> Result<TokenStream>12 pub fn setup_scaffolding(namespace: String) -> Result<TokenStream> {
13     let module_path = mod_path()?;
14     let ffi_contract_version_ident = format_ident!("ffi_{module_path}_uniffi_contract_version");
15     let namespace_upper = namespace.to_ascii_uppercase();
16     let namespace_const_ident = format_ident!("UNIFFI_META_CONST_NAMESPACE_{namespace_upper}");
17     let namespace_static_ident = format_ident!("UNIFFI_META_NAMESPACE_{namespace_upper}");
18     let ffi_rustbuffer_alloc_ident = format_ident!("ffi_{module_path}_rustbuffer_alloc");
19     let ffi_rustbuffer_from_bytes_ident = format_ident!("ffi_{module_path}_rustbuffer_from_bytes");
20     let ffi_rustbuffer_free_ident = format_ident!("ffi_{module_path}_rustbuffer_free");
21     let ffi_rustbuffer_reserve_ident = format_ident!("ffi_{module_path}_rustbuffer_reserve");
22     let reexport_hack_ident = format_ident!("{module_path}_uniffi_reexport_hack");
23     let ffi_rust_future_scaffolding_fns = rust_future_scaffolding_fns(&module_path);
24 
25     Ok(quote! {
26         // Unit struct to parameterize the FfiConverter trait.
27         //
28         // We use FfiConverter<UniFfiTag> to handle lowering/lifting/serializing types for this crate.  See
29         // https://mozilla.github.io/uniffi-rs/internals/lifting_and_lowering.html#code-generation-and-the-fficonverter-trait
30         // for details.
31         //
32         // This is pub, since we need to access it to support external types
33         #[doc(hidden)]
34         pub struct UniFfiTag;
35 
36         #[allow(clippy::missing_safety_doc, missing_docs)]
37         #[doc(hidden)]
38         #[no_mangle]
39         pub extern "C" fn #ffi_contract_version_ident() -> u32 {
40             #UNIFFI_CONTRACT_VERSION
41         }
42 
43 
44         /// Export namespace metadata.
45         ///
46         /// See `uniffi_bindgen::macro_metadata` for how this is used.
47 
48         const #namespace_const_ident: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::NAMESPACE)
49             .concat_str(#module_path)
50             .concat_str(#namespace);
51 
52         #[doc(hidden)]
53         #[no_mangle]
54         pub static #namespace_static_ident: [u8; #namespace_const_ident.size] = #namespace_const_ident.into_array();
55 
56         // Everybody gets basic buffer support, since it's needed for passing complex types over the FFI.
57         //
58         // See `uniffi/src/ffi/rustbuffer.rs` for documentation on these functions
59 
60         #[allow(clippy::missing_safety_doc, missing_docs)]
61         #[doc(hidden)]
62         #[no_mangle]
63         pub extern "C" fn #ffi_rustbuffer_alloc_ident(size: u64, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer {
64             uniffi::ffi::uniffi_rustbuffer_alloc(size, call_status)
65         }
66 
67         #[allow(clippy::missing_safety_doc, missing_docs)]
68         #[doc(hidden)]
69         #[no_mangle]
70         pub unsafe extern "C" fn #ffi_rustbuffer_from_bytes_ident(bytes: uniffi::ForeignBytes, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer {
71             uniffi::ffi::uniffi_rustbuffer_from_bytes(bytes, call_status)
72         }
73 
74         #[allow(clippy::missing_safety_doc, missing_docs)]
75         #[doc(hidden)]
76         #[no_mangle]
77         pub unsafe extern "C" fn #ffi_rustbuffer_free_ident(buf: uniffi::RustBuffer, call_status: &mut uniffi::RustCallStatus) {
78             uniffi::ffi::uniffi_rustbuffer_free(buf, call_status);
79         }
80 
81         #[allow(clippy::missing_safety_doc, missing_docs)]
82         #[doc(hidden)]
83         #[no_mangle]
84         pub unsafe extern "C" fn #ffi_rustbuffer_reserve_ident(buf: uniffi::RustBuffer, additional: u64, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer {
85             uniffi::ffi::uniffi_rustbuffer_reserve(buf, additional, call_status)
86         }
87 
88         #ffi_rust_future_scaffolding_fns
89 
90         // Code to re-export the UniFFI scaffolding functions.
91         //
92         // Rust won't always re-export the functions from dependencies
93         // ([rust-lang#50007](https://github.com/rust-lang/rust/issues/50007))
94         //
95         // A workaround for this is to have the dependent crate reference a function from its dependency in
96         // an extern "C" function. This is clearly hacky and brittle, but at least we have some unittests
97         // that check if this works (fixtures/reexport-scaffolding-macro).
98         //
99         // The main way we use this macro is for that contain multiple UniFFI components (libxul,
100         // megazord).  The combined library has a cargo dependency for each component and calls
101         // uniffi_reexport_scaffolding!() for each one.
102 
103         #[allow(missing_docs)]
104         #[doc(hidden)]
105         pub const fn uniffi_reexport_hack() {}
106 
107         #[doc(hidden)]
108         #[macro_export]
109         macro_rules! uniffi_reexport_scaffolding {
110             () => {
111                 #[doc(hidden)]
112                 #[no_mangle]
113                 pub extern "C" fn #reexport_hack_ident() {
114                     $crate::uniffi_reexport_hack()
115                 }
116             };
117         }
118 
119         // A trait that's in our crate for our external wrapped types to implement.
120         #[allow(unused)]
121         #[doc(hidden)]
122         pub trait UniffiCustomTypeConverter {
123             type Builtin;
124             fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> where Self: Sized;
125             fn from_custom(obj: Self) -> Self::Builtin;
126         }
127     })
128 }
129 
130 /// Generates the rust_future_* functions
131 ///
132 /// The foreign side uses a type-erased `Handle` to interact with futures, which presents
133 /// a problem when creating scaffolding functions.  What is the `ReturnType` parameter of `RustFutureFfi`?
134 ///
135 /// Handle this by using some brute-force monomorphization.  For each possible ffi type, we
136 /// generate a set of scaffolding functions.  The bindings code is responsible for calling the one
137 /// corresponds the scaffolding function that created the `Handle`.
138 ///
139 /// This introduces safety issues, but we do get some type checking.  If the bindings code calls
140 /// the wrong rust_future_complete function, they should get an unexpected return type, which
141 /// hopefully will result in a compile-time error.
rust_future_scaffolding_fns(module_path: &str) -> TokenStream142 fn rust_future_scaffolding_fns(module_path: &str) -> TokenStream {
143     let fn_info = [
144         (quote! { u8 }, "u8"),
145         (quote! { i8 }, "i8"),
146         (quote! { u16 }, "u16"),
147         (quote! { i16 }, "i16"),
148         (quote! { u32 }, "u32"),
149         (quote! { i32 }, "i32"),
150         (quote! { u64 }, "u64"),
151         (quote! { i64 }, "i64"),
152         (quote! { f32 }, "f32"),
153         (quote! { f64 }, "f64"),
154         (quote! { *const ::std::ffi::c_void }, "pointer"),
155         (quote! { ::uniffi::RustBuffer }, "rust_buffer"),
156         (quote! { () }, "void"),
157     ];
158     fn_info.iter()
159     .map(|(return_type, fn_suffix)| {
160         let ffi_rust_future_poll = format_ident!("ffi_{module_path}_rust_future_poll_{fn_suffix}");
161         let ffi_rust_future_cancel = format_ident!("ffi_{module_path}_rust_future_cancel_{fn_suffix}");
162         let ffi_rust_future_complete = format_ident!("ffi_{module_path}_rust_future_complete_{fn_suffix}");
163         let ffi_rust_future_free = format_ident!("ffi_{module_path}_rust_future_free_{fn_suffix}");
164 
165         quote! {
166             #[allow(clippy::missing_safety_doc, missing_docs)]
167             #[doc(hidden)]
168             #[no_mangle]
169             pub unsafe extern "C" fn #ffi_rust_future_poll(handle: ::uniffi::Handle, callback: ::uniffi::RustFutureContinuationCallback, data: u64) {
170                 ::uniffi::ffi::rust_future_poll::<#return_type, crate::UniFfiTag>(handle, callback, data);
171             }
172 
173             #[allow(clippy::missing_safety_doc, missing_docs)]
174             #[doc(hidden)]
175             #[no_mangle]
176             pub unsafe extern "C" fn #ffi_rust_future_cancel(handle: ::uniffi::Handle) {
177                 ::uniffi::ffi::rust_future_cancel::<#return_type, crate::UniFfiTag>(handle)
178             }
179 
180             #[allow(clippy::missing_safety_doc, missing_docs)]
181             #[doc(hidden)]
182             #[no_mangle]
183             pub unsafe extern "C" fn #ffi_rust_future_complete(
184                 handle: ::uniffi::Handle,
185                 out_status: &mut ::uniffi::RustCallStatus
186             ) -> #return_type {
187                 ::uniffi::ffi::rust_future_complete::<#return_type, crate::UniFfiTag>(handle, out_status)
188             }
189 
190             #[allow(clippy::missing_safety_doc, missing_docs)]
191             #[doc(hidden)]
192             #[no_mangle]
193             pub unsafe extern "C" fn #ffi_rust_future_free(handle: ::uniffi::Handle) {
194                 ::uniffi::ffi::rust_future_free::<#return_type, crate::UniFfiTag>(handle)
195             }
196         }
197     })
198     .collect()
199 }
200