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: TokenStream = self.gen_napi_value_map_impl();
160
161 let class_helper_mod: TokenStream = 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 wrapped_value = Box::into_raw(Box::new(val));
285 let instance_value = #name::new_instance(env, wrapped_value.cast(), ctor_ref)?;
286 #iterator_implementation
287 Ok(instance_value)
288 } else {
289 Err(napi::bindgen_prelude::Error::new(
290 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}` in `ToNapiValue`", #js_name_raw))
291 )
292 }
293 }
294 }
295
296 #finalize_trait
297 #instance_of_impl
298 impl #name {
299 pub fn into_reference(val: #name, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<#name>> {
300 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
301 unsafe {
302 let wrapped_value = Box::into_raw(Box::new(val));
303 let instance_value = #name::new_instance(env.raw(), wrapped_value.cast(), ctor_ref)?;
304 {
305 let env = env.raw();
306 #iterator_implementation
307 }
308 napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value.cast(), env.raw())
309 }
310 } else {
311 Err(napi::bindgen_prelude::Error::new(
312 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
313 )
314 }
315 }
316
317 pub fn into_instance(self, env: napi::Env) -> napi::Result<napi::bindgen_prelude::ClassInstance<#name>> {
318 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
319 unsafe {
320 let wrapped_value = Box::leak(Box::new(self));
321 let instance_value = #name::new_instance(env.raw(), wrapped_value as *mut _ as *mut std::ffi::c_void, ctor_ref)?;
322
323 Ok(napi::bindgen_prelude::ClassInstance::<#name>::new(instance_value, wrapped_value))
324 }
325 } else {
326 Err(napi::bindgen_prelude::Error::new(
327 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
328 )
329 }
330 }
331
332 unsafe fn new_instance(
333 env: napi::sys::napi_env,
334 wrapped_value: *mut std::ffi::c_void,
335 ctor_ref: napi::sys::napi_ref,
336 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
337 let mut ctor = std::ptr::null_mut();
338 napi::check_status!(
339 napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
340 "Failed to get constructor reference of class `{}`",
341 #js_name_raw
342 )?;
343
344 let mut result = std::ptr::null_mut();
345 napi::__private::___CALL_FROM_FACTORY.with(|inner| inner.store(true, std::sync::atomic::Ordering::Relaxed));
346 napi::check_status!(
347 napi::sys::napi_new_instance(env, ctor, 0, std::ptr::null_mut(), &mut result),
348 "Failed to construct class `{}`",
349 #js_name_raw
350 )?;
351 napi::__private::___CALL_FROM_FACTORY.with(|inner| inner.store(false, std::sync::atomic::Ordering::Relaxed));
352 let mut object_ref = std::ptr::null_mut();
353 let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
354 let finalize_callbacks_ptr = std::rc::Rc::into_raw(std::rc::Rc::new(std::cell::Cell::new(Box::into_raw(initial_finalize))));
355 napi::check_status!(
356 napi::sys::napi_wrap(
357 env,
358 result,
359 wrapped_value,
360 Some(napi::bindgen_prelude::raw_finalize_unchecked::<#name>),
361 std::ptr::null_mut(),
362 &mut object_ref,
363 ),
364 "Failed to wrap native object of class `{}`",
365 #js_name_raw
366 )?;
367 napi::bindgen_prelude::Reference::<#name>::add_ref(env, wrapped_value, (wrapped_value, object_ref, finalize_callbacks_ptr));
368 Ok(result)
369 }
370 }
371 }
372 }
373
374 fn gen_iterator_property(&self, name: &Ident) -> TokenStream {
375 if !self.implement_iterator {
376 return quote! {};
377 }
378 quote! {
379 napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value);
380 }
381 }
382
383 fn gen_to_napi_value_ctor_impl(&self) -> TokenStream {
384 let name = &self.name;
385 let js_name_without_null = &self.js_name;
386 let js_name_str = format!("{}\0", &self.js_name);
387 let instance_of_impl = self.gen_instance_of_impl(name, &js_name_str);
388
389 let mut field_conversions = vec![];
390 let mut field_destructions = vec![];
391
392 for field in self.fields.iter() {
393 let ty = &field.ty;
394
395 match &field.name {
396 syn::Member::Named(ident) => {
397 // alias here prevents field name shadowing
398 let alias_ident = format_ident!("{}_", ident);
399 field_destructions.push(quote! { #ident: #alias_ident });
400 field_conversions.push(
401 quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #alias_ident)? },
402 );
403 }
404 syn::Member::Unnamed(i) => {
405 field_destructions.push(quote! { arg #i });
406 field_conversions.push(
407 quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, arg #i)? },
408 );
409 }
410 }
411 }
412
413 let destructed_fields = if self.is_tuple {
414 quote! {
415 Self (#(#field_destructions),*)
416 }
417 } else {
418 quote! {
419 Self {#(#field_destructions),*}
420 }
421 };
422
423 let finalize_trait = if self.use_custom_finalize {
424 quote! {}
425 } else {
426 quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} }
427 };
428
429 quote! {
430 impl napi::bindgen_prelude::ToNapiValue for #name {
431 unsafe fn to_napi_value(
432 env: napi::bindgen_prelude::sys::napi_env,
433 val: #name,
434 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
435 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
436 let mut ctor = std::ptr::null_mut();
437
438 napi::bindgen_prelude::check_status!(
439 napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
440 "Failed to get constructor reference of class `{}`",
441 #js_name_without_null
442 )?;
443
444 let mut instance_value = std::ptr::null_mut();
445 let #destructed_fields = val;
446 let args = vec![#(#field_conversions),*];
447
448 napi::bindgen_prelude::check_status!(
449 napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut instance_value),
450 "Failed to construct class `{}`",
451 #js_name_without_null
452 )?;
453
454 Ok(instance_value)
455 } else {
456 Err(napi::bindgen_prelude::Error::new(
457 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
458 )
459 }
460 }
461 }
462 #instance_of_impl
463 #finalize_trait
464 }
465 }
466
467 fn gen_to_napi_value_obj_impl(&self) -> TokenStream {
468 let name = &self.name;
469 let name_str = self.name.to_string();
470
471 let mut obj_field_setters = vec![];
472 let mut obj_field_getters = vec![];
473 let mut field_destructions = vec![];
474
475 for field in self.fields.iter() {
476 let field_js_name = &field.js_name;
477 let ty = &field.ty;
478 let is_optional_field = if let syn::Type::Path(syn::TypePath {
479 path: syn::Path { segments, .. },
480 ..
481 }) = &ty
482 {
483 if let Some(last_path) = segments.last() {
484 last_path.ident == "Option"
485 } else {
486 false
487 }
488 } else {
489 false
490 };
491 match &field.name {
492 syn::Member::Named(ident) => {
493 let alias_ident = format_ident!("{}_", ident);
494 field_destructions.push(quote! { #ident: #alias_ident });
495 if is_optional_field {
496 obj_field_setters.push(match self.use_nullable {
497 false => quote! {
498 if #alias_ident.is_some() {
499 obj.set(#field_js_name, #alias_ident)?;
500 }
501 },
502 true => quote! {
503 if let Some(#alias_ident) = #alias_ident {
504 obj.set(#field_js_name, #alias_ident)?;
505 } else {
506 obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
507 }
508 },
509 });
510 } else {
511 obj_field_setters.push(quote! { obj.set(#field_js_name, #alias_ident)?; });
512 }
513 if is_optional_field && !self.use_nullable {
514 obj_field_getters.push(quote! { let #alias_ident: #ty = obj.get(#field_js_name)?; });
515 } else {
516 obj_field_getters.push(quote! {
517 let #alias_ident: #ty = obj.get(#field_js_name)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
518 napi::bindgen_prelude::Status::InvalidArg,
519 format!("Missing field `{}`", #field_js_name),
520 ))?;
521 });
522 }
523 }
524 syn::Member::Unnamed(i) => {
525 field_destructions.push(quote! { arg #i });
526 if is_optional_field {
527 obj_field_setters.push(match self.use_nullable {
528 false => quote! {
529 if arg #1.is_some() {
530 obj.set(#field_js_name, arg #i)?;
531 }
532 },
533 true => quote! {
534 if let Some(arg #i) = arg #i {
535 obj.set(#field_js_name, arg #i)?;
536 } else {
537 obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
538 }
539 },
540 });
541 } else {
542 obj_field_setters.push(quote! { obj.set(#field_js_name, arg #1)?; });
543 }
544 if is_optional_field && !self.use_nullable {
545 obj_field_getters.push(quote! { let arg #i: #ty = obj.get(#field_js_name)?; });
546 } else {
547 obj_field_getters.push(quote! {
548 let arg #i: #ty = obj.get(#field_js_name)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
549 napi::bindgen_prelude::Status::InvalidArg,
550 format!("Missing field `{}`", #field_js_name),
551 ))?;
552 });
553 }
554 }
555 }
556 }
557
558 let destructed_fields = if self.is_tuple {
559 quote! {
560 Self (#(#field_destructions),*)
561 }
562 } else {
563 quote! {
564 Self {#(#field_destructions),*}
565 }
566 };
567
568 let to_napi_value = if self.object_to_js {
569 quote! {
570 impl napi::bindgen_prelude::ToNapiValue for #name {
571 unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
572 let env_wrapper = napi::bindgen_prelude::Env::from(env);
573 let mut obj = env_wrapper.create_object()?;
574
575 let #destructed_fields = val;
576 #(#obj_field_setters)*
577
578 napi::bindgen_prelude::Object::to_napi_value(env, obj)
579 }
580 }
581 }
582 } else {
583 quote! {}
584 };
585
586 let from_napi_value = if self.object_from_js {
587 quote! {
588 impl napi::bindgen_prelude::FromNapiValue for #name {
589 unsafe fn from_napi_value(
590 env: napi::bindgen_prelude::sys::napi_env,
591 napi_val: napi::bindgen_prelude::sys::napi_value
592 ) -> napi::bindgen_prelude::Result<Self> {
593 let env_wrapper = napi::bindgen_prelude::Env::from(env);
594 let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
595
596 #(#obj_field_getters)*
597
598 let val = #destructed_fields;
599
600 Ok(val)
601 }
602 }
603 }
604 } else {
605 quote! {}
606 };
607
608 quote! {
609 impl napi::bindgen_prelude::TypeName for #name {
610 fn type_name() -> &'static str {
611 #name_str
612 }
613
614 fn value_type() -> napi::ValueType {
615 napi::ValueType::Object
616 }
617 }
618
619 #to_napi_value
620
621 #from_napi_value
622
623 impl napi::bindgen_prelude::ValidateNapiValue for #name {}
624 }
625 }
626
627 fn gen_default_getters_setters(&self) -> Vec<(String, TokenStream)> {
628 let mut getters_setters = vec![];
629 let struct_name = &self.name;
630
631 for field in self.fields.iter() {
632 let field_ident = &field.name;
633 let field_name = match &field.name {
634 syn::Member::Named(ident) => ident.to_string(),
635 syn::Member::Unnamed(i) => format!("field{}", i.index),
636 };
637 let ty = &field.ty;
638
639 let getter_name = Ident::new(
640 &format!("get_{}", rm_raw_prefix(&field_name)),
641 Span::call_site(),
642 );
643 let setter_name = Ident::new(
644 &format!("set_{}", rm_raw_prefix(&field_name)),
645 Span::call_site(),
646 );
647
648 if field.getter {
649 let default_to_napi_value_convert = quote! {
650 let val = obj.#field_ident.to_owned();
651 unsafe { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
652 };
653 let to_napi_value_convert = if let syn::Type::Path(syn::TypePath {
654 path: syn::Path { segments, .. },
655 ..
656 }) = ty
657 {
658 if let Some(syn::PathSegment { ident, .. }) = segments.last() {
659 if TYPED_ARRAY_TYPE.iter().any(|name| ident == name) || ident == "Buffer" {
660 quote! {
661 let val = &mut obj.#field_ident;
662 unsafe { <&mut #ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
663 }
664 } else {
665 default_to_napi_value_convert
666 }
667 } else {
668 default_to_napi_value_convert
669 }
670 } else {
671 default_to_napi_value_convert
672 };
673 getters_setters.push((
674 field.js_name.clone(),
675 quote! {
676 extern "C" fn #getter_name(
677 env: napi::bindgen_prelude::sys::napi_env,
678 cb: napi::bindgen_prelude::sys::napi_callback_info
679 ) -> napi::bindgen_prelude::sys::napi_value {
680 napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0), false)
681 .and_then(|mut cb| unsafe { cb.unwrap_borrow_mut::<#struct_name>() })
682 .and_then(|obj| {
683 #to_napi_value_convert
684 })
685 .unwrap_or_else(|e| {
686 unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
687 std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
688 })
689 }
690 },
691 ));
692 }
693
694 if field.setter {
695 getters_setters.push((
696 field.js_name.clone(),
697 quote! {
698 extern "C" fn #setter_name(
699 env: napi::bindgen_prelude::sys::napi_env,
700 cb: napi::bindgen_prelude::sys::napi_callback_info
701 ) -> napi::bindgen_prelude::sys::napi_value {
702 napi::bindgen_prelude::CallbackInfo::<1>::new(env, cb, Some(1), false)
703 .and_then(|mut cb_info| unsafe {
704 cb_info.unwrap_borrow_mut::<#struct_name>()
705 .and_then(|obj| {
706 <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb_info.get_arg(0))
707 .and_then(move |val| {
708 obj.#field_ident = val;
709 <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ())
710 })
711 })
712 })
713 .unwrap_or_else(|e| {
714 unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
715 std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
716 })
717 }
718 },
719 ));
720 }
721 }
722
723 getters_setters
724 }
725
726 fn gen_register(&self) -> TokenStream {
727 let name_str = self.name.to_string();
728 let struct_register_name = &self.register_name;
729 let js_name = format!("{}\0", self.js_name);
730 let mut props = vec![];
731
732 if self.kind == NapiStructKind::Constructor {
733 props.push(quote! { napi::bindgen_prelude::Property::new("constructor").unwrap().with_ctor(constructor) });
734 }
735
736 for field in self.fields.iter() {
737 let field_name = match &field.name {
738 syn::Member::Named(ident) => ident.to_string(),
739 syn::Member::Unnamed(i) => format!("field{}", i.index),
740 };
741
742 if !field.getter {
743 continue;
744 }
745
746 let js_name = &field.js_name;
747 let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
748 if field.writable {
749 attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
750 }
751 if field.enumerable {
752 attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
753 }
754 if field.configurable {
755 attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
756 }
757
758 let mut prop = quote! {
759 napi::bindgen_prelude::Property::new(#js_name)
760 .unwrap()
761 .with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
762 };
763
764 if field.getter {
765 let getter_name = Ident::new(
766 &format!("get_{}", rm_raw_prefix(&field_name)),
767 Span::call_site(),
768 );
769 (quote! { .with_getter(#getter_name) }).to_tokens(&mut prop);
770 }
771
772 if field.writable && field.setter {
773 let setter_name = Ident::new(
774 &format!("set_{}", rm_raw_prefix(&field_name)),
775 Span::call_site(),
776 );
777 (quote! { .with_setter(#setter_name) }).to_tokens(&mut prop);
778 }
779
780 props.push(prop);
781 }
782 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
783 quote! {
784 #[allow(non_snake_case)]
785 #[allow(clippy::all)]
786 #[cfg(all(not(test), not(feature = "noop"), not(target_family = "wasm")))]
787 #[napi::bindgen_prelude::ctor]
788 fn #struct_register_name() {
789 napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
790 }
791
792 #[allow(non_snake_case)]
793 #[allow(clippy::all)]
794 #[cfg(all(not(test), not(feature = "noop"), target_family = "wasm"))]
795 #[no_mangle]
796 extern "C" fn #struct_register_name() {
797 napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
798 }
799 }
800 }
801
802 fn gen_instance_of_impl(&self, name: &Ident, js_name: &str) -> TokenStream {
803 quote! {
804 impl #name {
805 pub fn instance_of<V: napi::NapiRaw>(env: napi::Env, value: V) -> napi::Result<bool> {
806 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name) {
807 let mut ctor = std::ptr::null_mut();
808 napi::check_status!(
809 unsafe { napi::sys::napi_get_reference_value(env.raw(), ctor_ref, &mut ctor) },
810 "Failed to get constructor reference of class `{}`",
811 #js_name
812 )?;
813 let mut is_instance_of = false;
814 napi::check_status!(
815 unsafe { napi::sys::napi_instanceof(env.raw(), value.raw(), ctor, &mut is_instance_of) },
816 "Failed to run instanceof for class `{}`",
817 #js_name
818 )?;
819 Ok(is_instance_of)
820 } else {
821 Err(napi::Error::new(napi::Status::GenericFailure, format!("Failed to get constructor of class `{}`", #js_name)))
822 }
823 }
824 }
825 }
826 }
827}
828
829impl TryToTokens for NapiImpl {
830 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
831 self.gen_helper_mod()?.to_tokens(tokens);
832
833 Ok(())
834 }
835}
836
837impl NapiImpl {
838 fn gen_helper_mod(&self) -> BindgenResult<TokenStream> {
839 let name_str = self.name.to_string();
840 let js_name = format!("{}\0", self.js_name);
841 let mod_name = Ident::new(
842 &format!(
843 "__napi_impl_helper__{}__{}",
844 name_str,
845 NAPI_IMPL_ID.fetch_add(1, Ordering::SeqCst)
846 ),
847 Span::call_site(),
848 );
849
850 let register_name = &self.register_name;
851
852 let mut methods = vec![];
853 let mut props = HashMap::new();
854
855 for item in self.items.iter() {
856 let js_name = Literal::string(&item.js_name);
857 let item_str = item.name.to_string();
858 let intermediate_name = get_intermediate_ident(&item_str);
859 methods.push(item.try_to_token_stream()?);
860
861 let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
862 if item.writable {
863 attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
864 }
865 if item.enumerable {
866 attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
867 }
868 if item.configurable {
869 attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
870 }
871
872 let prop = props.entry(&item.js_name).or_insert_with(|| {
873 quote! {
874 napi::bindgen_prelude::Property::new(#js_name).unwrap().with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
875 }
876 });
877
878 let appendix = match item.kind {
879 FnKind::Constructor => quote! { .with_ctor(#intermediate_name) },
880 FnKind::Getter => quote! { .with_getter(#intermediate_name) },
881 FnKind::Setter => quote! { .with_setter(#intermediate_name) },
882 _ => {
883 if item.fn_self.is_some() {
884 quote! { .with_method(#intermediate_name) }
885 } else {
886 quote! { .with_method(#intermediate_name).with_property_attributes(napi::bindgen_prelude::PropertyAttributes::Static) }
887 }
888 }
889 };
890
891 appendix.to_tokens(prop);
892 }
893
894 let mut props: Vec<_> = props.into_iter().collect();
895 props.sort_by_key(|(_, prop)| prop.to_string());
896 let props = props.into_iter().map(|(_, prop)| prop);
897 let props_wasm = props.clone();
898 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
899 Ok(quote! {
900 #[allow(non_snake_case)]
901 #[allow(clippy::all)]
902 mod #mod_name {
903 use super::*;
904 #(#methods)*
905
906 #[cfg(all(not(test), not(feature = "noop"), not(target_family = "wasm")))]
907 #[napi::bindgen_prelude::ctor]
908 fn #register_name() {
909 napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
910 }
911
912 #[cfg(all(not(test), not(feature = "noop"), target_family = "wasm"))]
913 #[no_mangle]
914 extern "C" fn #register_name() {
915 napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props_wasm),*]);
916 }
917 }
918 })
919 }
920}
921
922fn rm_raw_prefix(s: &str) -> &str {
923 if let Some(stripped: &str) = s.strip_prefix("r#") {
924 stripped
925 } else {
926 s
927 }
928}
929