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