1 use proc_macro2::{Ident, Span, TokenStream};
2 use quote::quote;
3 use syn::DeriveInput;
4 
5 use crate::util::{
6     create_metadata_items, extract_docstring, ident_to_string, mod_path, tagged_impl_header,
7 };
8 use uniffi_meta::ObjectImpl;
9 
expand_object(input: DeriveInput, udl_mode: bool) -> syn::Result<TokenStream>10 pub fn expand_object(input: DeriveInput, udl_mode: bool) -> syn::Result<TokenStream> {
11     let module_path = mod_path()?;
12     let ident = &input.ident;
13     let docstring = extract_docstring(&input.attrs)?;
14     let name = ident_to_string(ident);
15     let clone_fn_ident = Ident::new(
16         &uniffi_meta::clone_fn_symbol_name(&module_path, &name),
17         Span::call_site(),
18     );
19     let free_fn_ident = Ident::new(
20         &uniffi_meta::free_fn_symbol_name(&module_path, &name),
21         Span::call_site(),
22     );
23     let meta_static_var = (!udl_mode).then(|| {
24         interface_meta_static_var(ident, ObjectImpl::Struct, &module_path, docstring)
25             .unwrap_or_else(syn::Error::into_compile_error)
26     });
27     let interface_impl = interface_impl(ident, udl_mode);
28 
29     Ok(quote! {
30         #[doc(hidden)]
31         #[no_mangle]
32         pub unsafe extern "C" fn #clone_fn_ident(
33             ptr: *const ::std::ffi::c_void,
34             call_status: &mut ::uniffi::RustCallStatus
35         ) -> *const ::std::ffi::c_void {
36             uniffi::rust_call(call_status, || {
37                 unsafe { ::std::sync::Arc::increment_strong_count(ptr) };
38                 Ok(ptr)
39             })
40         }
41 
42         #[doc(hidden)]
43         #[no_mangle]
44         pub unsafe extern "C" fn #free_fn_ident(
45             ptr: *const ::std::ffi::c_void,
46             call_status: &mut ::uniffi::RustCallStatus
47         ) {
48             uniffi::rust_call(call_status, || {
49                 assert!(!ptr.is_null());
50                 let ptr = ptr.cast::<#ident>();
51                 unsafe {
52                     ::std::sync::Arc::decrement_strong_count(ptr);
53                 }
54                 Ok(())
55             });
56         }
57 
58         #interface_impl
59         #meta_static_var
60     })
61 }
62 
interface_impl(ident: &Ident, udl_mode: bool) -> TokenStream63 pub(crate) fn interface_impl(ident: &Ident, udl_mode: bool) -> TokenStream {
64     let name = ident_to_string(ident);
65     let impl_spec = tagged_impl_header("FfiConverterArc", ident, udl_mode);
66     let lower_return_impl_spec = tagged_impl_header("LowerReturn", ident, udl_mode);
67     let lift_ref_impl_spec = tagged_impl_header("LiftRef", ident, udl_mode);
68     let mod_path = match mod_path() {
69         Ok(p) => p,
70         Err(e) => return e.into_compile_error(),
71     };
72 
73     quote! {
74         // All Object structs must be `Sync + Send`. The generated scaffolding will fail to compile
75         // if they are not, but unfortunately it fails with an unactionably obscure error message.
76         // By asserting the requirement explicitly, we help Rust produce a more scrutable error message
77         // and thus help the user debug why the requirement isn't being met.
78         uniffi::deps::static_assertions::assert_impl_all!(#ident: ::core::marker::Sync, ::core::marker::Send);
79 
80         #[doc(hidden)]
81         #[automatically_derived]
82         /// Support for passing reference-counted shared objects via the FFI.
83         ///
84         /// To avoid dealing with complex lifetime semantics over the FFI, any data passed
85         /// by reference must be encapsulated in an `Arc`, and must be safe to share
86         /// across threads.
87         unsafe #impl_spec {
88             // Don't use a pointer to <T> as that requires a `pub <T>`
89             type FfiType = *const ::std::os::raw::c_void;
90 
91             /// When lowering, we have an owned `Arc` and we transfer that ownership
92             /// to the foreign-language code, "leaking" it out of Rust's ownership system
93             /// as a raw pointer. This works safely because we have unique ownership of `self`.
94             /// The foreign-language code is responsible for freeing this by calling the
95             /// `ffi_object_free` FFI function provided by the corresponding UniFFI type.
96             ///
97             /// Safety: when freeing the resulting pointer, the foreign-language code must
98             /// call the destructor function specific to the type `T`. Calling the destructor
99             /// function for other types may lead to undefined behaviour.
100             fn lower(obj: ::std::sync::Arc<Self>) -> Self::FfiType {
101                 ::std::sync::Arc::into_raw(obj) as Self::FfiType
102             }
103 
104             /// When lifting, we receive an owned `Arc` that the foreign language code cloned.
105             fn try_lift(v: Self::FfiType) -> ::uniffi::Result<::std::sync::Arc<Self>> {
106                 let v = v as *const #ident;
107                 Ok(unsafe { ::std::sync::Arc::<Self>::from_raw(v) })
108             }
109 
110             /// When writing as a field of a complex structure, make a clone and transfer ownership
111             /// of it to the foreign-language code by writing its pointer into the buffer.
112             /// The foreign-language code is responsible for freeing this by calling the
113             /// `ffi_object_free` FFI function provided by the corresponding UniFFI type.
114             ///
115             /// Safety: when freeing the resulting pointer, the foreign-language code must
116             /// call the destructor function specific to the type `T`. Calling the destructor
117             /// function for other types may lead to undefined behaviour.
118             fn write(obj: ::std::sync::Arc<Self>, buf: &mut Vec<u8>) {
119                 ::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8);
120                 ::uniffi::deps::bytes::BufMut::put_u64(buf, <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::lower(obj) as u64);
121             }
122 
123             /// When reading as a field of a complex structure, we receive a "borrow" of the `Arc`
124             /// that is owned by the foreign-language code, and make a clone for our own use.
125             ///
126             /// Safety: the buffer must contain a pointer previously obtained by calling
127             /// the `lower()` or `write()` method of this impl.
128             fn try_read(buf: &mut &[u8]) -> ::uniffi::Result<::std::sync::Arc<Self>> {
129                 ::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8);
130                 ::uniffi::check_remaining(buf, 8)?;
131                 <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::try_lift(::uniffi::deps::bytes::Buf::get_u64(buf) as Self::FfiType)
132             }
133 
134             const TYPE_ID_META: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::TYPE_INTERFACE)
135                 .concat_str(#mod_path)
136                 .concat_str(#name);
137         }
138 
139         unsafe #lower_return_impl_spec {
140             type ReturnType = <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::FfiType;
141 
142             fn lower_return(obj: Self) -> ::std::result::Result<Self::ReturnType, ::uniffi::RustBuffer> {
143                 Ok(<Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::lower(::std::sync::Arc::new(obj)))
144             }
145 
146             const TYPE_ID_META: ::uniffi::MetadataBuffer = <Self as ::uniffi::FfiConverterArc<crate::UniFfiTag>>::TYPE_ID_META;
147         }
148 
149         unsafe #lift_ref_impl_spec {
150             type LiftType = ::std::sync::Arc<Self>;
151         }
152     }
153 }
154 
interface_meta_static_var( ident: &Ident, imp: ObjectImpl, module_path: &str, docstring: String, ) -> syn::Result<TokenStream>155 pub(crate) fn interface_meta_static_var(
156     ident: &Ident,
157     imp: ObjectImpl,
158     module_path: &str,
159     docstring: String,
160 ) -> syn::Result<TokenStream> {
161     let name = ident_to_string(ident);
162     let code = match imp {
163         ObjectImpl::Struct => quote! { ::uniffi::metadata::codes::INTERFACE },
164         ObjectImpl::Trait => quote! { ::uniffi::metadata::codes::TRAIT_INTERFACE },
165         ObjectImpl::CallbackTrait => quote! { ::uniffi::metadata::codes::CALLBACK_TRAIT_INTERFACE },
166     };
167 
168     Ok(create_metadata_items(
169         "interface",
170         &name,
171         quote! {
172             ::uniffi::MetadataBuffer::from_code(#code)
173                 .concat_str(#module_path)
174                 .concat_str(#name)
175                 .concat_long_str(#docstring)
176         },
177         None,
178     ))
179 }
180