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