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