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