napi_derive_backend/codegen/
fn.rs

1use proc_macro2::{Ident, Span, TokenStream};
2use quote::ToTokens;
3use syn::{spanned::Spanned, Type, TypePath};
4
5use crate::{
6  codegen::{get_intermediate_ident, js_mod_to_token_stream},
7  BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, NapiFn, NapiFnArgKind, TryToTokens,
8  TYPEDARRAY_SLICE_TYPES,
9};
10
11impl TryToTokens for NapiFn {
12  fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
13    let name_str = self.name.to_string();
14    let intermediate_ident = get_intermediate_ident(&name_str);
15    let args_len = self.args.len();
16
17    let ArgConversions {
18      arg_conversions,
19      args: arg_names,
20      refs,
21      mut_ref_spans,
22      unsafe_,
23    } = self.gen_arg_conversions()?;
24    // The JS engine can't properly track mutability in an async context, so refuse to compile
25    // code that tries to use async and mutability together without `unsafe` mark.
26    if self.is_async && !mut_ref_spans.is_empty() && !unsafe_ {
27      return Diagnostic::from_vec(
28        mut_ref_spans
29          .into_iter()
30          .map(|s| Diagnostic::span_error(s, "mutable reference is unsafe with async"))
31          .collect(),
32      );
33    }
34    if Some(FnSelf::MutRef) == self.fn_self && self.is_async && !self.unsafe_ {
35      return Err(Diagnostic::span_error(
36        self.name.span(),
37        "&mut self in async napi methods should be marked as unsafe",
38      ));
39    }
40    let arg_ref_count = refs.len();
41    let receiver = self.gen_fn_receiver();
42    let receiver_ret_name = Ident::new("_ret", Span::call_site());
43    let ret = self.gen_fn_return(&receiver_ret_name)?;
44    let register = self.gen_fn_register();
45    let attrs = &self.attrs;
46
47    let build_ref_container = if self.is_async {
48      quote! {
49          struct NapiRefContainer([napi::sys::napi_ref; #arg_ref_count]);
50          impl NapiRefContainer {
51            fn drop(self, env: napi::sys::napi_env) {
52              for r in self.0.into_iter() {
53                assert_eq!(
54                  unsafe { napi::sys::napi_reference_unref(env, r, &mut 0) },
55                  napi::sys::Status::napi_ok,
56                  "failed to delete napi ref"
57                );
58                assert_eq!(
59                  unsafe { napi::sys::napi_delete_reference(env, r) },
60                  napi::sys::Status::napi_ok,
61                  "failed to delete napi ref"
62                );
63              }
64            }
65          }
66          unsafe impl Send for NapiRefContainer {}
67          unsafe impl Sync for NapiRefContainer {}
68          let _make_ref = |a: ::std::ptr::NonNull<napi::bindgen_prelude::sys::napi_value__>| {
69            let mut node_ref = ::std::mem::MaybeUninit::uninit();
70            napi::bindgen_prelude::check_status!(unsafe {
71                napi::bindgen_prelude::sys::napi_create_reference(env, a.as_ptr(), 1, node_ref.as_mut_ptr())
72              },
73              "failed to create napi ref"
74            )?;
75            Ok::<napi::sys::napi_ref, napi::Error>(unsafe { node_ref.assume_init() })
76          };
77          let mut _args_array = [::std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_ref__>(); #arg_ref_count];
78          let mut _arg_write_index = 0;
79
80          #(#refs)*
81
82          #[cfg(debug_assertions)]
83          {
84              for a in &_args_array {
85                assert!(!a.is_null(), "failed to initialize napi ref");
86              }
87          }
88          let _args_ref = NapiRefContainer(_args_array);
89      }
90    } else {
91      quote! {}
92    };
93    let native_call = if !self.is_async {
94      if self.within_async_runtime {
95        quote! {
96          napi::bindgen_prelude::within_runtime_if_available(move || {
97            let #receiver_ret_name = {
98              #receiver(#(#arg_names),*)
99            };
100            #ret
101          })
102        }
103      } else {
104        quote! {
105          let #receiver_ret_name = {
106            #receiver(#(#arg_names),*)
107          };
108          #ret
109        }
110      }
111    } else {
112      let call = if self.is_ret_result {
113        quote! { #receiver(#(#arg_names),*).await }
114      } else {
115        let ret_type = if let Some(t) = &self.ret {
116          quote! { #t }
117        } else {
118          quote! { () }
119        };
120        quote! { Ok::<#ret_type, napi::Error>(#receiver(#(#arg_names),*).await) }
121      };
122      quote! {
123        napi::bindgen_prelude::execute_tokio_future(env, async move { #call }, move |env, #receiver_ret_name| {
124          _args_ref.drop(env);
125          #ret
126        })
127      }
128    };
129
130    // async factory only
131    let use_after_async = if self.is_async && self.parent.is_some() && self.fn_self.is_none() {
132      quote! { true }
133    } else {
134      quote! { false }
135    };
136
137    let function_call_inner = quote! {
138      napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None, #use_after_async).and_then(|#[allow(unused_mut)] mut cb| {
139          let __wrapped_env = napi::bindgen_prelude::Env::from(env);
140          #build_ref_container
141          #(#arg_conversions)*
142          #native_call
143        })
144    };
145
146    let function_call = if args_len == 0
147      && self.fn_self.is_none()
148      && self.kind != FnKind::Constructor
149      && self.kind != FnKind::Factory
150      && !self.is_async
151    {
152      quote! { #native_call }
153    } else if self.kind == FnKind::Constructor {
154      let return_from_factory = if self.catch_unwind {
155        quote! { return Ok(std::ptr::null_mut()); }
156      } else {
157        quote! { return std::ptr::null_mut(); }
158      };
159      quote! {
160        // constructor function is called from class `factory`
161        // so we should skip the original `constructor` logic
162        if napi::__private::___CALL_FROM_FACTORY.with(|inner| inner.load(std::sync::atomic::Ordering::Relaxed)) {
163            #return_from_factory
164        }
165        #function_call_inner
166      }
167    } else {
168      function_call_inner
169    };
170
171    let function_call = if self.catch_unwind {
172      quote! {
173        {
174          std::panic::catch_unwind(|| { #function_call })
175            .map_err(|e| {
176              let message = {
177                if let Some(string) = e.downcast_ref::<String>() {
178                  string.clone()
179                } else if let Some(string) = e.downcast_ref::<&str>() {
180                  string.to_string()
181                } else {
182                  format!("panic from Rust code: {:?}", e)
183                }
184              };
185              napi::Error::new(napi::Status::GenericFailure, message)
186            })
187            .and_then(|r| r)
188        }
189      }
190    } else {
191      quote! {
192        #function_call
193      }
194    };
195
196    (quote! {
197      #(#attrs)*
198      #[doc(hidden)]
199      #[allow(non_snake_case)]
200      #[allow(clippy::all)]
201      extern "C" fn #intermediate_ident(
202        env: napi::bindgen_prelude::sys::napi_env,
203        cb: napi::bindgen_prelude::sys::napi_callback_info
204      ) -> napi::bindgen_prelude::sys::napi_value {
205        unsafe {
206          #function_call.unwrap_or_else(|e| {
207            napi::bindgen_prelude::JsError::from(e).throw_into(env);
208            std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
209          })
210        }
211      }
212
213      #register
214    })
215    .to_tokens(tokens);
216
217    Ok(())
218  }
219}
220
221impl NapiFn {
222  fn gen_arg_conversions(&self) -> BindgenResult<ArgConversions> {
223    let mut arg_conversions = vec![];
224    let mut args = vec![];
225    let mut refs = vec![];
226    let mut mut_ref_spans = vec![];
227    let make_ref = |input| {
228      quote! {
229        _args_array[_arg_write_index] = _make_ref(
230          ::std::ptr::NonNull::new(#input)
231            .ok_or_else(|| napi::Error::new(napi::Status::InvalidArg, "referenced ptr is null".to_owned()))?
232        )?;
233        _arg_write_index += 1;
234      }
235    };
236
237    // fetch this
238    if let Some(parent) = &self.parent {
239      match self.fn_self {
240        Some(FnSelf::Ref) => {
241          refs.push(make_ref(quote! { cb.this }));
242          arg_conversions.push(quote! {
243            let this_ptr = cb.unwrap_raw::<#parent>()?;
244            let this: &#parent = Box::leak(Box::from_raw(this_ptr));
245          });
246        }
247        Some(FnSelf::MutRef) => {
248          refs.push(make_ref(quote! { cb.this }));
249          arg_conversions.push(quote! {
250            let this_ptr = cb.unwrap_raw::<#parent>()?;
251            let this: &mut #parent = Box::leak(Box::from_raw(this_ptr));
252          });
253        }
254        _ => {}
255      };
256    }
257
258    let mut skipped_arg_count = 0;
259    for (i, arg) in self.args.iter().enumerate() {
260      let i = i - skipped_arg_count;
261      let ident = Ident::new(&format!("arg{}", i), Span::call_site());
262
263      match &arg.kind {
264        NapiFnArgKind::PatType(path) => {
265          if &path.ty.to_token_stream().to_string() == "Env" {
266            args.push(quote! { __wrapped_env });
267            skipped_arg_count += 1;
268          } else {
269            let is_in_class = self.parent.is_some();
270            if let syn::Type::Path(path) = path.ty.as_ref() {
271              if let Some(p) = path.path.segments.last() {
272                if p.ident == "Reference" {
273                  if !is_in_class {
274                    bail_span!(p, "`Reference` is only allowed in class methods");
275                  }
276                  if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
277                    args: angle_bracketed_args,
278                    ..
279                  }) = &p.arguments
280                  {
281                    if let Some(syn::GenericArgument::Type(syn::Type::Path(path))) =
282                      angle_bracketed_args.first()
283                    {
284                      if let Some(p) = path.path.segments.first() {
285                        if p.ident == *self.parent.as_ref().unwrap() {
286                          args.push(quote! {
287                            napi::bindgen_prelude::Reference::from_value_ptr(this_ptr.cast(), env)?
288                          });
289                          skipped_arg_count += 1;
290                          continue;
291                        }
292                      }
293                    }
294                  }
295                } else if p.ident == "This" {
296                  if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
297                    args: angle_bracketed_args,
298                    ..
299                  }) = &p.arguments
300                  {
301                    if let Some(syn::GenericArgument::Type(generic_type)) =
302                      angle_bracketed_args.first()
303                    {
304                      if let syn::Type::Path(syn::TypePath {
305                        path: syn::Path { segments, .. },
306                        ..
307                      }) = generic_type
308                      {
309                        if let Some(syn::PathSegment { ident, .. }) = segments.first() {
310                          if let Some((primitive_type, _)) =
311                            crate::PRIMITIVE_TYPES.iter().find(|(p, _)| ident == *p)
312                          {
313                            bail_span!(
314                              ident,
315                              "This type must not be {} \nthis in JavaScript function must be `Object` type or `undefined`",
316                              primitive_type
317                            );
318                          }
319                          args.push(
320                            quote! {
321                              {
322                                <#ident as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.this)?.into()
323                              }
324                            },
325                          );
326                          skipped_arg_count += 1;
327                          continue;
328                        }
329                      } else if let syn::Type::Reference(syn::TypeReference {
330                        elem,
331                        mutability,
332                        ..
333                      }) = generic_type
334                      {
335                        if let syn::Type::Path(syn::TypePath {
336                          path: syn::Path { segments, .. },
337                          ..
338                        }) = elem.as_ref()
339                        {
340                          if let Some(syn::PathSegment { ident, .. }) = segments.first() {
341                            refs.push(make_ref(quote! { cb.this }));
342                            let token = if mutability.is_some() {
343                              mut_ref_spans.push(generic_type.span());
344                              quote! { <#ident as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.this)?.into() }
345                            } else {
346                              quote! { <#ident as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.this)?.into() }
347                            };
348                            args.push(token);
349                            skipped_arg_count += 1;
350                            continue;
351                          }
352                        }
353                      }
354                    }
355                  }
356                  refs.push(make_ref(quote! { cb.this }));
357                  args.push(quote! { <napi::bindgen_prelude::This as napi::NapiValue>::from_raw_unchecked(env, cb.this) });
358                  skipped_arg_count += 1;
359                  continue;
360                }
361              }
362            }
363            let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, path)?;
364            if NapiArgType::MutRef == arg_type {
365              mut_ref_spans.push(path.ty.span());
366            }
367            if arg_type.is_ref() {
368              refs.push(make_ref(quote! { cb.get_arg(#i) }));
369            }
370            if arg_type == NapiArgType::Env {
371              args.push(quote! { &__wrapped_env });
372              skipped_arg_count += 1;
373              continue;
374            }
375            arg_conversions.push(arg_conversion);
376            args.push(quote! { #ident });
377          }
378        }
379        NapiFnArgKind::Callback(cb) => {
380          arg_conversions.push(self.gen_cb_arg_conversion(&ident, i, cb)?);
381          args.push(quote! { #ident });
382        }
383      }
384    }
385
386    Ok(ArgConversions {
387      arg_conversions,
388      args,
389      refs,
390      mut_ref_spans,
391      unsafe_: self.unsafe_,
392    })
393  }
394
395  /// Returns a type conversion, and a boolean indicating whether this value needs to have a reference created to extend the lifetime
396  /// for async functions.
397  fn gen_ty_arg_conversion(
398    &self,
399    arg_name: &Ident,
400    index: usize,
401    path: &syn::PatType,
402  ) -> BindgenResult<(TokenStream, NapiArgType)> {
403    let mut ty = *path.ty.clone();
404    let type_check = if self.return_if_invalid {
405      quote! {
406        if let Ok(maybe_promise) = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index)) {
407          if !maybe_promise.is_null() {
408            return Ok(maybe_promise);
409          }
410        } else {
411          return Ok(std::ptr::null_mut());
412        }
413      }
414    } else if self.strict {
415      quote! {
416        let maybe_promise = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index))?;
417        if !maybe_promise.is_null() {
418          return Ok(maybe_promise);
419        }
420      }
421    } else {
422      quote! {}
423    };
424
425    match ty {
426      syn::Type::Reference(syn::TypeReference {
427        mutability: Some(_),
428        elem,
429        ..
430      }) => {
431        let q = quote! {
432          let #arg_name = {
433            #type_check
434            <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
435          };
436        };
437        Ok((q, NapiArgType::MutRef))
438      }
439      syn::Type::Reference(syn::TypeReference {
440        mutability, elem, ..
441      }) => {
442        if let syn::Type::Slice(slice) = &*elem {
443          if let syn::Type::Path(ele) = &*slice.elem {
444            if let Some(syn::PathSegment { ident, .. }) = ele.path.segments.first() {
445              if TYPEDARRAY_SLICE_TYPES.contains_key(&&*ident.to_string()) {
446                let q = quote! {
447                  let #arg_name = {
448                    #type_check
449                    <&mut #elem as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))?
450                  };
451                };
452                return Ok((q, NapiArgType::Ref));
453              }
454            }
455          }
456        }
457        let q = if mutability.is_some() {
458          quote! {
459            let #arg_name = {
460              #type_check
461              <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
462            }
463          }
464        } else {
465          if let syn::Type::Path(ele) = &*elem {
466            if let Some(syn::PathSegment { ident, .. }) = ele.path.segments.last() {
467              if ident == "Env" {
468                return Ok((quote! {}, NapiArgType::Env));
469              } else if ident == "str" {
470                bail_span!(
471                  elem,
472                  "JavaScript String is primitive and cannot be passed by reference"
473                );
474              }
475            }
476          }
477          quote! {
478            let #arg_name = {
479              #type_check
480              <#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))?
481            };
482          }
483        };
484        Ok((
485          q,
486          if mutability.is_some() {
487            NapiArgType::MutRef
488          } else {
489            NapiArgType::Ref
490          },
491        ))
492      }
493      _ => {
494        hidden_ty_lifetime(&mut ty)?;
495        let q = quote! {
496          let #arg_name = {
497            #type_check
498            <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))?
499          };
500        };
501        Ok((q, NapiArgType::Value))
502      }
503    }
504  }
505
506  fn gen_cb_arg_conversion(
507    &self,
508    arg_name: &Ident,
509    index: usize,
510    cb: &CallbackArg,
511  ) -> BindgenResult<TokenStream> {
512    let mut inputs = vec![];
513    let mut arg_conversions = vec![];
514
515    for (i, ty) in cb.args.iter().enumerate() {
516      let cb_arg_ident = Ident::new(&format!("callback_arg_{}", i), Span::call_site());
517      inputs.push(quote! { #cb_arg_ident: #ty });
518      let mut maybe_has_lifetime_ty = ty.clone();
519      hidden_ty_lifetime(&mut maybe_has_lifetime_ty)?;
520      arg_conversions.push(
521        quote! { <#maybe_has_lifetime_ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #cb_arg_ident)? },
522      );
523    }
524
525    let ret = match &cb.ret {
526      Some(ty) => {
527        quote! {
528          let ret = <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, ret_ptr)?;
529
530          Ok(ret)
531        }
532      }
533      None => quote! { Ok(()) },
534    };
535
536    Ok(quote! {
537      napi::bindgen_prelude::assert_type_of!(env, cb.get_arg(#index), napi::bindgen_prelude::ValueType::Function)?;
538      let #arg_name = |#(#inputs),*| {
539        let args = vec![
540          #(#arg_conversions),*
541        ];
542
543        let mut ret_ptr = std::ptr::null_mut();
544
545        napi::bindgen_prelude::check_pending_exception!(
546          env,
547          napi::bindgen_prelude::sys::napi_call_function(
548            env,
549            cb.this(),
550            cb.get_arg(#index),
551            args.len(),
552            args.as_ptr(),
553            &mut ret_ptr
554          )
555        )?;
556
557        #ret
558      };
559    })
560  }
561
562  fn gen_fn_receiver(&self) -> TokenStream {
563    let name = &self.name;
564
565    match self.fn_self {
566      Some(FnSelf::Value) => {
567        // impossible, panic! in parser
568        unreachable!();
569      }
570      Some(FnSelf::Ref) | Some(FnSelf::MutRef) => quote! { this.#name },
571      None => match &self.parent {
572        Some(class) => quote! { #class::#name },
573        None => quote! { #name },
574      },
575    }
576  }
577
578  fn gen_fn_return(&self, ret: &Ident) -> BindgenResult<TokenStream> {
579    let js_name = &self.js_name;
580
581    if let Some(ty) = &self.ret {
582      let ty_string = ty.into_token_stream().to_string();
583      let is_return_self = ty_string == "& Self" || ty_string == "&mut Self";
584      if self.kind == FnKind::Constructor {
585        let parent = self
586          .parent
587          .as_ref()
588          .expect("Parent must exist for constructor");
589        if self.is_ret_result {
590          if self.parent_is_generator {
591            Ok(quote! { cb.construct_generator::<false, _>(#js_name, #ret?) })
592          } else {
593            Ok(quote! {
594              match #ret {
595                Ok(value) => {
596                  cb.construct::<false, _>(#js_name, value)
597                }
598                Err(err) => {
599                  napi::bindgen_prelude::JsError::from(err).throw_into(env);
600                  Ok(std::ptr::null_mut())
601                }
602              }
603            })
604          }
605        } else if self.parent_is_generator {
606          Ok(quote! { cb.construct_generator::<false, #parent>(#js_name, #ret) })
607        } else {
608          Ok(quote! { cb.construct::<false, #parent>(#js_name, #ret) })
609        }
610      } else if self.kind == FnKind::Factory {
611        if self.is_ret_result {
612          if self.parent_is_generator {
613            Ok(quote! { cb.generator_factory(#js_name, #ret?) })
614          } else if self.is_async {
615            Ok(quote! { cb.factory(#js_name, #ret) })
616          } else {
617            Ok(quote! {
618              match #ret {
619                Ok(value) => {
620                  cb.factory(#js_name, value)
621                }
622                Err(err) => {
623                  napi::bindgen_prelude::JsError::from(err).throw_into(env);
624                  Ok(std::ptr::null_mut())
625                }
626              }
627            })
628          }
629        } else if self.parent_is_generator {
630          Ok(quote! { cb.generator_factory(#js_name, #ret) })
631        } else {
632          Ok(quote! { cb.factory(#js_name, #ret) })
633        }
634      } else if self.is_ret_result {
635        if self.is_async {
636          Ok(quote! {
637            <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret)
638          })
639        } else if is_return_self {
640          Ok(quote! { #ret.map(|_| cb.this) })
641        } else {
642          Ok(quote! {
643            match #ret {
644              Ok(value) => napi::bindgen_prelude::ToNapiValue::to_napi_value(env, value),
645              Err(err) => {
646                napi::bindgen_prelude::JsError::from(err).throw_into(env);
647                Ok(std::ptr::null_mut())
648              },
649            }
650          })
651        }
652      } else if is_return_self {
653        Ok(quote! { Ok(cb.this) })
654      } else {
655        let mut return_ty = ty.clone();
656        hidden_ty_lifetime(&mut return_ty)?;
657        Ok(quote! {
658          <#return_ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret)
659        })
660      }
661    } else {
662      Ok(quote! {
663        <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ())
664      })
665    }
666  }
667
668  fn gen_fn_register(&self) -> TokenStream {
669    if self.parent.is_some() {
670      quote! {}
671    } else {
672      let name_str = self.name.to_string();
673      let js_name = format!("{}\0", &self.js_name);
674      let name_len = self.js_name.len();
675      let module_register_name = &self.register_name;
676      let intermediate_ident = get_intermediate_ident(&name_str);
677      let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
678      let cb_name = Ident::new(&format!("{}_js_function", name_str), Span::call_site());
679
680      quote! {
681        #[allow(non_snake_case)]
682        #[allow(clippy::all)]
683        unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
684          let mut fn_ptr = std::ptr::null_mut();
685
686          napi::bindgen_prelude::check_status!(
687            napi::bindgen_prelude::sys::napi_create_function(
688              env,
689              #js_name.as_ptr().cast(),
690              #name_len as isize,
691              Some(#intermediate_ident),
692              std::ptr::null_mut(),
693              &mut fn_ptr,
694            ),
695            "Failed to register function `{}`",
696            #name_str,
697          )?;
698          napi::bindgen_prelude::register_js_function(#js_name, #cb_name, Some(#intermediate_ident));
699          Ok(fn_ptr)
700        }
701
702        #[allow(clippy::all)]
703        #[allow(non_snake_case)]
704        #[cfg(all(not(test), not(target_family = "wasm")))]
705        #[napi::ctor::ctor(crate_path=::napi::ctor)]
706        fn #module_register_name() {
707          napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
708        }
709
710        #[allow(clippy::all)]
711        #[allow(non_snake_case)]
712        #[cfg(all(not(test), target_family = "wasm"))]
713        #[no_mangle]
714        extern "C" fn #module_register_name() {
715          napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
716        }
717      }
718    }
719  }
720}
721
722fn hidden_ty_lifetime(ty: &mut syn::Type) -> BindgenResult<()> {
723  if let Type::Path(TypePath {
724    path: syn::Path { segments, .. },
725    ..
726  }) = ty
727  {
728    if let Some(syn::PathSegment {
729      arguments:
730        syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
731      ..
732    }) = segments.last_mut()
733    {
734      if let Some(syn::GenericArgument::Lifetime(lt)) = args.first_mut() {
735        *lt = syn::Lifetime::new("'_", Span::call_site());
736      }
737      for arg in args.iter_mut().skip(1) {
738        if let syn::GenericArgument::Type(ty) = arg {
739          hidden_ty_lifetime(ty)?;
740        }
741      }
742    }
743  }
744  Ok(())
745}
746
747struct ArgConversions {
748  pub args: Vec<TokenStream>,
749  pub arg_conversions: Vec<TokenStream>,
750  pub refs: Vec<TokenStream>,
751  pub mut_ref_spans: Vec<Span>,
752  pub unsafe_: bool,
753}
754
755#[derive(Debug, PartialEq, Eq)]
756enum NapiArgType {
757  Ref,
758  MutRef,
759  Value,
760  Env,
761}
762
763impl NapiArgType {
764  fn is_ref(&self) -> bool {
765    matches!(self, NapiArgType::Ref | NapiArgType::MutRef)
766  }
767}