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