napi_derive_backend/codegen/
struct.rs

1use std::collections::HashMap;
2use std::sync::atomic::{AtomicU32, Ordering};
3
4use proc_macro2::{Ident, Literal, Span, TokenStream};
5use quote::ToTokens;
6
7use crate::{
8  codegen::{get_intermediate_ident, js_mod_to_token_stream},
9  BindgenResult, FnKind, NapiImpl, NapiStruct, NapiStructKind, TryToTokens,
10};
11
12static NAPI_IMPL_ID: AtomicU32 = AtomicU32::new(0);
13const TYPED_ARRAY_TYPE: &[&str] = &[
14  "Int8Array",
15  "Uint8Array",
16  "Uint8ClampedArray",
17  "Int16Array",
18  "Uint16Array",
19  "Int32Array",
20  "Uint32Array",
21  "Float32Array",
22  "Float64Array",
23  "BigInt64Array",
24  "BigUint64Array",
25];
26
27// Generate trait implementations for given Struct.
28fn gen_napi_value_map_impl(name: &Ident, to_napi_val_impl: TokenStream) -> TokenStream {
29  let name_str = name.to_string();
30  let js_name_str = format!("{}\0", name_str);
31  let validate = quote! {
32    unsafe fn validate(env: napi::sys::napi_env, napi_val: napi::sys::napi_value) -> napi::Result<napi::sys::napi_value> {
33      if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
34        let mut ctor = std::ptr::null_mut();
35        napi::check_status!(
36          napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
37          "Failed to get constructor reference of class `{}`",
38          #name_str
39        )?;
40        let mut is_instance_of = false;
41        napi::check_status!(
42          napi::sys::napi_instanceof(env, napi_val, ctor, &mut is_instance_of),
43          "Failed to get external value of class `{}`",
44          #name_str
45        )?;
46        if is_instance_of {
47          Ok(std::ptr::null_mut())
48        } else {
49          Err(napi::Error::new(
50            napi::Status::InvalidArg,
51            format!("Value is not instanceof class `{}`", #name_str)
52          ))
53        }
54      } else {
55        Err(napi::Error::new(
56          napi::Status::InvalidArg,
57          format!("Failed to get constructor of class `{}`", #name_str)
58        ))
59      }
60    }
61  };
62  quote! {
63    impl napi::bindgen_prelude::TypeName for #name {
64      fn type_name() -> &'static str {
65        #name_str
66      }
67
68      fn value_type() -> napi::ValueType {
69        napi::ValueType::Function
70      }
71    }
72
73    impl napi::bindgen_prelude::TypeName for &#name {
74      fn type_name() -> &'static str {
75        #name_str
76      }
77
78      fn value_type() -> napi::ValueType {
79        napi::ValueType::Object
80      }
81    }
82
83    impl napi::bindgen_prelude::TypeName for &mut #name {
84      fn type_name() -> &'static str {
85        #name_str
86      }
87
88      fn value_type() -> napi::ValueType {
89        napi::ValueType::Object
90      }
91    }
92
93    #to_napi_val_impl
94
95    impl napi::bindgen_prelude::FromNapiRef for #name {
96      unsafe fn from_napi_ref(
97        env: napi::bindgen_prelude::sys::napi_env,
98        napi_val: napi::bindgen_prelude::sys::napi_value
99      ) -> napi::bindgen_prelude::Result<&'static Self> {
100        let mut wrapped_val: *mut std::ffi::c_void = std::ptr::null_mut();
101
102        napi::bindgen_prelude::check_status!(
103          napi::bindgen_prelude::sys::napi_unwrap(env, napi_val, &mut wrapped_val),
104          "Failed to recover `{}` type from napi value",
105          #name_str,
106        )?;
107
108        Ok(&*(wrapped_val as *const #name))
109      }
110    }
111
112    impl napi::bindgen_prelude::FromNapiMutRef for #name {
113      unsafe fn from_napi_mut_ref(
114        env: napi::bindgen_prelude::sys::napi_env,
115        napi_val: napi::bindgen_prelude::sys::napi_value
116      ) -> napi::bindgen_prelude::Result<&'static mut Self> {
117        let mut wrapped_val: *mut std::ffi::c_void = std::ptr::null_mut();
118
119        napi::bindgen_prelude::check_status!(
120          napi::bindgen_prelude::sys::napi_unwrap(env, napi_val, &mut wrapped_val),
121          "Failed to recover `{}` type from napi value",
122          #name_str,
123        )?;
124
125        Ok(&mut *(wrapped_val as *mut #name))
126      }
127    }
128
129    impl napi::bindgen_prelude::FromNapiValue for &#name {
130      unsafe fn from_napi_value(
131        env: napi::bindgen_prelude::sys::napi_env,
132        napi_val: napi::bindgen_prelude::sys::napi_value
133      ) -> napi::bindgen_prelude::Result<Self> {
134        napi::bindgen_prelude::FromNapiRef::from_napi_ref(env, napi_val)
135      }
136    }
137
138    impl napi::bindgen_prelude::FromNapiValue for &mut #name {
139      unsafe fn from_napi_value(
140        env: napi::bindgen_prelude::sys::napi_env,
141        napi_val: napi::bindgen_prelude::sys::napi_value
142      ) -> napi::bindgen_prelude::Result<Self> {
143        napi::bindgen_prelude::FromNapiMutRef::from_napi_mut_ref(env, napi_val)
144      }
145    }
146
147    impl napi::bindgen_prelude::ValidateNapiValue for &#name {
148      #validate
149    }
150
151    impl napi::bindgen_prelude::ValidateNapiValue for &mut #name {
152      #validate
153    }
154  }
155}
156
157impl TryToTokens for NapiStruct {
158  fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
159    let napi_value_map_impl = self.gen_napi_value_map_impl();
160
161    let class_helper_mod = if self.kind == NapiStructKind::Object {
162      quote! {}
163    } else {
164      self.gen_helper_mod()
165    };
166
167    (quote! {
168      #napi_value_map_impl
169      #class_helper_mod
170    })
171    .to_tokens(tokens);
172
173    Ok(())
174  }
175}
176
177impl NapiStruct {
178  fn gen_helper_mod(&self) -> TokenStream {
179    let mod_name = Ident::new(&format!("__napi_helper__{}", self.name), Span::call_site());
180
181    let ctor = if self.kind == NapiStructKind::Constructor {
182      self.gen_default_ctor()
183    } else {
184      quote! {}
185    };
186
187    let mut getters_setters = self.gen_default_getters_setters();
188    getters_setters.sort_by(|a, b| a.0.cmp(&b.0));
189    let register = self.gen_register();
190
191    let getters_setters_token = getters_setters.into_iter().map(|(_, token)| token);
192
193    quote! {
194      #[allow(clippy::all)]
195      #[allow(non_snake_case)]
196      mod #mod_name {
197        use std::ptr;
198        use super::*;
199
200        #ctor
201        #(#getters_setters_token)*
202        #register
203      }
204    }
205  }
206
207  fn gen_default_ctor(&self) -> TokenStream {
208    let name = &self.name;
209    let js_name_str = &self.js_name;
210    let fields_len = self.fields.len();
211    let mut fields = vec![];
212
213    for (i, field) in self.fields.iter().enumerate() {
214      let ty = &field.ty;
215      match &field.name {
216        syn::Member::Named(ident) => fields
217          .push(quote! { #ident: <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#i))? }),
218        syn::Member::Unnamed(_) => {
219          fields.push(quote! { <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#i))? });
220        }
221      }
222    }
223
224    let construct = if self.is_tuple {
225      quote! { #name (#(#fields),*) }
226    } else {
227      quote! { #name {#(#fields),*} }
228    };
229
230    let is_empty_struct_hint = fields_len == 0;
231
232    let constructor = if self.implement_iterator {
233      quote! { unsafe { cb.construct_generator::<#is_empty_struct_hint, #name>(#js_name_str, #construct) } }
234    } else {
235      quote! { unsafe { cb.construct::<#is_empty_struct_hint, #name>(#js_name_str, #construct) } }
236    };
237
238    quote! {
239      extern "C" fn constructor(
240        env: napi::bindgen_prelude::sys::napi_env,
241        cb: napi::bindgen_prelude::sys::napi_callback_info
242      ) -> napi::bindgen_prelude::sys::napi_value {
243        napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None, false)
244          .and_then(|cb| #constructor)
245          .unwrap_or_else(|e| {
246            unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
247            std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
248          })
249      }
250    }
251  }
252
253  fn gen_napi_value_map_impl(&self) -> TokenStream {
254    match self.kind {
255      NapiStructKind::None => gen_napi_value_map_impl(
256        &self.name,
257        self.gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(),
258      ),
259      NapiStructKind::Constructor => {
260        gen_napi_value_map_impl(&self.name, self.gen_to_napi_value_ctor_impl())
261      }
262      NapiStructKind::Object => self.gen_to_napi_value_obj_impl(),
263    }
264  }
265
266  fn gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(&self) -> TokenStream {
267    let name = &self.name;
268    let js_name_raw = &self.js_name;
269    let js_name_str = format!("{}\0", js_name_raw);
270    let iterator_implementation = self.gen_iterator_property(name);
271    let finalize_trait = if self.use_custom_finalize {
272      quote! {}
273    } else {
274      quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} }
275    };
276    let instance_of_impl = self.gen_instance_of_impl(name, &js_name_str);
277    quote! {
278      impl napi::bindgen_prelude::ToNapiValue for #name {
279        unsafe fn to_napi_value(
280          env: napi::sys::napi_env,
281          val: #name
282        ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
283          if let Some(ctor_ref) = napi::__private::get_class_constructor(#js_name_str) {
284            let mut wrapped_value = Box::into_raw(Box::new(val));
285            if wrapped_value as usize == 0x1 {
286              wrapped_value = Box::into_raw(Box::new(0u8)).cast();
287            }
288            let instance_value = #name::new_instance(env, wrapped_value.cast(), ctor_ref)?;
289            #iterator_implementation
290            Ok(instance_value)
291          } else {
292            Err(napi::bindgen_prelude::Error::new(
293              napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}` in `ToNapiValue`", #js_name_raw))
294            )
295          }
296        }
297      }
298
299      #finalize_trait
300      #instance_of_impl
301      impl #name {
302        pub fn into_reference(val: #name, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<#name>> {
303          if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
304            unsafe {
305              let mut wrapped_value = Box::into_raw(Box::new(val));
306              if wrapped_value as usize == 0x1 {
307                wrapped_value = Box::into_raw(Box::new(0u8)).cast();
308              }
309              let instance_value = #name::new_instance(env.raw(), wrapped_value.cast(), ctor_ref)?;
310              {
311                let env = env.raw();
312                #iterator_implementation
313              }
314              napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value.cast(), env.raw())
315            }
316          } else {
317            Err(napi::bindgen_prelude::Error::new(
318              napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
319            )
320          }
321        }
322
323        pub fn into_instance(self, env: napi::Env) -> napi::Result<napi::bindgen_prelude::ClassInstance<#name>> {
324          if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
325            unsafe {
326              let wrapped_value = Box::leak(Box::new(self));
327              let instance_value = #name::new_instance(env.raw(), wrapped_value as *mut _ as *mut std::ffi::c_void, ctor_ref)?;
328
329              Ok(napi::bindgen_prelude::ClassInstance::<#name>::new(instance_value, wrapped_value))
330            }
331          } else {
332            Err(napi::bindgen_prelude::Error::new(
333              napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
334            )
335          }
336        }
337
338        unsafe fn new_instance(
339          env: napi::sys::napi_env,
340          wrapped_value: *mut std::ffi::c_void,
341          ctor_ref: napi::sys::napi_ref,
342        ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
343          let mut ctor = std::ptr::null_mut();
344          napi::check_status!(
345            napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
346            "Failed to get constructor reference of class `{}`",
347            #js_name_raw
348          )?;
349
350          let mut result = std::ptr::null_mut();
351          napi::__private::___CALL_FROM_FACTORY.with(|inner| inner.store(true, std::sync::atomic::Ordering::Relaxed));
352          napi::check_status!(
353            napi::sys::napi_new_instance(env, ctor, 0, std::ptr::null_mut(), &mut result),
354            "Failed to construct class `{}`",
355            #js_name_raw
356          )?;
357          napi::__private::___CALL_FROM_FACTORY.with(|inner| inner.store(false, std::sync::atomic::Ordering::Relaxed));
358          let mut object_ref = std::ptr::null_mut();
359          let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
360          let finalize_callbacks_ptr = std::rc::Rc::into_raw(std::rc::Rc::new(std::cell::Cell::new(Box::into_raw(initial_finalize))));
361          napi::check_status!(
362            napi::sys::napi_wrap(
363              env,
364              result,
365              wrapped_value,
366              Some(napi::bindgen_prelude::raw_finalize_unchecked::<#name>),
367              std::ptr::null_mut(),
368              &mut object_ref,
369            ),
370            "Failed to wrap native object of class `{}`",
371            #js_name_raw
372          )?;
373          napi::bindgen_prelude::Reference::<#name>::add_ref(env, wrapped_value, (wrapped_value, object_ref, finalize_callbacks_ptr));
374          Ok(result)
375        }
376      }
377    }
378  }
379
380  fn gen_iterator_property(&self, name: &Ident) -> TokenStream {
381    if !self.implement_iterator {
382      return quote! {};
383    }
384    quote! {
385      napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value);
386    }
387  }
388
389  fn gen_to_napi_value_ctor_impl(&self) -> TokenStream {
390    let name = &self.name;
391    let js_name_without_null = &self.js_name;
392    let js_name_str = format!("{}\0", &self.js_name);
393    let instance_of_impl = self.gen_instance_of_impl(name, &js_name_str);
394
395    let mut field_conversions = vec![];
396    let mut field_destructions = vec![];
397
398    for field in self.fields.iter() {
399      let ty = &field.ty;
400
401      match &field.name {
402        syn::Member::Named(ident) => {
403          // alias here prevents field name shadowing
404          let alias_ident = format_ident!("{}_", ident);
405          field_destructions.push(quote! { #ident: #alias_ident });
406          field_conversions.push(
407            quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #alias_ident)? },
408          );
409        }
410        syn::Member::Unnamed(i) => {
411          field_destructions.push(quote! { arg #i });
412          field_conversions.push(
413            quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, arg #i)? },
414          );
415        }
416      }
417    }
418
419    let destructed_fields = if self.is_tuple {
420      quote! {
421        Self (#(#field_destructions),*)
422      }
423    } else {
424      quote! {
425        Self {#(#field_destructions),*}
426      }
427    };
428
429    let finalize_trait = if self.use_custom_finalize {
430      quote! {}
431    } else {
432      quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} }
433    };
434
435    quote! {
436      impl napi::bindgen_prelude::ToNapiValue for #name {
437        unsafe fn to_napi_value(
438          env: napi::bindgen_prelude::sys::napi_env,
439          val: #name,
440        ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
441          if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
442            let mut ctor = std::ptr::null_mut();
443
444            napi::bindgen_prelude::check_status!(
445              napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
446              "Failed to get constructor reference of class `{}`",
447              #js_name_without_null
448            )?;
449
450            let mut instance_value = std::ptr::null_mut();
451            let #destructed_fields = val;
452            let args = vec![#(#field_conversions),*];
453
454            napi::bindgen_prelude::check_status!(
455              napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut instance_value),
456              "Failed to construct class `{}`",
457              #js_name_without_null
458            )?;
459
460            Ok(instance_value)
461          } else {
462            Err(napi::bindgen_prelude::Error::new(
463              napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
464            )
465          }
466        }
467      }
468      #instance_of_impl
469      #finalize_trait
470    }
471  }
472
473  fn gen_to_napi_value_obj_impl(&self) -> TokenStream {
474    let name = &self.name;
475    let name_str = self.name.to_string();
476
477    let mut obj_field_setters = vec![];
478    let mut obj_field_getters = vec![];
479    let mut field_destructions = vec![];
480
481    for field in self.fields.iter() {
482      let field_js_name = &field.js_name;
483      let ty = &field.ty;
484      let is_optional_field = if let syn::Type::Path(syn::TypePath {
485        path: syn::Path { segments, .. },
486        ..
487      }) = &ty
488      {
489        if let Some(last_path) = segments.last() {
490          last_path.ident == "Option"
491        } else {
492          false
493        }
494      } else {
495        false
496      };
497      match &field.name {
498        syn::Member::Named(ident) => {
499          let alias_ident = format_ident!("{}_", ident);
500          field_destructions.push(quote! { #ident: #alias_ident });
501          if is_optional_field {
502            obj_field_setters.push(match self.use_nullable {
503              false => quote! {
504                if #alias_ident.is_some() {
505                  obj.set(#field_js_name, #alias_ident)?;
506                }
507              },
508              true => quote! {
509                if let Some(#alias_ident) = #alias_ident {
510                  obj.set(#field_js_name, #alias_ident)?;
511                } else {
512                  obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
513                }
514              },
515            });
516          } else {
517            obj_field_setters.push(quote! { obj.set(#field_js_name, #alias_ident)?; });
518          }
519          if is_optional_field && !self.use_nullable {
520            obj_field_getters.push(quote! {
521              let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
522                err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
523                err
524              })?;
525            });
526          } else {
527            obj_field_getters.push(quote! {
528              let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
529                err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
530                err
531              })?.ok_or_else(|| napi::bindgen_prelude::Error::new(
532                napi::bindgen_prelude::Status::InvalidArg,
533                format!("Missing field `{}`", #field_js_name),
534              ))?;
535            });
536          }
537        }
538        syn::Member::Unnamed(i) => {
539          field_destructions.push(quote! { arg #i });
540          if is_optional_field {
541            obj_field_setters.push(match self.use_nullable {
542              false => quote! {
543                if arg #1.is_some() {
544                  obj.set(#field_js_name, arg #i)?;
545                }
546              },
547              true => quote! {
548                if let Some(arg #i) = arg #i {
549                  obj.set(#field_js_name, arg #i)?;
550                } else {
551                  obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
552                }
553              },
554            });
555          } else {
556            obj_field_setters.push(quote! { obj.set(#field_js_name, arg #1)?; });
557          }
558          if is_optional_field && !self.use_nullable {
559            obj_field_getters.push(quote! { let arg #i: #ty = obj.get(#field_js_name)?; });
560          } else {
561            obj_field_getters.push(quote! {
562              let arg #i: #ty = obj.get(#field_js_name)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
563                napi::bindgen_prelude::Status::InvalidArg,
564                format!("Missing field `{}`", #field_js_name),
565              ))?;
566            });
567          }
568        }
569      }
570    }
571
572    let destructed_fields = if self.is_tuple {
573      quote! {
574        Self (#(#field_destructions),*)
575      }
576    } else {
577      quote! {
578        Self {#(#field_destructions),*}
579      }
580    };
581
582    let to_napi_value = if self.object_to_js {
583      quote! {
584        impl napi::bindgen_prelude::ToNapiValue for #name {
585          unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
586            let env_wrapper = napi::bindgen_prelude::Env::from(env);
587            let mut obj = env_wrapper.create_object()?;
588
589            let #destructed_fields = val;
590            #(#obj_field_setters)*
591
592            napi::bindgen_prelude::Object::to_napi_value(env, obj)
593          }
594        }
595      }
596    } else {
597      quote! {}
598    };
599
600    let from_napi_value = if self.object_from_js {
601      quote! {
602        impl napi::bindgen_prelude::FromNapiValue for #name {
603          unsafe fn from_napi_value(
604            env: napi::bindgen_prelude::sys::napi_env,
605            napi_val: napi::bindgen_prelude::sys::napi_value
606          ) -> napi::bindgen_prelude::Result<Self> {
607            let env_wrapper = napi::bindgen_prelude::Env::from(env);
608            let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
609
610            #(#obj_field_getters)*
611
612            let val = #destructed_fields;
613
614            Ok(val)
615          }
616        }
617      }
618    } else {
619      quote! {}
620    };
621
622    quote! {
623      impl napi::bindgen_prelude::TypeName for #name {
624        fn type_name() -> &'static str {
625          #name_str
626        }
627
628        fn value_type() -> napi::ValueType {
629          napi::ValueType::Object
630        }
631      }
632
633      #to_napi_value
634
635      #from_napi_value
636
637      impl napi::bindgen_prelude::ValidateNapiValue for #name {}
638    }
639  }
640
641  fn gen_default_getters_setters(&self) -> Vec<(String, TokenStream)> {
642    let mut getters_setters = vec![];
643    let struct_name = &self.name;
644
645    for field in self.fields.iter() {
646      let field_ident = &field.name;
647      let field_name = match &field.name {
648        syn::Member::Named(ident) => ident.to_string(),
649        syn::Member::Unnamed(i) => format!("field{}", i.index),
650      };
651      let ty = &field.ty;
652
653      let getter_name = Ident::new(
654        &format!("get_{}", rm_raw_prefix(&field_name)),
655        Span::call_site(),
656      );
657      let setter_name = Ident::new(
658        &format!("set_{}", rm_raw_prefix(&field_name)),
659        Span::call_site(),
660      );
661
662      if field.getter {
663        let default_to_napi_value_convert = quote! {
664          let val = obj.#field_ident.to_owned();
665          unsafe { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
666        };
667        let to_napi_value_convert = if let syn::Type::Path(syn::TypePath {
668          path: syn::Path { segments, .. },
669          ..
670        }) = ty
671        {
672          if let Some(syn::PathSegment { ident, .. }) = segments.last() {
673            if TYPED_ARRAY_TYPE.iter().any(|name| ident == name) || ident == "Buffer" {
674              quote! {
675                let val = &mut obj.#field_ident;
676                unsafe { <&mut #ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
677              }
678            } else {
679              default_to_napi_value_convert
680            }
681          } else {
682            default_to_napi_value_convert
683          }
684        } else {
685          default_to_napi_value_convert
686        };
687        getters_setters.push((
688          field.js_name.clone(),
689          quote! {
690            extern "C" fn #getter_name(
691              env: napi::bindgen_prelude::sys::napi_env,
692              cb: napi::bindgen_prelude::sys::napi_callback_info
693            ) -> napi::bindgen_prelude::sys::napi_value {
694              napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0), false)
695                .and_then(|mut cb| unsafe { cb.unwrap_borrow_mut::<#struct_name>() })
696                .and_then(|obj| {
697                  #to_napi_value_convert
698                })
699                .unwrap_or_else(|e| {
700                  unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
701                  std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
702                })
703            }
704          },
705        ));
706      }
707
708      if field.setter {
709        getters_setters.push((
710          field.js_name.clone(),
711          quote! {
712            extern "C" fn #setter_name(
713              env: napi::bindgen_prelude::sys::napi_env,
714              cb: napi::bindgen_prelude::sys::napi_callback_info
715            ) -> napi::bindgen_prelude::sys::napi_value {
716              napi::bindgen_prelude::CallbackInfo::<1>::new(env, cb, Some(1), false)
717                .and_then(|mut cb_info| unsafe {
718                  cb_info.unwrap_borrow_mut::<#struct_name>()
719                    .and_then(|obj| {
720                      <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb_info.get_arg(0))
721                        .and_then(move |val| {
722                          obj.#field_ident = val;
723                          <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ())
724                        })
725                    })
726                })
727                .unwrap_or_else(|e| {
728                  unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
729                  std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
730                })
731            }
732          },
733        ));
734      }
735    }
736
737    getters_setters
738  }
739
740  fn gen_register(&self) -> TokenStream {
741    let name_str = self.name.to_string();
742    let struct_register_name = &self.register_name;
743    let js_name = format!("{}\0", self.js_name);
744    let mut props = vec![];
745
746    if self.kind == NapiStructKind::Constructor {
747      props.push(quote! { napi::bindgen_prelude::Property::new("constructor").unwrap().with_ctor(constructor) });
748    }
749
750    for field in self.fields.iter() {
751      let field_name = match &field.name {
752        syn::Member::Named(ident) => ident.to_string(),
753        syn::Member::Unnamed(i) => format!("field{}", i.index),
754      };
755
756      if !field.getter {
757        continue;
758      }
759
760      let js_name = &field.js_name;
761      let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
762      if field.writable {
763        attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
764      }
765      if field.enumerable {
766        attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
767      }
768      if field.configurable {
769        attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
770      }
771
772      let mut prop = quote! {
773        napi::bindgen_prelude::Property::new(#js_name)
774          .unwrap()
775          .with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
776      };
777
778      if field.getter {
779        let getter_name = Ident::new(
780          &format!("get_{}", rm_raw_prefix(&field_name)),
781          Span::call_site(),
782        );
783        (quote! { .with_getter(#getter_name) }).to_tokens(&mut prop);
784      }
785
786      if field.writable && field.setter {
787        let setter_name = Ident::new(
788          &format!("set_{}", rm_raw_prefix(&field_name)),
789          Span::call_site(),
790        );
791        (quote! { .with_setter(#setter_name) }).to_tokens(&mut prop);
792      }
793
794      props.push(prop);
795    }
796    let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
797    quote! {
798      #[allow(non_snake_case)]
799      #[allow(clippy::all)]
800      #[cfg(all(not(test), not(target_family = "wasm")))]
801      #[napi::bindgen_prelude::ctor]
802      fn #struct_register_name() {
803        napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
804      }
805
806      #[allow(non_snake_case)]
807      #[allow(clippy::all)]
808      #[cfg(all(not(test), target_family = "wasm"))]
809      #[no_mangle]
810      extern "C" fn #struct_register_name() {
811        napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
812      }
813    }
814  }
815
816  fn gen_instance_of_impl(&self, name: &Ident, js_name: &str) -> TokenStream {
817    quote! {
818      impl #name {
819        pub fn instance_of<V: napi::NapiRaw>(env: napi::Env, value: V) -> napi::Result<bool> {
820          if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name) {
821            let mut ctor = std::ptr::null_mut();
822            napi::check_status!(
823              unsafe { napi::sys::napi_get_reference_value(env.raw(), ctor_ref, &mut ctor) },
824              "Failed to get constructor reference of class `{}`",
825              #js_name
826            )?;
827            let mut is_instance_of = false;
828            napi::check_status!(
829              unsafe { napi::sys::napi_instanceof(env.raw(), value.raw(), ctor, &mut is_instance_of) },
830              "Failed to run instanceof for class `{}`",
831              #js_name
832            )?;
833            Ok(is_instance_of)
834          } else {
835            Err(napi::Error::new(napi::Status::GenericFailure, format!("Failed to get constructor of class `{}`", #js_name)))
836          }
837        }
838      }
839    }
840  }
841}
842
843impl TryToTokens for NapiImpl {
844  fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
845    self.gen_helper_mod()?.to_tokens(tokens);
846
847    Ok(())
848  }
849}
850
851impl NapiImpl {
852  fn gen_helper_mod(&self) -> BindgenResult<TokenStream> {
853    let name_str = self.name.to_string();
854    let js_name = format!("{}\0", self.js_name);
855    let mod_name = Ident::new(
856      &format!(
857        "__napi_impl_helper__{}__{}",
858        name_str,
859        NAPI_IMPL_ID.fetch_add(1, Ordering::SeqCst)
860      ),
861      Span::call_site(),
862    );
863
864    let register_name = &self.register_name;
865
866    let mut methods = vec![];
867    let mut props = HashMap::new();
868
869    for item in self.items.iter() {
870      let js_name = Literal::string(&item.js_name);
871      let item_str = item.name.to_string();
872      let intermediate_name = get_intermediate_ident(&item_str);
873      methods.push(item.try_to_token_stream()?);
874
875      let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
876      if item.writable {
877        attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
878      }
879      if item.enumerable {
880        attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
881      }
882      if item.configurable {
883        attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
884      }
885
886      let prop = props.entry(&item.js_name).or_insert_with(|| {
887        quote! {
888          napi::bindgen_prelude::Property::new(#js_name).unwrap().with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
889        }
890      });
891
892      let appendix = match item.kind {
893        FnKind::Constructor => quote! { .with_ctor(#intermediate_name) },
894        FnKind::Getter => quote! { .with_getter(#intermediate_name) },
895        FnKind::Setter => quote! { .with_setter(#intermediate_name) },
896        _ => {
897          if item.fn_self.is_some() {
898            quote! { .with_method(#intermediate_name) }
899          } else {
900            quote! { .with_method(#intermediate_name).with_property_attributes(napi::bindgen_prelude::PropertyAttributes::Static) }
901          }
902        }
903      };
904
905      appendix.to_tokens(prop);
906    }
907
908    let mut props: Vec<_> = props.into_iter().collect();
909    props.sort_by_key(|(_, prop)| prop.to_string());
910    let props = props.into_iter().map(|(_, prop)| prop);
911    let props_wasm = props.clone();
912    let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
913    Ok(quote! {
914      #[allow(non_snake_case)]
915      #[allow(clippy::all)]
916      mod #mod_name {
917        use super::*;
918        #(#methods)*
919
920        #[cfg(all(not(test), not(target_family = "wasm")))]
921        #[napi::bindgen_prelude::ctor]
922        fn #register_name() {
923          napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
924        }
925
926        #[cfg(all(not(test), target_family = "wasm"))]
927        #[no_mangle]
928        extern "C" fn #register_name() {
929          napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props_wasm),*]);
930        }
931      }
932    })
933  }
934}
935
936pub fn rm_raw_prefix(s: &str) -> &str {
937  if let Some(stripped) = s.strip_prefix("r#") {
938    stripped
939  } else {
940    s
941  }
942}