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::{Ident, Span, TokenStream};
6 use quote::{quote, quote_spanned};
7
8 use uniffi_meta::ObjectImpl;
9
10 use crate::{
11 export::{
12 attributes::ExportTraitArgs, callback_interface, gen_method_scaffolding, item::ImplItem,
13 },
14 object::interface_meta_static_var,
15 util::{ident_to_string, tagged_impl_header},
16 };
17
gen_trait_scaffolding( mod_path: &str, args: ExportTraitArgs, self_ident: Ident, items: Vec<ImplItem>, udl_mode: bool, with_foreign: bool, docstring: String, ) -> syn::Result<TokenStream>18 pub(super) fn gen_trait_scaffolding(
19 mod_path: &str,
20 args: ExportTraitArgs,
21 self_ident: Ident,
22 items: Vec<ImplItem>,
23 udl_mode: bool,
24 with_foreign: bool,
25 docstring: String,
26 ) -> syn::Result<TokenStream> {
27 if let Some(rt) = args.async_runtime {
28 return Err(syn::Error::new_spanned(rt, "not supported for traits"));
29 }
30 let trait_name = ident_to_string(&self_ident);
31 let trait_impl = with_foreign.then(|| {
32 callback_interface::trait_impl(mod_path, &self_ident, &items)
33 .unwrap_or_else(|e| e.into_compile_error())
34 });
35
36 let clone_fn_ident = Ident::new(
37 &uniffi_meta::clone_fn_symbol_name(mod_path, &trait_name),
38 Span::call_site(),
39 );
40 let free_fn_ident = Ident::new(
41 &uniffi_meta::free_fn_symbol_name(mod_path, &trait_name),
42 Span::call_site(),
43 );
44
45 let helper_fn_tokens = quote! {
46 #[doc(hidden)]
47 #[no_mangle]
48 /// Clone a pointer to this object type
49 ///
50 /// Safety: Only pass pointers returned by a UniFFI call. Do not pass pointers that were
51 /// passed to the free function.
52 pub unsafe extern "C" fn #clone_fn_ident(
53 ptr: *const ::std::ffi::c_void,
54 call_status: &mut ::uniffi::RustCallStatus
55 ) -> *const ::std::ffi::c_void {
56 uniffi::rust_call(call_status, || {
57 let ptr = ptr as *mut std::sync::Arc<dyn #self_ident>;
58 let arc = unsafe { ::std::sync::Arc::clone(&*ptr) };
59 Ok(::std::boxed::Box::into_raw(::std::boxed::Box::new(arc)) as *const ::std::ffi::c_void)
60 })
61 }
62
63 #[doc(hidden)]
64 #[no_mangle]
65 /// Free a pointer to this object type
66 ///
67 /// Safety: Only pass pointers returned by a UniFFI call. Do not pass pointers that were
68 /// passed to the free function.
69 ///
70 /// Note: clippy doesn't complain about this being unsafe, but it definitely is since it
71 /// calls `Box::from_raw`.
72 pub unsafe extern "C" fn #free_fn_ident(
73 ptr: *const ::std::ffi::c_void,
74 call_status: &mut ::uniffi::RustCallStatus
75 ) {
76 uniffi::rust_call(call_status, || {
77 assert!(!ptr.is_null());
78 drop(unsafe { ::std::boxed::Box::from_raw(ptr as *mut std::sync::Arc<dyn #self_ident>) });
79 Ok(())
80 });
81 }
82 };
83
84 let impl_tokens: TokenStream = items
85 .into_iter()
86 .map(|item| match item {
87 ImplItem::Method(sig) => gen_method_scaffolding(sig, &None, udl_mode),
88 _ => unreachable!("traits have no constructors"),
89 })
90 .collect::<syn::Result<_>>()?;
91
92 let meta_static_var = (!udl_mode).then(|| {
93 let imp = if with_foreign {
94 ObjectImpl::CallbackTrait
95 } else {
96 ObjectImpl::Trait
97 };
98 interface_meta_static_var(&self_ident, imp, mod_path, docstring)
99 .unwrap_or_else(syn::Error::into_compile_error)
100 });
101 let ffi_converter_tokens = ffi_converter(mod_path, &self_ident, udl_mode, with_foreign);
102
103 Ok(quote_spanned! { self_ident.span() =>
104 #meta_static_var
105 #helper_fn_tokens
106 #trait_impl
107 #impl_tokens
108 #ffi_converter_tokens
109 })
110 }
111
ffi_converter( mod_path: &str, trait_ident: &Ident, udl_mode: bool, with_foreign: bool, ) -> TokenStream112 pub(crate) fn ffi_converter(
113 mod_path: &str,
114 trait_ident: &Ident,
115 udl_mode: bool,
116 with_foreign: bool,
117 ) -> TokenStream {
118 let impl_spec = tagged_impl_header("FfiConverterArc", "e! { dyn #trait_ident }, udl_mode);
119 let lift_ref_impl_spec = tagged_impl_header("LiftRef", "e! { dyn #trait_ident }, udl_mode);
120 let trait_name = ident_to_string(trait_ident);
121 let try_lift = if with_foreign {
122 let trait_impl_ident = callback_interface::trait_impl_ident(&trait_name);
123 quote! {
124 fn try_lift(v: Self::FfiType) -> ::uniffi::deps::anyhow::Result<::std::sync::Arc<Self>> {
125 Ok(::std::sync::Arc::new(<#trait_impl_ident>::new(v as u64)))
126 }
127 }
128 } else {
129 quote! {
130 fn try_lift(v: Self::FfiType) -> ::uniffi::deps::anyhow::Result<::std::sync::Arc<Self>> {
131 unsafe {
132 Ok(*::std::boxed::Box::from_raw(v as *mut ::std::sync::Arc<Self>))
133 }
134 }
135 }
136 };
137 let metadata_code = if with_foreign {
138 quote! { ::uniffi::metadata::codes::TYPE_CALLBACK_TRAIT_INTERFACE }
139 } else {
140 quote! { ::uniffi::metadata::codes::TYPE_TRAIT_INTERFACE }
141 };
142
143 quote! {
144 // All traits must be `Sync + Send`. The generated scaffolding will fail to compile
145 // if they are not, but unfortunately it fails with an unactionably obscure error message.
146 // By asserting the requirement explicitly, we help Rust produce a more scrutable error message
147 // and thus help the user debug why the requirement isn't being met.
148 uniffi::deps::static_assertions::assert_impl_all!(dyn #trait_ident: ::core::marker::Sync, ::core::marker::Send);
149
150 unsafe #impl_spec {
151 type FfiType = *const ::std::os::raw::c_void;
152
153 fn lower(obj: ::std::sync::Arc<Self>) -> Self::FfiType {
154 ::std::boxed::Box::into_raw(::std::boxed::Box::new(obj)) as *const ::std::os::raw::c_void
155 }
156
157 #try_lift
158
159 fn write(obj: ::std::sync::Arc<Self>, buf: &mut Vec<u8>) {
160 ::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8);
161 ::uniffi::deps::bytes::BufMut::put_u64(
162 buf,
163 <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::lower(obj) as u64,
164 );
165 }
166
167 fn try_read(buf: &mut &[u8]) -> ::uniffi::Result<::std::sync::Arc<Self>> {
168 ::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8);
169 ::uniffi::check_remaining(buf, 8)?;
170 <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::try_lift(
171 ::uniffi::deps::bytes::Buf::get_u64(buf) as Self::FfiType)
172 }
173
174 const TYPE_ID_META: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(#metadata_code)
175 .concat_str(#mod_path)
176 .concat_str(#trait_name);
177 }
178
179 unsafe #lift_ref_impl_spec {
180 type LiftType = ::std::sync::Arc<dyn #trait_ident>;
181 }
182 }
183 }
184