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 crate::{
6     default::{default_value_metadata_calls, DefaultValue},
7     export::{DefaultMap, ExportFnArgs, ExportedImplFnArgs},
8     util::{create_metadata_items, ident_to_string, mod_path, try_metadata_value_from_usize},
9 };
10 use proc_macro2::{Span, TokenStream};
11 use quote::quote;
12 use syn::{spanned::Spanned, FnArg, Ident, Pat, Receiver, ReturnType, Type};
13 
14 pub(crate) struct FnSignature {
15     pub kind: FnKind,
16     pub span: Span,
17     pub mod_path: String,
18     // The identifier of the Rust function.
19     pub ident: Ident,
20     // The foreign name for this function, usually == ident.
21     pub name: String,
22     pub is_async: bool,
23     pub receiver: Option<ReceiverArg>,
24     pub args: Vec<NamedArg>,
25     pub return_ty: TokenStream,
26     // Does this the return type look like a result?
27     // Only use this in UDL mode.
28     // In general, it's not reliable because it fails for type aliases.
29     pub looks_like_result: bool,
30     pub docstring: String,
31 }
32 
33 impl FnSignature {
new_function( sig: syn::Signature, args: ExportFnArgs, docstring: String, ) -> syn::Result<Self>34     pub(crate) fn new_function(
35         sig: syn::Signature,
36         args: ExportFnArgs,
37         docstring: String,
38     ) -> syn::Result<Self> {
39         Self::new(FnKind::Function, sig, args.name, args.defaults, docstring)
40     }
41 
new_method( self_ident: Ident, sig: syn::Signature, args: ExportedImplFnArgs, docstring: String, ) -> syn::Result<Self>42     pub(crate) fn new_method(
43         self_ident: Ident,
44         sig: syn::Signature,
45         args: ExportedImplFnArgs,
46         docstring: String,
47     ) -> syn::Result<Self> {
48         Self::new(
49             FnKind::Method { self_ident },
50             sig,
51             args.name,
52             args.defaults,
53             docstring,
54         )
55     }
56 
new_constructor( self_ident: Ident, sig: syn::Signature, args: ExportedImplFnArgs, docstring: String, ) -> syn::Result<Self>57     pub(crate) fn new_constructor(
58         self_ident: Ident,
59         sig: syn::Signature,
60         args: ExportedImplFnArgs,
61         docstring: String,
62     ) -> syn::Result<Self> {
63         Self::new(
64             FnKind::Constructor { self_ident },
65             sig,
66             args.name,
67             args.defaults,
68             docstring,
69         )
70     }
71 
new_trait_method( self_ident: Ident, sig: syn::Signature, args: ExportedImplFnArgs, index: u32, docstring: String, ) -> syn::Result<Self>72     pub(crate) fn new_trait_method(
73         self_ident: Ident,
74         sig: syn::Signature,
75         args: ExportedImplFnArgs,
76         index: u32,
77         docstring: String,
78     ) -> syn::Result<Self> {
79         Self::new(
80             FnKind::TraitMethod { self_ident, index },
81             sig,
82             args.name,
83             args.defaults,
84             docstring,
85         )
86     }
87 
new( kind: FnKind, sig: syn::Signature, name: Option<String>, mut defaults: DefaultMap, docstring: String, ) -> syn::Result<Self>88     pub(crate) fn new(
89         kind: FnKind,
90         sig: syn::Signature,
91         name: Option<String>,
92         mut defaults: DefaultMap,
93         docstring: String,
94     ) -> syn::Result<Self> {
95         let span = sig.span();
96         let ident = sig.ident;
97         let looks_like_result = looks_like_result(&sig.output);
98         let output = match sig.output {
99             ReturnType::Default => quote! { () },
100             ReturnType::Type(_, ty) => quote! { #ty },
101         };
102         let is_async = sig.asyncness.is_some();
103 
104         let mut input_iter = sig
105             .inputs
106             .into_iter()
107             .map(|a| Arg::new(a, &mut defaults))
108             .peekable();
109 
110         let receiver = input_iter
111             .next_if(|a| matches!(a, Ok(a) if a.is_receiver()))
112             .map(|a| match a {
113                 Ok(Arg {
114                     kind: ArgKind::Receiver(r),
115                     ..
116                 }) => r,
117                 _ => unreachable!(),
118             });
119         let args = input_iter
120             .map(|a| {
121                 a.and_then(|a| match a.kind {
122                     ArgKind::Named(named) => Ok(named),
123                     ArgKind::Receiver(_) => {
124                         Err(syn::Error::new(a.span, "Unexpected receiver argument"))
125                     }
126                 })
127             })
128             .collect::<syn::Result<Vec<_>>>()?;
129 
130         if let Some(ident) = defaults.idents().first() {
131             return Err(syn::Error::new(
132                 ident.span(),
133                 format!("Unknown default argument: {}", ident),
134             ));
135         }
136 
137         Ok(Self {
138             kind,
139             span,
140             mod_path: mod_path()?,
141             name: name.unwrap_or_else(|| ident_to_string(&ident)),
142             ident,
143             is_async,
144             receiver,
145             args,
146             return_ty: output,
147             looks_like_result,
148             docstring,
149         })
150     }
151 
lower_return_impl(&self) -> TokenStream152     pub fn lower_return_impl(&self) -> TokenStream {
153         let return_ty = &self.return_ty;
154         quote! {
155             <#return_ty as ::uniffi::LowerReturn<crate::UniFfiTag>>
156         }
157     }
158 
lift_return_impl(&self) -> TokenStream159     pub fn lift_return_impl(&self) -> TokenStream {
160         let return_ty = &self.return_ty;
161         quote! {
162             <#return_ty as ::uniffi::LiftReturn<crate::UniFfiTag>>
163         }
164     }
165 
166     /// Generate a closure that tries to lift all arguments into a tuple.
167     ///
168     /// The closure moves all scaffolding arguments into itself and returns:
169     ///   - The lifted argument tuple on success
170     ///   - The field name and error on failure (`Err(&'static str, anyhow::Error>`)
lift_closure(&self, self_lift: Option<TokenStream>) -> TokenStream171     pub fn lift_closure(&self, self_lift: Option<TokenStream>) -> TokenStream {
172         let arg_lifts = self.args.iter().map(|arg| {
173             let ident = &arg.ident;
174             let lift_impl = arg.lift_impl();
175             let name = &arg.name;
176             quote! {
177                 match #lift_impl::try_lift(#ident) {
178                     Ok(v) => v,
179                     Err(e) => return Err((#name, e)),
180                 }
181             }
182         });
183         let all_lifts = self_lift.into_iter().chain(arg_lifts);
184         quote! {
185             move || Ok((
186                 #(#all_lifts,)*
187             ))
188         }
189     }
190 
191     /// Call a Rust function from a [Self::lift_closure] success.
192     ///
193     /// This takes an Ok value returned by `lift_closure` with the name `uniffi_args` and generates
194     /// a series of parameters to pass to the Rust function.
rust_call_params(&self, self_lift: bool) -> TokenStream195     pub fn rust_call_params(&self, self_lift: bool) -> TokenStream {
196         let start_idx = if self_lift { 1 } else { 0 };
197         let args = self.args.iter().enumerate().map(|(i, arg)| {
198             let idx = syn::Index::from(i + start_idx);
199             let ty = &arg.ty;
200             match &arg.ref_type {
201                 None => quote! { uniffi_args.#idx },
202                 Some(ref_type) => quote! {
203                     <#ty as ::std::borrow::Borrow<#ref_type>>::borrow(&uniffi_args.#idx)
204                 },
205             }
206         });
207         quote! { #(#args),* }
208     }
209 
210     /// Parameters expressions for each of our arguments
params(&self) -> impl Iterator<Item = TokenStream> + '_211     pub fn params(&self) -> impl Iterator<Item = TokenStream> + '_ {
212         self.args.iter().map(NamedArg::param)
213     }
214 
215     /// Name of the scaffolding function to generate for this function
scaffolding_fn_ident(&self) -> syn::Result<Ident>216     pub fn scaffolding_fn_ident(&self) -> syn::Result<Ident> {
217         let name = &self.name;
218         let name = match &self.kind {
219             FnKind::Function => uniffi_meta::fn_symbol_name(&self.mod_path, name),
220             FnKind::Method { self_ident } | FnKind::TraitMethod { self_ident, .. } => {
221                 uniffi_meta::method_symbol_name(&self.mod_path, &ident_to_string(self_ident), name)
222             }
223             FnKind::Constructor { self_ident } => uniffi_meta::constructor_symbol_name(
224                 &self.mod_path,
225                 &ident_to_string(self_ident),
226                 name,
227             ),
228         };
229         Ok(Ident::new(&name, Span::call_site()))
230     }
231 
232     /// Scaffolding parameters expressions for each of our arguments
scaffolding_param_names(&self) -> impl Iterator<Item = TokenStream> + '_233     pub fn scaffolding_param_names(&self) -> impl Iterator<Item = TokenStream> + '_ {
234         self.args.iter().map(|a| {
235             let ident = &a.ident;
236             quote! { #ident }
237         })
238     }
239 
scaffolding_param_types(&self) -> impl Iterator<Item = TokenStream> + '_240     pub fn scaffolding_param_types(&self) -> impl Iterator<Item = TokenStream> + '_ {
241         self.args.iter().map(|a| {
242             let lift_impl = a.lift_impl();
243             quote! { #lift_impl::FfiType }
244         })
245     }
246 
247     /// Generate metadata items for this function
metadata_expr(&self) -> syn::Result<TokenStream>248     pub(crate) fn metadata_expr(&self) -> syn::Result<TokenStream> {
249         let Self {
250             name,
251             return_ty,
252             is_async,
253             mod_path,
254             docstring,
255             ..
256         } = &self;
257         let args_len = try_metadata_value_from_usize(
258             // Use param_lifts to calculate this instead of sig.inputs to avoid counting any self
259             // params
260             self.args.len(),
261             "UniFFI limits functions to 256 arguments",
262         )?;
263         let arg_metadata_calls = self
264             .args
265             .iter()
266             .map(NamedArg::arg_metadata)
267             .collect::<syn::Result<Vec<_>>>()?;
268 
269         match &self.kind {
270             FnKind::Function => Ok(quote! {
271                 ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::FUNC)
272                     .concat_str(#mod_path)
273                     .concat_str(#name)
274                     .concat_bool(#is_async)
275                     .concat_value(#args_len)
276                     #(#arg_metadata_calls)*
277                     .concat(<#return_ty as ::uniffi::LowerReturn<crate::UniFfiTag>>::TYPE_ID_META)
278                     .concat_long_str(#docstring)
279             }),
280 
281             FnKind::Method { self_ident } => {
282                 let object_name = ident_to_string(self_ident);
283                 Ok(quote! {
284                     ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::METHOD)
285                         .concat_str(#mod_path)
286                         .concat_str(#object_name)
287                         .concat_str(#name)
288                         .concat_bool(#is_async)
289                         .concat_value(#args_len)
290                         #(#arg_metadata_calls)*
291                         .concat(<#return_ty as ::uniffi::LowerReturn<crate::UniFfiTag>>::TYPE_ID_META)
292                         .concat_long_str(#docstring)
293                 })
294             }
295 
296             FnKind::TraitMethod { self_ident, index } => {
297                 let object_name = ident_to_string(self_ident);
298                 Ok(quote! {
299                     ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::TRAIT_METHOD)
300                         .concat_str(#mod_path)
301                         .concat_str(#object_name)
302                         .concat_u32(#index)
303                         .concat_str(#name)
304                         .concat_bool(#is_async)
305                         .concat_value(#args_len)
306                         #(#arg_metadata_calls)*
307                         .concat(<#return_ty as ::uniffi::LowerReturn<crate::UniFfiTag>>::TYPE_ID_META)
308                         .concat_long_str(#docstring)
309                 })
310             }
311 
312             FnKind::Constructor { self_ident } => {
313                 let object_name = ident_to_string(self_ident);
314                 Ok(quote! {
315                     ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::CONSTRUCTOR)
316                         .concat_str(#mod_path)
317                         .concat_str(#object_name)
318                         .concat_str(#name)
319                         .concat_bool(#is_async)
320                         .concat_value(#args_len)
321                         #(#arg_metadata_calls)*
322                         .concat(<#return_ty as ::uniffi::LowerReturn<crate::UniFfiTag>>::TYPE_ID_META)
323                         .concat_long_str(#docstring)
324                 })
325             }
326         }
327     }
328 
metadata_items(&self) -> syn::Result<TokenStream>329     pub(crate) fn metadata_items(&self) -> syn::Result<TokenStream> {
330         let Self { name, .. } = &self;
331         match &self.kind {
332             FnKind::Function => Ok(create_metadata_items(
333                 "func",
334                 name,
335                 self.metadata_expr()?,
336                 Some(self.checksum_symbol_name()),
337             )),
338 
339             FnKind::Method { self_ident } => {
340                 let object_name = ident_to_string(self_ident);
341                 Ok(create_metadata_items(
342                     "method",
343                     &format!("{object_name}_{name}"),
344                     self.metadata_expr()?,
345                     Some(self.checksum_symbol_name()),
346                 ))
347             }
348 
349             FnKind::TraitMethod { self_ident, .. } => {
350                 let object_name = ident_to_string(self_ident);
351                 Ok(create_metadata_items(
352                     "method",
353                     &format!("{object_name}_{name}"),
354                     self.metadata_expr()?,
355                     Some(self.checksum_symbol_name()),
356                 ))
357             }
358 
359             FnKind::Constructor { self_ident } => {
360                 let object_name = ident_to_string(self_ident);
361                 Ok(create_metadata_items(
362                     "constructor",
363                     &format!("{object_name}_{name}"),
364                     self.metadata_expr()?,
365                     Some(self.checksum_symbol_name()),
366                 ))
367             }
368         }
369     }
370 
371     /// Generate metadata items for callback interfaces
372     ///
373     /// Unfortunately, most of this is duplicate code from [Self::metadata_items] and
374     /// [Self::metadata_expr].  However, one issue with that code is that it needs to assume if the
375     /// arguments are being lifted vs lowered in order to get TYPE_ID_META.  That code uses
376     /// `<Type as Lift>::TYPE_ID_META` for arguments and `<Type as LowerReturn>::TYPE_ID_META` for
377     /// return types, which works for accidental/historical reasons.
378     ///
379     /// The one exception is callback interfaces (#1947), which are handled by this method.
380     ///
381     /// TODO: fix the metadata system so that this is not needed.
metadata_items_for_callback_interface(&self) -> syn::Result<TokenStream>382     pub(crate) fn metadata_items_for_callback_interface(&self) -> syn::Result<TokenStream> {
383         let Self {
384             name,
385             return_ty,
386             is_async,
387             mod_path,
388             docstring,
389             ..
390         } = &self;
391         match &self.kind {
392             FnKind::TraitMethod {
393                 self_ident, index, ..
394             } => {
395                 let object_name = ident_to_string(self_ident);
396                 let args_len = try_metadata_value_from_usize(
397                     // Use param_lifts to calculate this instead of sig.inputs to avoid counting any self
398                     // params
399                     self.args.len(),
400                     "UniFFI limits functions to 256 arguments",
401                 )?;
402                 let arg_metadata_calls = self
403                     .args
404                     .iter()
405                     .map(NamedArg::arg_metadata)
406                     .collect::<syn::Result<Vec<_>>>()?;
407                 let metadata_expr = quote! {
408                     ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::TRAIT_METHOD)
409                         .concat_str(#mod_path)
410                         .concat_str(#object_name)
411                         .concat_u32(#index)
412                         .concat_str(#name)
413                         .concat_bool(#is_async)
414                         .concat_value(#args_len)
415                         #(#arg_metadata_calls)*
416                         .concat(<#return_ty as ::uniffi::LiftReturn<crate::UniFfiTag>>::TYPE_ID_META)
417                         .concat_long_str(#docstring)
418                 };
419                 Ok(create_metadata_items(
420                     "method",
421                     &format!("{object_name}_{name}"),
422                     metadata_expr,
423                     Some(self.checksum_symbol_name()),
424                 ))
425             }
426 
427             // This should never happen and indicates an error in the internal code
428             _ => panic!(
429                 "metadata_items_for_callback_interface can only be called with `TraitMethod` sigs"
430             ),
431         }
432     }
433 
checksum_symbol_name(&self) -> String434     pub(crate) fn checksum_symbol_name(&self) -> String {
435         let name = &self.name;
436         match &self.kind {
437             FnKind::Function => uniffi_meta::fn_checksum_symbol_name(&self.mod_path, name),
438             FnKind::Method { self_ident } | FnKind::TraitMethod { self_ident, .. } => {
439                 uniffi_meta::method_checksum_symbol_name(
440                     &self.mod_path,
441                     &ident_to_string(self_ident),
442                     name,
443                 )
444             }
445             FnKind::Constructor { self_ident } => uniffi_meta::constructor_checksum_symbol_name(
446                 &self.mod_path,
447                 &ident_to_string(self_ident),
448                 name,
449             ),
450         }
451     }
452 }
453 
454 pub(crate) struct Arg {
455     pub(crate) span: Span,
456     pub(crate) kind: ArgKind,
457 }
458 
459 pub(crate) enum ArgKind {
460     Receiver(ReceiverArg),
461     Named(NamedArg),
462 }
463 
464 impl Arg {
new(syn_arg: FnArg, defaults: &mut DefaultMap) -> syn::Result<Self>465     fn new(syn_arg: FnArg, defaults: &mut DefaultMap) -> syn::Result<Self> {
466         let span = syn_arg.span();
467         let kind = match syn_arg {
468             FnArg::Typed(p) => match *p.pat {
469                 Pat::Ident(i) => Ok(ArgKind::Named(NamedArg::new(i.ident, &p.ty, defaults)?)),
470                 _ => Err(syn::Error::new_spanned(p, "Argument name missing")),
471             },
472             FnArg::Receiver(receiver) => Ok(ArgKind::Receiver(ReceiverArg::from(receiver))),
473         }?;
474 
475         Ok(Self { span, kind })
476     }
477 
is_receiver(&self) -> bool478     pub(crate) fn is_receiver(&self) -> bool {
479         matches!(self.kind, ArgKind::Receiver(_))
480     }
481 }
482 
483 pub(crate) enum ReceiverArg {
484     Ref,
485     Arc,
486 }
487 
488 impl From<Receiver> for ReceiverArg {
from(receiver: Receiver) -> Self489     fn from(receiver: Receiver) -> Self {
490         if let Type::Path(p) = *receiver.ty {
491             if let Some(segment) = p.path.segments.last() {
492                 // This comparison will fail if a user uses a typedef for Arc.  Maybe we could
493                 // implement some system like TYPE_ID_META to figure this out from the type system.
494                 // However, this seems good enough for now.
495                 if segment.ident == "Arc" {
496                     return ReceiverArg::Arc;
497                 }
498             }
499         }
500         Self::Ref
501     }
502 }
503 
504 pub(crate) struct NamedArg {
505     pub(crate) ident: Ident,
506     pub(crate) name: String,
507     pub(crate) ty: TokenStream,
508     pub(crate) ref_type: Option<Type>,
509     pub(crate) default: Option<DefaultValue>,
510 }
511 
512 impl NamedArg {
new(ident: Ident, ty: &Type, defaults: &mut DefaultMap) -> syn::Result<Self>513     pub(crate) fn new(ident: Ident, ty: &Type, defaults: &mut DefaultMap) -> syn::Result<Self> {
514         Ok(match ty {
515             Type::Reference(r) => {
516                 let inner = &r.elem;
517                 Self {
518                     name: ident_to_string(&ident),
519                     ty: quote! { <#inner as ::uniffi::LiftRef<crate::UniFfiTag>>::LiftType },
520                     ref_type: Some(*inner.clone()),
521                     default: defaults.remove(&ident),
522                     ident,
523                 }
524             }
525             _ => Self {
526                 name: ident_to_string(&ident),
527                 ty: quote! { #ty },
528                 ref_type: None,
529                 default: defaults.remove(&ident),
530                 ident,
531             },
532         })
533     }
534 
lift_impl(&self) -> TokenStream535     pub(crate) fn lift_impl(&self) -> TokenStream {
536         let ty = &self.ty;
537         quote! { <#ty as ::uniffi::Lift<crate::UniFfiTag>> }
538     }
539 
lower_impl(&self) -> TokenStream540     pub(crate) fn lower_impl(&self) -> TokenStream {
541         let ty = &self.ty;
542         quote! { <#ty as ::uniffi::Lower<crate::UniFfiTag>> }
543     }
544 
545     /// Generate the parameter for this Arg
param(&self) -> TokenStream546     pub(crate) fn param(&self) -> TokenStream {
547         let ident = &self.ident;
548         let ty = &self.ty;
549         quote! { #ident: #ty }
550     }
551 
arg_metadata(&self) -> syn::Result<TokenStream>552     pub(crate) fn arg_metadata(&self) -> syn::Result<TokenStream> {
553         let name = &self.name;
554         let lift_impl = self.lift_impl();
555         let default_calls = default_value_metadata_calls(&self.default)?;
556         Ok(quote! {
557             .concat_str(#name)
558             .concat(#lift_impl::TYPE_ID_META)
559             #default_calls
560         })
561     }
562 }
563 
looks_like_result(return_type: &ReturnType) -> bool564 fn looks_like_result(return_type: &ReturnType) -> bool {
565     if let ReturnType::Type(_, ty) = return_type {
566         if let Type::Path(p) = &**ty {
567             if let Some(seg) = p.path.segments.last() {
568                 if seg.ident == "Result" {
569                     return true;
570                 }
571             }
572         }
573     }
574 
575     false
576 }
577 
578 #[derive(Debug)]
579 pub(crate) enum FnKind {
580     Function,
581     Constructor { self_ident: Ident },
582     Method { self_ident: Ident },
583     TraitMethod { self_ident: Ident, index: u32 },
584 }
585