1 | use std::collections::HashMap; |
2 | use std::sync::atomic::{AtomicU32, Ordering}; |
3 | |
4 | use proc_macro2::{Ident, Literal, Span, TokenStream}; |
5 | use quote::ToTokens; |
6 | |
7 | use crate::{ |
8 | codegen::{get_intermediate_ident, js_mod_to_token_stream}, |
9 | BindgenResult, FnKind, NapiImpl, NapiStruct, NapiStructKind, TryToTokens, |
10 | }; |
11 | |
12 | static NAPI_IMPL_ID: AtomicU32 = AtomicU32::new(0); |
13 | const 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. |
28 | fn 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 | |
157 | impl 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 | |
177 | impl 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 | |
829 | impl 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 | |
837 | impl 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 | |
922 | fn 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 | |