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
27fn 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 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}