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 }) = pat_type.ty.as_ref()
456 {
457 segments.last().is_some_and(|s| s.ident == "Env")
458 } else {
459 false
460 };
461 if is_env_type {
462 args.push(quote! { __wrapped_env });
463 skipped_arg_count += 1;
464 } else {
465 let is_in_class = self.parent.is_some();
466 if let syn::Type::Path(path) = pat_type.ty.as_ref() {
468 if let Some(p) = path.path.segments.last() {
470 if p.ident == "Reference" {
471 if !is_in_class {
472 bail_span!(p, "`Reference` is only allowed in class methods");
473 }
474 if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
476 args: angle_bracketed_args,
477 ..
478 }) = &p.arguments
479 {
480 if let Some(syn::GenericArgument::Type(syn::Type::Path(path))) =
481 angle_bracketed_args.first()
482 {
483 if let Some(p) = path.path.segments.first() {
484 if p.ident == *self.parent.as_ref().unwrap() {
485 receiver_dependent_conversions.push(quote! {
486 let #injected_ident =
487 napi::bindgen_prelude::Reference::<#path>::from_value_ptr(this_ptr.cast(), env)?;
488 });
489 args.push(quote! { #injected_ident });
490 skipped_arg_count += 1;
491 continue;
492 }
493 }
494 }
495 }
496 } else if p.ident == "This" {
497 if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
499 args: angle_bracketed_args,
500 ..
501 }) = &p.arguments
502 {
503 if let Some(syn::GenericArgument::Type(generic_type)) =
504 angle_bracketed_args.first()
505 {
506 if let syn::Type::Path(syn::TypePath {
507 path: syn::Path { segments, .. },
508 ..
509 }) = generic_type
510 {
511 if let Some(syn::PathSegment { ident, .. }) = segments.first() {
512 if let Some((primitive_type, _)) =
513 crate::PRIMITIVE_TYPES.iter().find(|(p, _)| ident == *p)
514 {
515 bail_span!(
516 ident,
517 "This type must not be {} \nthis in JavaScript function must be `Object` type or `undefined`",
518 primitive_type
519 );
520 }
521 this_conversions.push(quote! {
522 let #injected_ident =
523 <#ident as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.this())?;
524 });
525 args.push(quote! { #injected_ident.into() });
526 skipped_arg_count += 1;
527 continue;
528 }
529 } else if let syn::Type::Reference(syn::TypeReference {
530 elem,
531 mutability,
532 ..
533 }) = generic_type
534 {
535 if let syn::Type::Path(syn::TypePath {
536 path: syn::Path { segments, .. },
537 ..
538 }) = elem.as_ref()
539 {
540 if let Some(syn::PathSegment { ident, .. }) = segments.first() {
541 refs.push(make_ref(quote! { cb.this() }));
542 let token = if mutability.is_some() {
543 mut_ref_spans.push(generic_type.span());
544 quote! { <#ident as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.this())? }
545 } else {
546 quote! { <#ident as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.this())? }
547 };
548 this_conversions.push(quote! {
549 let #injected_ident = #token;
550 });
551 args.push(quote! { #injected_ident.into() });
552 skipped_arg_count += 1;
553 continue;
554 }
555 }
556 }
557 }
558 }
559 refs.push(make_ref(quote! { cb.this() }));
560 this_conversions.push(quote! {
561 let #injected_ident =
562 <napi::bindgen_prelude::This as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.this())?;
563 });
564 args.push(quote! { #injected_ident });
565 skipped_arg_count += 1;
566 continue;
567 }
568 }
569 }
570 let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, pat_type)?;
571 if NapiArgType::MutRef == arg_type {
572 mut_ref_spans.push(pat_type.ty.span());
573 }
574 if arg_type.is_ref() {
575 refs.push(make_ref(quote! { cb.get_arg(#i) }));
576 }
577 if arg_type == NapiArgType::Env {
578 args.push(quote! { &__wrapped_env });
579 skipped_arg_count += 1;
580 continue;
581 }
582 arg_conversions.push(arg_conversion);
583 args.push(quote! { #ident });
584 }
585 }
586 NapiFnArgKind::Callback(cb) => {
587 arg_conversions.push(self.gen_cb_arg_conversion(&ident, i, cb)?);
588 args.push(quote! { #ident });
589 }
590 }
591 }
592
593 Ok(ArgConversions {
594 arg_conversions,
595 this_conversions,
596 receiver_unwrap,
597 receiver_dependent_conversions,
598 receiver_conversion,
599 args,
600 refs,
601 mut_ref_spans,
602 unsafe_: self.unsafe_,
603 })
604 }
605
606 fn gen_ty_arg_conversion(
609 &self,
610 arg_name: &Ident,
611 index: usize,
612 path: &syn::PatType,
613 ) -> BindgenResult<(TokenStream, NapiArgType)> {
614 let mut ty = *path.ty.clone();
615 let type_check = if self.return_if_invalid {
616 quote! {
617 if let Ok(maybe_promise) = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index)) {
618 if !maybe_promise.is_null() {
619 return Ok(maybe_promise);
620 }
621 } else {
622 return Ok(std::ptr::null_mut());
623 }
624 }
625 } else if self.strict {
626 quote! {
627 let maybe_promise = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index))?;
628 if !maybe_promise.is_null() {
629 return Ok(maybe_promise);
630 }
631 }
632 } else {
633 quote! {}
634 };
635
636 let arg_conversion = if self.module_exports {
637 quote! { _napi_module_exports_ }
638 } else {
639 quote! { cb.get_arg(#index) }
640 };
641
642 match ty {
643 syn::Type::Reference(syn::TypeReference {
644 mutability: Some(_),
645 elem,
646 ..
647 }) => {
648 let q = quote! {
649 let #arg_name = {
650 #type_check
651 <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
652 };
653 };
654 Ok((q, NapiArgType::MutRef))
655 }
656 syn::Type::Reference(syn::TypeReference {
657 mutability, elem, ..
658 }) => {
659 if let syn::Type::Slice(slice) = &*elem {
660 if let syn::Type::Path(ele) = &*slice.elem {
661 if let Some(syn::PathSegment { ident, .. }) = ele.path.segments.first() {
662 if TYPEDARRAY_SLICE_TYPES.contains_key(&&*ident.to_string()) {
663 let q = quote! {
664 let #arg_name = {
665 #type_check
666 <&mut #elem as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))?
667 };
668 };
669 return Ok((q, NapiArgType::Ref));
670 }
671 }
672 }
673 }
674 let q = if mutability.is_some() {
675 quote! {
676 let #arg_name = {
677 #type_check
678 <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
679 }
680 }
681 } else {
682 if let syn::Type::Path(syn::TypePath { qself: None, path }) = &*elem {
685 if let Some(syn::PathSegment { ident, .. }) = path.segments.last() {
686 if ident == "Env" {
687 return Ok((quote! {}, NapiArgType::Env));
688 } else if ident == "str" {
689 bail_span!(
690 elem,
691 "JavaScript String is primitive and cannot be passed by reference"
692 );
693 }
694 }
695 }
696 quote! {
697 let #arg_name = {
698 #type_check
699 <#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))?
700 };
701 }
702 };
703 Ok((
704 q,
705 if mutability.is_some() {
706 NapiArgType::MutRef
707 } else {
708 NapiArgType::Ref
709 },
710 ))
711 }
712 _ => {
713 hidden_ty_lifetime(&mut ty)?;
714 let mut arg_type = NapiArgType::Value;
715 let mut is_array = false;
716 if let syn::Type::Path(path) = &ty {
717 if let Some(syn::PathSegment { ident, arguments }) = path.path.segments.first() {
720 if ident == "Vec" {
722 is_array = true;
723 if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
724 args: angle_bracketed_args,
725 ..
726 }) = &arguments
727 {
728 if let Some(syn::GenericArgument::Type(syn::Type::Reference(
730 syn::TypeReference { .. },
731 ))) = angle_bracketed_args.first()
732 {
733 arg_type = NapiArgType::Ref;
735 }
736 }
737 }
738 }
739 }
740 let from_napi_value = if is_array && self.return_if_invalid {
743 quote! {
744 match <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, #arg_conversion) {
745 Ok(value) => value,
746 Err(err) => {
747 if err.status < napi::bindgen_prelude::Status::GenericFailure {
749 return Ok(std::ptr::null_mut());
750 } else {
751 return Err(err);
752 }
753 }
754 }
755 }
756 } else {
757 quote! {
758 <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, #arg_conversion)?
759 }
760 };
761 let q = quote! {
762 let #arg_name = {
763 #type_check
764 #from_napi_value
765 };
766 };
767 Ok((q, arg_type))
768 }
769 }
770 }
771
772 fn gen_cb_arg_conversion(
773 &self,
774 arg_name: &Ident,
775 index: usize,
776 cb: &CallbackArg,
777 ) -> BindgenResult<TokenStream> {
778 let mut inputs = vec![];
779 let mut arg_conversions = vec![];
780
781 for (i, ty) in cb.args.iter().enumerate() {
782 let cb_arg_ident = Ident::new(&format!("callback_arg_{i}"), Span::call_site());
783 inputs.push(quote! { #cb_arg_ident: #ty });
784 let mut maybe_has_lifetime_ty = ty.clone();
785 hidden_ty_lifetime(&mut maybe_has_lifetime_ty)?;
786 arg_conversions.push(
787 quote! { <#maybe_has_lifetime_ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #cb_arg_ident)? },
788 );
789 }
790
791 let ret = match &cb.ret {
792 Some(ty) => {
793 quote! {
794 let ret = <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, ret_ptr)?;
795
796 Ok(ret)
797 }
798 }
799 None => quote! { Ok(()) },
800 };
801
802 Ok(quote! {
803 napi::bindgen_prelude::assert_type_of!(env, cb.get_arg(#index), napi::bindgen_prelude::ValueType::Function)?;
804 let #arg_name = |#(#inputs),*| {
805 let args = vec![
806 #(#arg_conversions),*
807 ];
808
809 let mut ret_ptr = std::ptr::null_mut();
810
811 napi::bindgen_prelude::check_pending_exception!(
812 env,
813 napi::bindgen_prelude::sys::napi_call_function(
814 env,
815 cb.this(),
816 cb.get_arg(#index),
817 args.len(),
818 args.as_ptr(),
819 &mut ret_ptr
820 )
821 )?;
822
823 #ret
824 };
825 })
826 }
827
828 fn gen_fn_receiver(&self) -> TokenStream {
829 let name = &self.name;
830
831 match self.fn_self {
832 Some(FnSelf::Value) => {
833 unreachable!();
835 }
836 Some(FnSelf::Ref) | Some(FnSelf::MutRef) => quote! { this.#name },
837 None => match &self.parent {
838 Some(class) => quote! { #class::#name },
839 None => quote! { #name },
840 },
841 }
842 }
843
844 fn gen_fn_return(&self, ret: &Ident) -> BindgenResult<TokenStream> {
845 let js_name = &self.js_name;
846
847 if let Some(ty) = &self.ret {
848 let ty_string = ty.into_token_stream().to_string();
849 let is_return_self = ty_string == "& Self" || ty_string == "&mut Self";
850 if self.kind == FnKind::Constructor {
851 let parent = self
852 .parent
853 .as_ref()
854 .expect("Parent must exist for constructor");
855 if self.is_ret_result {
856 if self.parent_is_generator {
857 Ok(quote! { cb.construct_generator::<false, _>(#js_name, #ret?) })
858 } else if self.parent_is_async_generator {
859 Ok(quote! { cb.construct_async_generator::<false, _>(#js_name, #ret?) })
860 } else {
861 Ok(quote! {
862 match #ret {
863 Ok(value) => {
864 cb.construct::<false, _>(#js_name, value)
865 }
866 Err(err) => {
867 napi::bindgen_prelude::JsError::from(err).throw_into(env);
868 Ok(std::ptr::null_mut())
869 }
870 }
871 })
872 }
873 } else if self.parent_is_generator {
874 Ok(quote! { cb.construct_generator::<false, #parent>(#js_name, #ret) })
875 } else if self.parent_is_async_generator {
876 Ok(quote! { cb.construct_async_generator::<false, #parent>(#js_name, #ret) })
877 } else {
878 Ok(quote! { cb.construct::<false, #parent>(#js_name, #ret) })
879 }
880 } else if self.kind == FnKind::Factory {
881 if self.is_ret_result {
882 if self.parent_is_generator {
883 Ok(quote! { cb.generator_factory(#js_name, #ret?) })
884 } else if self.parent_is_async_generator {
885 Ok(quote! { cb.async_generator_factory(#js_name, #ret?) })
886 } else if self.is_async {
887 Ok(quote! { cb.factory(#js_name, #ret) })
888 } else {
889 Ok(quote! {
890 match #ret {
891 Ok(value) => {
892 cb.factory(#js_name, value)
893 }
894 Err(err) => {
895 napi::bindgen_prelude::JsError::from(err).throw_into(env);
896 Ok(std::ptr::null_mut())
897 }
898 }
899 })
900 }
901 } else if self.parent_is_generator {
902 Ok(quote! { cb.generator_factory(#js_name, #ret) })
903 } else if self.parent_is_async_generator {
904 Ok(quote! { cb.async_generator_factory(#js_name, #ret) })
905 } else {
906 Ok(quote! { cb.factory(#js_name, #ret) })
907 }
908 } else if self.is_ret_result {
909 if self.is_async {
910 Ok(quote! {
911 <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret)
912 })
913 } else if is_return_self {
914 Ok(quote! { #ret.map(|_| cb.this()) })
915 } else {
916 Ok(quote! {
917 match #ret {
918 Ok(value) => napi::bindgen_prelude::ToNapiValue::to_napi_value(env, value),
919 Err(err) => {
920 napi::bindgen_prelude::JsError::from(err).throw_into(env);
921 Ok(std::ptr::null_mut())
922 },
923 }
924 })
925 }
926 } else if is_return_self {
927 Ok(quote! { Ok(cb.this()) })
928 } else {
929 let mut return_ty = ty.clone();
930 hidden_ty_lifetime(&mut return_ty)?;
931 Ok(quote! {
932 <#return_ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret)
933 })
934 }
935 } else {
936 Ok(quote! {
937 <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ())
938 })
939 }
940 }
941
942 fn gen_fn_register(&self) -> TokenStream {
943 if self.parent.is_some() || cfg!(test) {
944 quote! {}
945 } else {
946 let name_str = self.name.to_string();
947 let js_name = format!("{}\0", self.js_name);
948 let name_len = self.js_name.len();
949 let module_register_name = &self.register_name;
950 let intermediate_ident = get_intermediate_ident(&name_str);
951 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
952 let cb_name = Ident::new(
953 &format!("_napi_rs_internal_register_{name_str}"),
954 Span::call_site(),
955 );
956
957 if self.module_exports {
958 return quote! {
959 #[doc(hidden)]
960 #[allow(non_snake_case)]
961 #[allow(clippy::all)]
962 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> {
963 #intermediate_ident(env, exports)?;
964 Ok(exports)
965 }
966
967 #[cfg(all(not(test), not(target_family = "wasm")))]
968 napi::ctor::declarative::ctor! {
969 #[doc(hidden)]
970 #[allow(clippy::all)]
971 #[allow(non_snake_case)]
972 #[ctor(unsafe)]
973 fn #module_register_name() {
974 napi::bindgen_prelude::register_module_export_hook(#cb_name);
975 }
976 }
977
978 #[allow(clippy::all)]
979 #[allow(non_snake_case)]
980 #[cfg(all(not(test), target_family = "wasm"))]
981 #[no_mangle]
982 extern "C" fn #module_register_name() {
983 napi::bindgen_prelude::register_module_export_hook(#cb_name);
984 }
985 };
986 }
987
988 let register_module_export_tokens = if self.no_export {
989 quote! {}
990 } else {
991 quote! {
992 #[cfg(all(not(test), not(target_family = "wasm")))]
993 napi::ctor::declarative::ctor! {
994 #[doc(hidden)]
995 #[allow(clippy::all)]
996 #[allow(non_snake_case)]
997 #[ctor(unsafe)]
998 fn #module_register_name() {
999 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
1000 }
1001 }
1002
1003 #[doc(hidden)]
1004 #[allow(clippy::all)]
1005 #[allow(non_snake_case)]
1006 #[cfg(all(not(test), target_family = "wasm"))]
1007 #[no_mangle]
1008 extern "C" fn #module_register_name() {
1009 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
1010 }
1011 }
1012 };
1013
1014 quote! {
1015 #[doc(hidden)]
1016 #[allow(non_snake_case)]
1017 #[allow(clippy::all)]
1018 unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1019 let mut fn_ptr = std::ptr::null_mut();
1020
1021 napi::bindgen_prelude::check_status!(
1022 napi::bindgen_prelude::sys::napi_create_function(
1023 env,
1024 #js_name.as_ptr().cast(),
1025 #name_len as isize,
1026 Some(#intermediate_ident),
1027 std::ptr::null_mut(),
1028 &mut fn_ptr,
1029 ),
1030 "Failed to register function `{}`",
1031 #name_str,
1032 )?;
1033 Ok(fn_ptr)
1034 }
1035
1036 #register_module_export_tokens
1037 }
1038 }
1039 }
1040}
1041
1042fn hidden_ty_lifetime(ty: &mut syn::Type) -> BindgenResult<()> {
1043 match ty {
1044 Type::Path(TypePath {
1045 path: syn::Path { segments, .. },
1046 ..
1047 }) => {
1048 if let Some(syn::PathSegment {
1049 arguments:
1050 syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
1051 ..
1052 }) = segments.last_mut()
1053 {
1054 let mut has_lifetime = false;
1055 if let Some(syn::GenericArgument::Lifetime(lt)) = args.first_mut() {
1056 *lt = syn::Lifetime::new("'_", Span::call_site());
1057 has_lifetime = true;
1058 }
1059 for arg in args.iter_mut().skip(if has_lifetime { 1 } else { 0 }) {
1060 if let syn::GenericArgument::Type(ty) = arg {
1061 hidden_ty_lifetime(ty)?;
1062 }
1063 }
1064 }
1065 }
1066 Type::Reference(TypeReference {
1067 lifetime: Some(lt), ..
1068 }) => {
1069 *lt = syn::Lifetime::new("'_", Span::call_site());
1070 }
1071 _ => {}
1072 }
1073 Ok(())
1074}
1075
1076fn make_ref(input: TokenStream) -> TokenStream {
1077 quote! {
1078 _args_array[_arg_write_index] = _make_ref(
1079 ::std::ptr::NonNull::new(#input)
1080 .ok_or_else(|| napi::Error::new(napi::Status::InvalidArg, "referenced ptr is null".to_owned()))?
1081 )?;
1082 _arg_write_index += 1;
1083 }
1084}
1085
1086struct ArgConversions {
1087 pub args: Vec<TokenStream>,
1088 pub arg_conversions: Vec<TokenStream>,
1089 pub this_conversions: Vec<TokenStream>,
1090 pub receiver_unwrap: TokenStream,
1091 pub receiver_dependent_conversions: Vec<TokenStream>,
1092 pub receiver_conversion: TokenStream,
1093 pub refs: Vec<TokenStream>,
1094 pub mut_ref_spans: Vec<Span>,
1095 pub unsafe_: bool,
1096}
1097
1098#[derive(Debug, PartialEq, Eq)]
1099enum NapiArgType {
1100 Ref,
1101 MutRef,
1102 Value,
1103 Env,
1104}
1105
1106impl NapiArgType {
1107 fn is_ref(&self) -> bool {
1108 matches!(self, NapiArgType::Ref | NapiArgType::MutRef)
1109 }
1110}