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