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