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