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
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 let mut is_array = false;
524 if let syn::Type::Path(path) = &ty {
525 if let Some(syn::PathSegment { ident, arguments }) = path.path.segments.first() {
528 if ident == "Vec" {
530 is_array = true;
531 if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
532 args: angle_bracketed_args,
533 ..
534 }) = &arguments
535 {
536 if let Some(syn::GenericArgument::Type(syn::Type::Reference(
538 syn::TypeReference { .. },
539 ))) = angle_bracketed_args.first()
540 {
541 arg_type = NapiArgType::Ref;
543 }
544 }
545 }
546 }
547 }
548 let from_napi_value = if is_array && self.return_if_invalid {
551 quote! {
552 match <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, #arg_conversion) {
553 Ok(value) => value,
554 Err(err) => {
555 if err.status < napi::bindgen_prelude::Status::GenericFailure {
557 return Ok(std::ptr::null_mut());
558 } else {
559 return Err(err);
560 }
561 }
562 }
563 }
564 } else {
565 quote! {
566 <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, #arg_conversion)?
567 }
568 };
569 let q = quote! {
570 let #arg_name = {
571 #type_check
572 #from_napi_value
573 };
574 };
575 Ok((q, arg_type))
576 }
577 }
578 }
579
580 fn gen_cb_arg_conversion(
581 &self,
582 arg_name: &Ident,
583 index: usize,
584 cb: &CallbackArg,
585 ) -> BindgenResult<TokenStream> {
586 let mut inputs = vec![];
587 let mut arg_conversions = vec![];
588
589 for (i, ty) in cb.args.iter().enumerate() {
590 let cb_arg_ident = Ident::new(&format!("callback_arg_{i}"), Span::call_site());
591 inputs.push(quote! { #cb_arg_ident: #ty });
592 let mut maybe_has_lifetime_ty = ty.clone();
593 hidden_ty_lifetime(&mut maybe_has_lifetime_ty)?;
594 arg_conversions.push(
595 quote! { <#maybe_has_lifetime_ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #cb_arg_ident)? },
596 );
597 }
598
599 let ret = match &cb.ret {
600 Some(ty) => {
601 quote! {
602 let ret = <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, ret_ptr)?;
603
604 Ok(ret)
605 }
606 }
607 None => quote! { Ok(()) },
608 };
609
610 Ok(quote! {
611 napi::bindgen_prelude::assert_type_of!(env, cb.get_arg(#index), napi::bindgen_prelude::ValueType::Function)?;
612 let #arg_name = |#(#inputs),*| {
613 let args = vec![
614 #(#arg_conversions),*
615 ];
616
617 let mut ret_ptr = std::ptr::null_mut();
618
619 napi::bindgen_prelude::check_pending_exception!(
620 env,
621 napi::bindgen_prelude::sys::napi_call_function(
622 env,
623 cb.this(),
624 cb.get_arg(#index),
625 args.len(),
626 args.as_ptr(),
627 &mut ret_ptr
628 )
629 )?;
630
631 #ret
632 };
633 })
634 }
635
636 fn gen_fn_receiver(&self) -> TokenStream {
637 let name = &self.name;
638
639 match self.fn_self {
640 Some(FnSelf::Value) => {
641 unreachable!();
643 }
644 Some(FnSelf::Ref) | Some(FnSelf::MutRef) => quote! { this.#name },
645 None => match &self.parent {
646 Some(class) => quote! { #class::#name },
647 None => quote! { #name },
648 },
649 }
650 }
651
652 fn gen_fn_return(&self, ret: &Ident) -> BindgenResult<TokenStream> {
653 let js_name = &self.js_name;
654
655 if let Some(ty) = &self.ret {
656 let ty_string = ty.into_token_stream().to_string();
657 let is_return_self = ty_string == "& Self" || ty_string == "&mut Self";
658 if self.kind == FnKind::Constructor {
659 let parent = self
660 .parent
661 .as_ref()
662 .expect("Parent must exist for constructor");
663 if self.is_ret_result {
664 if self.parent_is_generator {
665 Ok(quote! { cb.construct_generator::<false, _>(#js_name, #ret?) })
666 } else {
667 Ok(quote! {
668 match #ret {
669 Ok(value) => {
670 cb.construct::<false, _>(#js_name, value)
671 }
672 Err(err) => {
673 napi::bindgen_prelude::JsError::from(err).throw_into(env);
674 Ok(std::ptr::null_mut())
675 }
676 }
677 })
678 }
679 } else if self.parent_is_generator {
680 Ok(quote! { cb.construct_generator::<false, #parent>(#js_name, #ret) })
681 } else {
682 Ok(quote! { cb.construct::<false, #parent>(#js_name, #ret) })
683 }
684 } else if self.kind == FnKind::Factory {
685 if self.is_ret_result {
686 if self.parent_is_generator {
687 Ok(quote! { cb.generator_factory(#js_name, #ret?) })
688 } else if self.is_async {
689 Ok(quote! { cb.factory(#js_name, #ret) })
690 } else {
691 Ok(quote! {
692 match #ret {
693 Ok(value) => {
694 cb.factory(#js_name, value)
695 }
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 self.parent_is_generator {
704 Ok(quote! { cb.generator_factory(#js_name, #ret) })
705 } else {
706 Ok(quote! { cb.factory(#js_name, #ret) })
707 }
708 } else if self.is_ret_result {
709 if self.is_async {
710 Ok(quote! {
711 <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret)
712 })
713 } else if is_return_self {
714 Ok(quote! { #ret.map(|_| cb.this) })
715 } else {
716 Ok(quote! {
717 match #ret {
718 Ok(value) => napi::bindgen_prelude::ToNapiValue::to_napi_value(env, value),
719 Err(err) => {
720 napi::bindgen_prelude::JsError::from(err).throw_into(env);
721 Ok(std::ptr::null_mut())
722 },
723 }
724 })
725 }
726 } else if is_return_self {
727 Ok(quote! { Ok(cb.this) })
728 } else {
729 let mut return_ty = ty.clone();
730 hidden_ty_lifetime(&mut return_ty)?;
731 Ok(quote! {
732 <#return_ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret)
733 })
734 }
735 } else {
736 Ok(quote! {
737 <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ())
738 })
739 }
740 }
741
742 fn gen_fn_register(&self) -> TokenStream {
743 if self.parent.is_some() || cfg!(test) {
744 quote! {}
745 } else {
746 let name_str = self.name.to_string();
747 let js_name = format!("{}\0", &self.js_name);
748 let name_len = self.js_name.len();
749 let module_register_name = &self.register_name;
750 let intermediate_ident = get_intermediate_ident(&name_str);
751 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
752 let cb_name = Ident::new(
753 &format!("_napi_rs_internal_register_{name_str}"),
754 Span::call_site(),
755 );
756
757 if self.module_exports {
758 return quote! {
759 #[doc(hidden)]
760 #[allow(non_snake_case)]
761 #[allow(clippy::all)]
762 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> {
763 #intermediate_ident(env, exports)?;
764 Ok(exports)
765 }
766
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_hook(#cb_name);
774 }
775
776 #[allow(clippy::all)]
777 #[allow(non_snake_case)]
778 #[cfg(all(not(test), target_family = "wasm"))]
779 #[no_mangle]
780 extern "C" fn #module_register_name() {
781 napi::bindgen_prelude::register_module_export_hook(#cb_name);
782 }
783 };
784 }
785
786 let register_module_export_tokens = if self.no_export {
787 quote! {}
788 } else {
789 quote! {
790 #[doc(hidden)]
791 #[allow(clippy::all)]
792 #[allow(non_snake_case)]
793 #[cfg(all(not(test), not(target_family = "wasm")))]
794 #[napi::ctor::ctor(crate_path=::napi::ctor)]
795 fn #module_register_name() {
796 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
797 }
798
799 #[doc(hidden)]
800 #[allow(clippy::all)]
801 #[allow(non_snake_case)]
802 #[cfg(all(not(test), target_family = "wasm"))]
803 #[no_mangle]
804 extern "C" fn #module_register_name() {
805 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name);
806 }
807 }
808 };
809
810 quote! {
811 #[doc(hidden)]
812 #[allow(non_snake_case)]
813 #[allow(clippy::all)]
814 unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
815 let mut fn_ptr = std::ptr::null_mut();
816
817 napi::bindgen_prelude::check_status!(
818 napi::bindgen_prelude::sys::napi_create_function(
819 env,
820 #js_name.as_ptr().cast(),
821 #name_len as isize,
822 Some(#intermediate_ident),
823 std::ptr::null_mut(),
824 &mut fn_ptr,
825 ),
826 "Failed to register function `{}`",
827 #name_str,
828 )?;
829 Ok(fn_ptr)
830 }
831
832 #register_module_export_tokens
833 }
834 }
835 }
836}
837
838fn hidden_ty_lifetime(ty: &mut syn::Type) -> BindgenResult<()> {
839 match ty {
840 Type::Path(TypePath {
841 path: syn::Path { segments, .. },
842 ..
843 }) => {
844 if let Some(syn::PathSegment {
845 arguments:
846 syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
847 ..
848 }) = segments.last_mut()
849 {
850 let mut has_lifetime = false;
851 if let Some(syn::GenericArgument::Lifetime(lt)) = args.first_mut() {
852 *lt = syn::Lifetime::new("'_", Span::call_site());
853 has_lifetime = true;
854 }
855 for arg in args.iter_mut().skip(if has_lifetime { 1 } else { 0 }) {
856 if let syn::GenericArgument::Type(ty) = arg {
857 hidden_ty_lifetime(ty)?;
858 }
859 }
860 }
861 }
862 Type::Reference(TypeReference {
863 lifetime: Some(lt), ..
864 }) => {
865 *lt = syn::Lifetime::new("'_", Span::call_site());
866 }
867 _ => {}
868 }
869 Ok(())
870}
871
872fn make_ref(input: TokenStream) -> TokenStream {
873 quote! {
874 _args_array[_arg_write_index] = _make_ref(
875 ::std::ptr::NonNull::new(#input)
876 .ok_or_else(|| napi::Error::new(napi::Status::InvalidArg, "referenced ptr is null".to_owned()))?
877 )?;
878 _arg_write_index += 1;
879 }
880}
881
882struct ArgConversions {
883 pub args: Vec<TokenStream>,
884 pub arg_conversions: Vec<TokenStream>,
885 pub refs: Vec<TokenStream>,
886 pub mut_ref_spans: Vec<Span>,
887 pub unsafe_: bool,
888}
889
890#[derive(Debug, PartialEq, Eq)]
891enum NapiArgType {
892 Ref,
893 MutRef,
894 Value,
895 Env,
896}
897
898impl NapiArgType {
899 fn is_ref(&self) -> bool {
900 matches!(self, NapiArgType::Ref | NapiArgType::MutRef)
901 }
902}