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