1use proc_macro2::{Ident, Span, TokenStream};
2use quote::ToTokens;
3use syn::{spanned::Spanned, Type, TypePath, TypeReference};
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
11#[cfg(feature = "tracing")]
12fn gen_tracing_debug(js_name: &str, parent_js_name: Option<&String>) -> TokenStream {
13 let full_name = if let Some(parent) = parent_js_name {
14 format!("{}::{}", parent, js_name)
15 } else {
16 js_name.to_string()
17 };
18 quote! {
19 napi::bindgen_prelude::trace_napi_call(#full_name);
20 }
21}
22
23#[cfg(not(feature = "tracing"))]
24fn gen_tracing_debug(_js_name: &str, _parent_js_name: Option<&String>) -> TokenStream {
25 quote! {}
26}
27
28impl TryToTokens for NapiFn {
29 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
30 let name_str = self.name.to_string();
31 let intermediate_ident = get_intermediate_ident(&name_str);
32 let args_len = self.args.len();
33
34 let ArgConversions {
35 arg_conversions,
36 this_conversions,
37 receiver_unwrap,
38 receiver_dependent_conversions,
39 receiver_conversion,
40 args: arg_names,
41 refs,
42 mut_ref_spans,
43 unsafe_,
44 } = self.gen_arg_conversions()?;
45 let attrs = &self.attrs;
46 let arg_ref_count = refs.len();
47 let receiver = self.gen_fn_receiver();
48 let receiver_ret_name = Ident::new("_ret", Span::call_site());
49 let ret = self.gen_fn_return(&receiver_ret_name)?;
50 let register = self.gen_fn_register();
51 let tracing_debug = gen_tracing_debug(&self.js_name, self.parent_js_name.as_ref());
52
53 if self.module_exports {
54 (quote! {
55 #(#attrs)*
56 #[doc(hidden)]
57 #[allow(non_snake_case)]
58 #[allow(clippy::all)]
59 unsafe extern "C" fn #intermediate_ident(
60 env: napi::bindgen_prelude::sys::napi_env,
61 _napi_module_exports_: napi::bindgen_prelude::sys::napi_value,
62 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
63 #tracing_debug
64 let __wrapped_env = napi::bindgen_prelude::Env::from(env);
65 #(#arg_conversions)*
66 #(#this_conversions)*
67 #receiver_unwrap
68 #(#receiver_dependent_conversions)*
69 #receiver_conversion
70 let #receiver_ret_name = {
71 #receiver(#(#arg_names),*)
72 };
73 #ret
74 }
75
76 #register
77 })
78 .to_tokens(tokens);
79
80 return Ok(());
81 }
82
83 if self.is_async && !mut_ref_spans.is_empty() && !unsafe_ {
86 return Diagnostic::from_vec(
87 mut_ref_spans
88 .into_iter()
89 .map(|s| Diagnostic::span_error(s, "mutable reference is unsafe with async"))
90 .collect(),
91 );
92 }
93 if Some(FnSelf::MutRef) == self.fn_self && self.is_async && !self.unsafe_ {
94 return Err(Diagnostic::span_error(
95 self.name.span(),
96 "&mut self in async napi methods should be marked as unsafe",
97 ));
98 }
99
100 if self.parent.is_some()
101 && matches!(self.kind, FnKind::Getter | FnKind::Setter)
102 && !self.is_async
103 {
104 let accessor_value_param = if self.kind == FnKind::Setter {
105 quote! { , value: napi::bindgen_prelude::sys::napi_value }
106 } else {
107 quote! {}
108 };
109 let accessor_args = if self.kind == FnKind::Setter && args_len > 0 {
110 quote! {
111 let mut __napi_accessor_args = [std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>(); #args_len];
112 __napi_accessor_args[0] = value;
113 }
114 } else {
115 quote! {
116 let __napi_accessor_args = [std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>(); #args_len];
117 }
118 };
119
120 let native_call = if self.within_async_runtime {
121 quote! {
122 napi::bindgen_prelude::within_runtime_if_available(move || {
123 let #receiver_ret_name = {
124 #receiver(#(#arg_names),*)
125 };
126 #ret
127 })
128 }
129 } else {
130 quote! {
131 let #receiver_ret_name = {
132 #receiver(#(#arg_names),*)
133 };
134 #ret
135 }
136 };
137
138 let function_call_inner = quote! {
139 #accessor_args
140 let mut cb = napi::bindgen_prelude::ClassAccessorCallbackInfo::<#args_len>::new(
141 env,
142 this,
143 __napi_accessor_args,
144 );
145 let mut _napi_native_borrow_scope = napi::bindgen_prelude::NativeBorrowScope::new();
151 let __wrapped_env = napi::bindgen_prelude::Env::from(env);
152 #(#arg_conversions)*
153 #(#this_conversions)*
154 #receiver_unwrap
155 #(#receiver_dependent_conversions)*
156 #receiver_conversion
157 _napi_native_borrow_scope.finish();
158 let _napi_native_borrow_barrier =
159 napi::bindgen_prelude::NativeBorrowBarrier::new();
160 #native_call
161 };
162
163 let function_call = if self.catch_unwind {
164 quote! {
165 {
166 std::panic::catch_unwind(|| { #function_call_inner })
167 .map_err(napi::bindgen_prelude::panic_to_error)
168 .and_then(|r| r)
169 }
170 }
171 } else {
172 quote! {
173 #function_call_inner
174 }
175 };
176
177 (quote! {
178 #(#attrs)*
179 #[doc(hidden)]
180 #[allow(non_snake_case)]
181 #[allow(clippy::all)]
182 unsafe fn #intermediate_ident(
183 env: napi::bindgen_prelude::sys::napi_env,
184 this: napi::bindgen_prelude::sys::napi_value
185 #accessor_value_param
186 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
187 #tracing_debug
188 unsafe {
189 #function_call
190 }
191 }
192
193 #register
194 })
195 .to_tokens(tokens);
196
197 return Ok(());
198 }
199
200 let build_ref_container = if self.is_async {
201 quote! {
202 struct NapiRefContainer([napi::sys::napi_ref; #arg_ref_count]);
203 impl NapiRefContainer {
204 fn drop(self, env: napi::sys::napi_env) {
205 for r in self.0.into_iter() {
206 assert_eq!(
207 unsafe { napi::sys::napi_reference_unref(env, r, &mut 0) },
208 napi::sys::Status::napi_ok,
209 "failed to delete napi ref"
210 );
211 assert_eq!(
212 unsafe { napi::sys::napi_delete_reference(env, r) },
213 napi::sys::Status::napi_ok,
214 "failed to delete napi ref"
215 );
216 }
217 }
218 }
219 unsafe impl Send for NapiRefContainer {}
220 unsafe impl Sync for NapiRefContainer {}
221 let _make_ref = |a: ::std::ptr::NonNull<napi::bindgen_prelude::sys::napi_value__>| {
222 let mut node_ref = ::std::mem::MaybeUninit::uninit();
223 napi::bindgen_prelude::check_status!(unsafe {
224 napi::bindgen_prelude::sys::napi_create_reference(env, a.as_ptr(), 1, node_ref.as_mut_ptr())
225 },
226 "failed to create napi ref"
227 )?;
228 Ok::<napi::sys::napi_ref, napi::Error>(unsafe { node_ref.assume_init() })
229 };
230 let mut _args_array = [::std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_ref__>(); #arg_ref_count];
231 let mut _arg_write_index = 0;
232
233 #(#refs)*
234
235 #[cfg(debug_assertions)]
236 {
237 for a in &_args_array {
238 assert!(!a.is_null(), "failed to initialize napi ref");
239 }
240 }
241 let _args_ref = NapiRefContainer(_args_array);
242 }
243 } else {
244 quote! {}
245 };
246 let create_native_borrow_scope = if self.is_async {
251 quote! {
252 napi::bindgen_prelude::NativeBorrowScope::new_async()
253 }
254 } else {
255 quote! {
256 napi::bindgen_prelude::NativeBorrowScope::new()
257 }
258 };
259 let native_call = if !self.is_async {
260 if self.within_async_runtime {
261 quote! {
262 napi::bindgen_prelude::within_runtime_if_available(move || {
263 let #receiver_ret_name = {
264 #receiver(#(#arg_names),*)
265 };
266 #ret
267 })
268 }
269 } else {
270 quote! {
271 let #receiver_ret_name = {
272 #receiver(#(#arg_names),*)
273 };
274 #ret
275 }
276 }
277 } else {
278 let call = if self.is_ret_result {
279 quote! { #receiver(#(#arg_names),*).await }
280 } else {
281 let ret_type = if let Some(t) = &self.ret {
282 quote! { #t }
283 } else {
284 quote! { () }
285 };
286 quote! { Ok::<#ret_type, napi::Error>(#receiver(#(#arg_names),*).await) }
287 };
288 quote! {
289 napi::bindgen_prelude::execute_tokio_future_with_finalize_callback(env, async move { #call }, move |env, #receiver_ret_name| {
290 #ret
291 }, Some(Box::new(move |env| {
292 _napi_native_borrow_scope.release(env);
293 _args_ref.drop(env);
294 })))
295 }
296 };
297
298 let use_after_async = if self.is_async && self.parent.is_some() && self.fn_self.is_none() {
300 quote! { true }
301 } else {
302 quote! { false }
303 };
304
305 let function_call_inner = quote! {
306 napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None, #use_after_async).and_then(|#[allow(unused_mut)] mut cb| {
307 let __wrapped_env = napi::bindgen_prelude::Env::from(env);
308 let mut _napi_native_borrow_scope = #create_native_borrow_scope;
315 #(#arg_conversions)*
316 #(#this_conversions)*
317 #receiver_unwrap
318 #(#receiver_dependent_conversions)*
319 #build_ref_container
320 #receiver_conversion
321 _napi_native_borrow_scope.finish();
322 let _napi_native_borrow_barrier =
323 napi::bindgen_prelude::NativeBorrowBarrier::new();
324 #native_call
325 })
326 };
327
328 let function_call = if args_len == 0
329 && self.fn_self.is_none()
330 && self.kind != FnKind::Constructor
331 && self.kind != FnKind::Factory
332 && !self.is_async
333 {
334 quote! {
335 let _napi_native_borrow_barrier =
336 napi::bindgen_prelude::NativeBorrowBarrier::new();
337 #native_call
338 }
339 } else if self.kind == FnKind::Constructor {
340 let return_from_factory = if self.catch_unwind {
341 quote! { return Ok(std::ptr::null_mut()); }
342 } else {
343 quote! { return std::ptr::null_mut(); }
344 };
345 quote! {
346 if napi::__private::___CALL_FROM_FACTORY.with(|inner| inner.get()) {
349 #return_from_factory
350 }
351 #function_call_inner
352 }
353 } else {
354 function_call_inner
355 };
356
357 let function_call = if self.catch_unwind {
358 quote! {
359 {
360 std::panic::catch_unwind(|| { #function_call })
361 .map_err(napi::bindgen_prelude::panic_to_error)
362 .and_then(|r| r)
363 }
364 }
365 } else {
366 quote! {
367 #function_call
368 }
369 };
370
371 (quote! {
372 #(#attrs)*
373 #[doc(hidden)]
374 #[allow(non_snake_case)]
375 #[allow(clippy::all)]
376 extern "C" fn #intermediate_ident(
377 env: napi::bindgen_prelude::sys::napi_env,
378 cb: napi::bindgen_prelude::sys::napi_callback_info
379 ) -> napi::bindgen_prelude::sys::napi_value {
380 #tracing_debug
381 unsafe {
382 #function_call.unwrap_or_else(|e| {
383 napi::bindgen_prelude::JsError::from(e).throw_into(env);
384 std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
385 })
386 }
387 }
388
389 #register
390 })
391 .to_tokens(tokens);
392
393 Ok(())
394 }
395}
396
397impl NapiFn {
398 fn gen_arg_conversions(&self) -> BindgenResult<ArgConversions> {
399 let mut arg_conversions = vec![];
400 let mut this_conversions = vec![];
401 let mut receiver_unwrap = quote! {};
402 let mut receiver_dependent_conversions = vec![];
403 let mut args = vec![];
404 let mut refs = vec![];
405 let mut mut_ref_spans = vec![];
406 let mut receiver_conversion = quote! {};
407 if let Some(parent) = &self.parent {
409 match self.fn_self {
410 Some(FnSelf::Ref) => {
411 refs.push(make_ref(quote! { cb.this() }));
412 receiver_unwrap = quote! {
413 let this_ptr = cb.unwrap_raw::<#parent>()?;
414 napi::bindgen_prelude::register_native_borrow_with_value(
415 env,
416 cb.this(),
417 this_ptr,
418 false,
419 )?;
420 };
421 receiver_conversion = quote! {
422 let this: &#parent = Box::leak(Box::from_raw(this_ptr));
423 };
424 }
425 Some(FnSelf::MutRef) => {
426 refs.push(make_ref(quote! { cb.this() }));
427 receiver_unwrap = quote! {
428 let this_ptr = cb.unwrap_raw::<#parent>()?;
429 napi::bindgen_prelude::register_native_borrow_with_value(
430 env,
431 cb.this(),
432 this_ptr,
433 true,
434 )?;
435 };
436 receiver_conversion = quote! {
437 let this: &mut #parent = Box::leak(Box::from_raw(this_ptr));
438 };
439 }
440 _ => {}
441 };
442 }
443
444 let mut skipped_arg_count = 0;
445 for (rust_arg_index, arg) in self.args.iter().enumerate() {
446 let i = rust_arg_index - skipped_arg_count;
447 let ident = Ident::new(&format!("arg{i}"), Span::call_site());
448 let injected_ident = Ident::new(&format!("__napi_arg_{rust_arg_index}"), Span::call_site());
449
450 match &arg.kind {
451 NapiFnArgKind::PatType(pat_type) => {
452 let is_env_type = if let syn::Type::Path(syn::TypePath {
453 qself: None,
454 path: syn::Path { segments, .. },
455 ..
456 }) = pat_type.ty.as_ref()
457 {
458 segments.last().is_some_and(|s| s.ident == "Env")
459 } else {
460 false
461 };
462 if is_env_type {
463 args.push(quote! { __wrapped_env });
464 skipped_arg_count += 1;
465 } else {
466 let is_in_class = self.parent.is_some();
467 if let syn::Type::Path(path) = pat_type.ty.as_ref() {
469 if let Some(p) = path.path.segments.last() {
471 if p.ident == "Reference" {
472 if !is_in_class {
473 bail_span!(p, "`Reference` is only allowed in class methods");
474 }
475 if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
477 args: angle_bracketed_args,
478 ..
479 }) = &p.arguments
480 {
481 if let Some(syn::GenericArgument::Type(syn::Type::Path(path))) =
482 angle_bracketed_args.first()
483 {
484 if let Some(p) = path.path.segments.first() {
485 if p.ident == *self.parent.as_ref().unwrap() {
486 receiver_dependent_conversions.push(quote! {
487 let #injected_ident =
488 napi::bindgen_prelude::Reference::<#path>::from_value_ptr(this_ptr.cast(), env)?;
489 });
490 args.push(quote! { #injected_ident });
491 skipped_arg_count += 1;
492 continue;
493 }
494 }
495 }
496 }
497 } else if p.ident == "This" {
498 if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
500 args: angle_bracketed_args,
501 ..
502 }) = &p.arguments
503 {
504 if let Some(syn::GenericArgument::Type(generic_type)) =
505 angle_bracketed_args.first()
506 {
507 if let syn::Type::Path(syn::TypePath {
508 path: syn::Path { segments, .. },
509 ..
510 }) = generic_type
511 {
512 if let Some(syn::PathSegment { ident, .. }) = segments.first() {
513 if let Some((primitive_type, _)) =
514 crate::PRIMITIVE_TYPES.iter().find(|(p, _)| ident == *p)
515 {
516 bail_span!(
517 ident,
518 "This type must not be {} \nthis in JavaScript function must be `Object` type or `undefined`",
519 primitive_type
520 );
521 }
522 this_conversions.push(quote! {
523 let #injected_ident =
524 <#ident as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.this())?;
525 });
526 args.push(quote! { #injected_ident.into() });
527 skipped_arg_count += 1;
528 continue;
529 }
530 } else if let syn::Type::Reference(syn::TypeReference {
531 elem,
532 mutability,
533 ..
534 }) = generic_type
535 {
536 if let syn::Type::Path(syn::TypePath {
537 path: syn::Path { segments, .. },
538 ..
539 }) = elem.as_ref()
540 {
541 if let Some(syn::PathSegment { ident, .. }) = segments.first() {
542 refs.push(make_ref(quote! { cb.this() }));
543 let token = if mutability.is_some() {
544 mut_ref_spans.push(generic_type.span());
545 quote! { <#ident as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.this())? }
546 } else {
547 quote! { <#ident as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.this())? }
548 };
549 this_conversions.push(quote! {
550 let #injected_ident = #token;
551 });
552 args.push(quote! { #injected_ident.into() });
553 skipped_arg_count += 1;
554 continue;
555 }
556 }
557 }
558 }
559 }
560 refs.push(make_ref(quote! { cb.this() }));
561 this_conversions.push(quote! {
562 let #injected_ident =
563 <napi::bindgen_prelude::This as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.this())?;
564 });
565 args.push(quote! { #injected_ident });
566 skipped_arg_count += 1;
567 continue;
568 }
569 }
570 }
571 let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, pat_type)?;
572 if NapiArgType::MutRef == arg_type {
573 mut_ref_spans.push(pat_type.ty.span());
574 }
575 if arg_type.is_ref() {
576 refs.push(make_ref(quote! { cb.get_arg(#i) }));
577 }
578 if arg_type == NapiArgType::Env {
579 args.push(quote! { &__wrapped_env });
580 skipped_arg_count += 1;
581 continue;
582 }
583 arg_conversions.push(arg_conversion);
584 args.push(quote! { #ident });
585 }
586 }
587 NapiFnArgKind::Callback(cb) => {
588 arg_conversions.push(self.gen_cb_arg_conversion(&ident, i, cb)?);
589 args.push(quote! { #ident });
590 }
591 }
592 }
593
594 Ok(ArgConversions {
595 arg_conversions,
596 this_conversions,
597 receiver_unwrap,
598 receiver_dependent_conversions,
599 receiver_conversion,
600 args,
601 refs,
602 mut_ref_spans,
603 unsafe_: self.unsafe_,
604 })
605 }
606
607 fn gen_ty_arg_conversion(
610 &self,
611 arg_name: &Ident,
612 index: usize,
613 path: &syn::PatType,
614 ) -> BindgenResult<(TokenStream, NapiArgType)> {
615 let mut ty = *path.ty.clone();
616 let type_check = if self.return_if_invalid {
617 quote! {
618 if let Ok(maybe_promise) = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index)) {
619 if !maybe_promise.is_null() {
620 return Ok(maybe_promise);
621 }
622 } else {
623 return Ok(std::ptr::null_mut());
624 }
625 }
626 } else if self.strict {
627 quote! {
628 let maybe_promise = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index))?;
629 if !maybe_promise.is_null() {
630 return Ok(maybe_promise);
631 }
632 }
633 } else {
634 quote! {}
635 };
636
637 let arg_conversion = if self.module_exports {
638 quote! { _napi_module_exports_ }
639 } else {
640 quote! { cb.get_arg(#index) }
641 };
642
643 match ty {
644 syn::Type::Reference(syn::TypeReference {
645 mutability: Some(_),
646 elem,
647 ..
648 }) => {
649 let q = quote! {
650 let #arg_name = {
651 #type_check
652 <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
653 };
654 };
655 Ok((q, NapiArgType::MutRef))
656 }
657 syn::Type::Reference(syn::TypeReference {
658 mutability, elem, ..
659 }) => {
660 if let syn::Type::Slice(slice) = &*elem {
661 if let syn::Type::Path(ele) = &*slice.elem {
662 if let Some(syn::PathSegment { ident, .. }) = ele.path.segments.first() {
663 if TYPEDARRAY_SLICE_TYPES.contains_key(&&*ident.to_string()) {
664 let q = quote! {
665 let #arg_name = {
666 #type_check
667 <&mut #elem as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))?
668 };
669 };
670 return Ok((q, NapiArgType::Ref));
671 }
672 }
673 }
674 }
675 let q = if mutability.is_some() {
676 quote! {
677 let #arg_name = {
678 #type_check
679 <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
680 }
681 }
682 } else {
683 if let syn::Type::Path(syn::TypePath {
686 qself: None, path, ..
687 }) = &*elem
688 {
689 if let Some(syn::PathSegment { ident, .. }) = path.segments.last() {
690 if ident == "Env" {
691 return Ok((quote! {}, NapiArgType::Env));
692 } else if ident == "str" {
693 bail_span!(
694 elem,
695 "JavaScript String is primitive and cannot be passed by reference"
696 );
697 }
698 }
699 }
700 quote! {
701 let #arg_name = {
702 #type_check
703 <#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))?
704 };
705 }
706 };
707 Ok((
708 q,
709 if mutability.is_some() {
710 NapiArgType::MutRef
711 } else {
712 NapiArgType::Ref
713 },
714 ))
715 }
716 _ => {
717 hidden_ty_lifetime(&mut ty)?;
718 let mut arg_type = NapiArgType::Value;
719 let mut is_array = false;
720 if let syn::Type::Path(path) = &ty {
721 if let Some(syn::PathSegment { ident, arguments }) = path.path.segments.first() {
724 if ident == "Vec" {
726 is_array = true;
727 if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
728 args: angle_bracketed_args,
729 ..
730 }) = &arguments
731 {
732 if let Some(syn::GenericArgument::Type(syn::Type::Reference(
734 syn::TypeReference { .. },
735 ))) = angle_bracketed_args.first()
736 {
737 arg_type = NapiArgType::Ref;
739 }
740 }
741 }
742 }
743 }
744 let from_napi_value = if is_array && self.return_if_invalid {
747 quote! {
748 match <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, #arg_conversion) {
749 Ok(value) => value,
750 Err(err) => {
751 if err.status < napi::bindgen_prelude::Status::GenericFailure {
753 return Ok(std::ptr::null_mut());
754 } else {
755 return Err(err);
756 }
757 }
758 }
759 }
760 } else {
761 quote! {
762 <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, #arg_conversion)?
763 }
764 };
765 let q = quote! {
766 let #arg_name = {
767 #type_check
768 #from_napi_value
769 };
770 };
771 Ok((q, arg_type))
772 }
773 }
774 }
775
776 fn gen_cb_arg_conversion(
777 &self,
778 arg_name: &Ident,
779 index: usize,
780 cb: &CallbackArg,
781 ) -> BindgenResult<TokenStream> {
782 let mut inputs = vec![];
783 let mut arg_conversions = vec![];
784
785 for (i, ty) in cb.args.iter().enumerate() {
786 let cb_arg_ident = Ident::new(&format!("callback_arg_{i}"), Span::call_site());
787 inputs.push(quote! { #cb_arg_ident: #ty });
788 let mut maybe_has_lifetime_ty = ty.clone();
789 hidden_ty_lifetime(&mut maybe_has_lifetime_ty)?;
790 arg_conversions.push(
791 quote! { <#maybe_has_lifetime_ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #cb_arg_ident)? },
792 );
793 }
794
795 let ret = match &cb.ret {
796 Some(ty) => {
797 quote! {
798 let ret = <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, ret_ptr)?;
799
800 Ok(ret)
801 }
802 }
803 None => quote! { Ok(()) },
804 };
805
806 Ok(quote! {
807 napi::bindgen_prelude::assert_type_of!(env, cb.get_arg(#index), napi::bindgen_prelude::ValueType::Function)?;
808 let #arg_name = |#(#inputs),*| {
809 let args = vec![
810 #(#arg_conversions),*
811 ];
812
813 let mut ret_ptr = std::ptr::null_mut();
814
815 napi::bindgen_prelude::check_pending_exception!(
816 env,
817 napi::bindgen_prelude::sys::napi_call_function(
818 env,
819 cb.this(),
820 cb.get_arg(#index),
821 args.len(),
822 args.as_ptr(),
823 &mut ret_ptr
824 )
825 )?;
826
827 #ret
828 };
829 })
830 }
831
832 fn gen_fn_receiver(&self) -> TokenStream {
833 let name = &self.name;
834
835 match self.fn_self {
836 Some(FnSelf::Value) => {
837 unreachable!();
839 }
840 Some(FnSelf::Ref) | Some(FnSelf::MutRef) => quote! { this.#name },
841 None => match &self.parent {
842 Some(class) => quote! { #class::#name },
843 None => quote! { #name },
844 },
845 }
846 }
847
848 fn gen_fn_return(&self, ret: &Ident) -> BindgenResult<TokenStream> {
849 let js_name = &self.js_name;
850
851 if let Some(ty) = &self.ret {
852 let ty_string = ty.into_token_stream().to_string();
853 let is_return_self = ty_string == "& Self" || ty_string == "&mut Self";
854 if self.kind == FnKind::Constructor {
855 let parent = self
856 .parent
857 .as_ref()
858 .expect("Parent must exist for constructor");
859 if self.is_ret_result {
860 if self.parent_is_generator {
861 Ok(quote! { cb.construct_generator::<false, _>(#js_name, #ret?) })
862 } else if self.parent_is_async_generator {
863 Ok(quote! { cb.construct_async_generator::<false, _>(#js_name, #ret?) })
864 } else {
865 Ok(quote! {
866 match #ret {
867 Ok(value) => {
868 cb.construct::<false, _>(#js_name, value)
869 }
870 Err(err) => {
871 napi::bindgen_prelude::JsError::from(err).throw_into(env);
872 Ok(std::ptr::null_mut())
873 }
874 }
875 })
876 }
877 } else if self.parent_is_generator {
878 Ok(quote! { cb.construct_generator::<false, #parent>(#js_name, #ret) })
879 } else if self.parent_is_async_generator {
880 Ok(quote! { cb.construct_async_generator::<false, #parent>(#js_name, #ret) })
881 } else {
882 Ok(quote! { cb.construct::<false, #parent>(#js_name, #ret) })
883 }
884 } else if self.kind == FnKind::Factory {
885 if self.is_ret_result {
886 if self.parent_is_generator {
887 Ok(quote! { cb.generator_factory(#js_name, #ret?) })
888 } else if self.parent_is_async_generator {
889 Ok(quote! { cb.async_generator_factory(#js_name, #ret?) })
890 } else if self.is_async {
891 Ok(quote! { cb.factory(#js_name, #ret) })
892 } else {
893 Ok(quote! {
894 match #ret {
895 Ok(value) => {
896 cb.factory(#js_name, value)
897 }
898 Err(err) => {
899 napi::bindgen_prelude::JsError::from(err).throw_into(env);
900 Ok(std::ptr::null_mut())
901 }
902 }
903 })
904 }
905 } else if self.parent_is_generator {
906 Ok(quote! { cb.generator_factory(#js_name, #ret) })
907 } else if self.parent_is_async_generator {
908 Ok(quote! { cb.async_generator_factory(#js_name, #ret) })
909 } else {
910 Ok(quote! { cb.factory(#js_name, #ret) })
911 }
912 } else if self.is_ret_result {
913 if self.is_async {
914 Ok(quote! {
915 <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret)
916 })
917 } else if is_return_self {
918 Ok(quote! { #ret.map(|_| cb.this()) })
919 } else {
920 Ok(quote! {
921 match #ret {
922 Ok(value) => napi::bindgen_prelude::ToNapiValue::to_napi_value(env, value),
923 Err(err) => {
924 napi::bindgen_prelude::JsError::from(err).throw_into(env);
925 Ok(std::ptr::null_mut())
926 },
927 }
928 })
929 }
930 } else if is_return_self {
931 Ok(quote! { Ok(cb.this()) })
932 } else {
933 let mut return_ty = ty.clone();
934 hidden_ty_lifetime(&mut return_ty)?;
935 Ok(quote! {
936 <#return_ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret)
937 })
938 }
939 } else {
940 Ok(quote! {
941 <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ())
942 })
943 }
944 }
945
946 fn gen_fn_register(&self) -> TokenStream {
947 if self.parent.is_some() || cfg!(test) {
948 quote! {}
949 } else {
950 let name_str = self.name.to_string();
951 let js_name = format!("{}\0", self.js_name);
952 let name_len = self.js_name.len();
953 let module_register_name = &self.register_name;
954 let intermediate_ident = get_intermediate_ident(&name_str);
955 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
956 let cb_name = Ident::new(
957 &format!("_napi_rs_internal_register_{name_str}"),
958 Span::call_site(),
959 );
960
961 if self.module_exports {
962 return quote! {
963 #[doc(hidden)]
964 #[allow(non_snake_case)]
965 #[allow(clippy::all)]
966 unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env, exports: napi::bindgen_prelude::sys::napi_value) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
967 #intermediate_ident(env, exports)?;
968 Ok(exports)
969 }
970
971 #[cfg(all(not(test), not(target_family = "wasm")))]
972 napi::ctor::declarative::ctor! {
973 #[doc(hidden)]
974 #[allow(clippy::all)]
975 #[allow(non_snake_case)]
976 #[ctor(unsafe)]
977 fn #module_register_name() {
978 napi::bindgen_prelude::register_module_export_hook(#cb_name);
979 }
980 }
981
982 #[allow(clippy::all)]
983 #[allow(non_snake_case)]
984 #[cfg(all(not(test), target_family = "wasm"))]
985 #[no_mangle]
986 extern "C" fn #module_register_name() {
987 napi::bindgen_prelude::register_module_export_hook(#cb_name);
988 }
989 };
990 }
991
992 let register_module_export_tokens = if self.no_export {
993 quote! {}
994 } else {
995 quote! {
996 #[cfg(all(not(test), not(target_family = "wasm")))]
997 napi::ctor::declarative::ctor! {
998 #[doc(hidden)]
999 #[allow(clippy::all)]
1000 #[allow(non_snake_case)]
1001 #[ctor(unsafe)]
1002 fn #module_register_name() {
1003 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
1004 }
1005 }
1006
1007 #[doc(hidden)]
1008 #[allow(clippy::all)]
1009 #[allow(non_snake_case)]
1010 #[cfg(all(not(test), target_family = "wasm"))]
1011 #[no_mangle]
1012 extern "C" fn #module_register_name() {
1013 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
1014 }
1015 }
1016 };
1017
1018 quote! {
1019 #[doc(hidden)]
1020 #[allow(non_snake_case)]
1021 #[allow(clippy::all)]
1022 unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1023 let mut fn_ptr = std::ptr::null_mut();
1024
1025 napi::bindgen_prelude::check_status!(
1026 napi::bindgen_prelude::sys::napi_create_function(
1027 env,
1028 #js_name.as_ptr().cast(),
1029 #name_len as isize,
1030 Some(#intermediate_ident),
1031 std::ptr::null_mut(),
1032 &mut fn_ptr,
1033 ),
1034 "Failed to register function `{}`",
1035 #name_str,
1036 )?;
1037 Ok(fn_ptr)
1038 }
1039
1040 #register_module_export_tokens
1041 }
1042 }
1043 }
1044}
1045
1046fn hidden_ty_lifetime(ty: &mut syn::Type) -> BindgenResult<()> {
1047 match ty {
1048 Type::Path(TypePath {
1049 path: syn::Path { segments, .. },
1050 ..
1051 }) => {
1052 if let Some(syn::PathSegment {
1053 arguments:
1054 syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
1055 ..
1056 }) = segments.last_mut()
1057 {
1058 let mut has_lifetime = false;
1059 if let Some(syn::GenericArgument::Lifetime(lt)) = args.first_mut() {
1060 *lt = syn::Lifetime::new("'_", Span::call_site());
1061 has_lifetime = true;
1062 }
1063 for arg in args.iter_mut().skip(if has_lifetime { 1 } else { 0 }) {
1064 if let syn::GenericArgument::Type(ty) = arg {
1065 hidden_ty_lifetime(ty)?;
1066 }
1067 }
1068 }
1069 }
1070 Type::Reference(TypeReference {
1071 lifetime: Some(lt), ..
1072 }) => {
1073 *lt = syn::Lifetime::new("'_", Span::call_site());
1074 }
1075 _ => {}
1076 }
1077 Ok(())
1078}
1079
1080fn make_ref(input: TokenStream) -> TokenStream {
1081 quote! {
1082 _args_array[_arg_write_index] = _make_ref(
1083 ::std::ptr::NonNull::new(#input)
1084 .ok_or_else(|| napi::Error::new(napi::Status::InvalidArg, "referenced ptr is null".to_owned()))?
1085 )?;
1086 _arg_write_index += 1;
1087 }
1088}
1089
1090struct ArgConversions {
1091 pub args: Vec<TokenStream>,
1092 pub arg_conversions: Vec<TokenStream>,
1093 pub this_conversions: Vec<TokenStream>,
1094 pub receiver_unwrap: TokenStream,
1095 pub receiver_dependent_conversions: Vec<TokenStream>,
1096 pub receiver_conversion: TokenStream,
1097 pub refs: Vec<TokenStream>,
1098 pub mut_ref_spans: Vec<Span>,
1099 pub unsafe_: bool,
1100}
1101
1102#[derive(Debug, PartialEq, Eq)]
1103enum NapiArgType {
1104 Ref,
1105 MutRef,
1106 Value,
1107 Env,
1108}
1109
1110impl NapiArgType {
1111 fn is_ref(&self) -> bool {
1112 matches!(self, NapiArgType::Ref | NapiArgType::MutRef)
1113 }
1114}