1 | use proc_macro2::{Ident, Span, TokenStream}; |
2 | use quote::ToTokens; |
3 | use syn::spanned::Spanned; |
4 | |
5 | use crate::{ |
6 | codegen::{get_intermediate_ident, js_mod_to_token_stream}, |
7 | BindgenResult, CallbackArg, Diagnostic, FnKind, FnSelf, NapiFn, NapiFnArgKind, TryToTokens, |
8 | TYPEDARRAY_SLICE_TYPES, |
9 | }; |
10 | |
11 | impl TryToTokens for NapiFn { |
12 | fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> { |
13 | let name_str = self.name.to_string(); |
14 | let intermediate_ident = get_intermediate_ident(&name_str); |
15 | let args_len = self.args.len(); |
16 | |
17 | let ArgConversions { |
18 | arg_conversions, |
19 | args: arg_names, |
20 | refs, |
21 | mut_ref_spans, |
22 | unsafe_, |
23 | } = self.gen_arg_conversions()?; |
24 | // The JS engine can't properly track mutability in an async context, so refuse to compile |
25 | // code that tries to use async and mutability together without `unsafe` mark. |
26 | if self.is_async && !mut_ref_spans.is_empty() && !unsafe_ { |
27 | return Diagnostic::from_vec( |
28 | mut_ref_spans |
29 | .into_iter() |
30 | .map(|s| Diagnostic::span_error(s, "mutable reference is unsafe with async" )) |
31 | .collect(), |
32 | ); |
33 | } |
34 | if Some(FnSelf::MutRef) == self.fn_self && self.is_async && !self.unsafe_ { |
35 | return Err(Diagnostic::span_error( |
36 | self.name.span(), |
37 | "&mut self in async napi methods should be marked as unsafe" , |
38 | )); |
39 | } |
40 | let arg_ref_count = refs.len(); |
41 | let receiver = self.gen_fn_receiver(); |
42 | let receiver_ret_name = Ident::new("_ret" , Span::call_site()); |
43 | let ret = self.gen_fn_return(&receiver_ret_name); |
44 | let register = self.gen_fn_register(); |
45 | let attrs = &self.attrs; |
46 | |
47 | let build_ref_container = if self.is_async { |
48 | quote! { |
49 | struct NapiRefContainer([napi::sys::napi_ref; #arg_ref_count]); |
50 | impl NapiRefContainer { |
51 | fn drop(self, env: napi::sys::napi_env) { |
52 | for r in self.0.into_iter() { |
53 | assert_eq!( |
54 | unsafe { napi::sys::napi_reference_unref(env, r, &mut 0) }, |
55 | napi::sys::Status::napi_ok, |
56 | "failed to delete napi ref" |
57 | ); |
58 | assert_eq!( |
59 | unsafe { napi::sys::napi_delete_reference(env, r) }, |
60 | napi::sys::Status::napi_ok, |
61 | "failed to delete napi ref" |
62 | ); |
63 | } |
64 | } |
65 | } |
66 | unsafe impl Send for NapiRefContainer {} |
67 | unsafe impl Sync for NapiRefContainer {} |
68 | let _make_ref = |a: ::std::ptr::NonNull<napi::bindgen_prelude::sys::napi_value__>| { |
69 | let mut node_ref = ::std::mem::MaybeUninit::uninit(); |
70 | napi::bindgen_prelude::check_status!(unsafe { |
71 | napi::bindgen_prelude::sys::napi_create_reference(env, a.as_ptr(), 1, node_ref.as_mut_ptr()) |
72 | }, |
73 | "failed to create napi ref" |
74 | )?; |
75 | Ok::<napi::sys::napi_ref, napi::Error>(unsafe { node_ref.assume_init() }) |
76 | }; |
77 | let mut _args_array = [::std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_ref__>(); #arg_ref_count]; |
78 | let mut _arg_write_index = 0; |
79 | |
80 | #(#refs)* |
81 | |
82 | #[cfg(debug_assert)] |
83 | { |
84 | for a in &_args_array { |
85 | assert!(!a.is_null(), "failed to initialize napi ref" ); |
86 | } |
87 | } |
88 | let _args_ref = NapiRefContainer(_args_array); |
89 | } |
90 | } else { |
91 | quote! {} |
92 | }; |
93 | let native_call = if !self.is_async { |
94 | quote! { |
95 | napi::bindgen_prelude::within_runtime_if_available(move || { |
96 | let #receiver_ret_name = { |
97 | #receiver(#(#arg_names),*) |
98 | }; |
99 | #ret |
100 | }) |
101 | } |
102 | } else { |
103 | let call = if self.is_ret_result { |
104 | quote! { #receiver(#(#arg_names),*).await } |
105 | } else { |
106 | quote! { Ok(#receiver(#(#arg_names),*).await) } |
107 | }; |
108 | quote! { |
109 | napi::bindgen_prelude::execute_tokio_future(env, async move { #call }, move |env, #receiver_ret_name| { |
110 | _args_ref.drop(env); |
111 | #ret |
112 | }) |
113 | } |
114 | }; |
115 | |
116 | // async factory only |
117 | let use_after_async = if self.is_async && self.parent.is_some() && self.fn_self.is_none() { |
118 | quote! { true } |
119 | } else { |
120 | quote! { false } |
121 | }; |
122 | |
123 | let function_call_inner = quote! { |
124 | napi::bindgen_prelude::CallbackInfo::<#args_len>::new(env, cb, None, #use_after_async).and_then(|mut cb| { |
125 | #build_ref_container |
126 | #(#arg_conversions)* |
127 | #native_call |
128 | }) |
129 | }; |
130 | |
131 | let function_call = if args_len == 0 |
132 | && self.fn_self.is_none() |
133 | && self.kind != FnKind::Constructor |
134 | && self.kind != FnKind::Factory |
135 | && !self.is_async |
136 | { |
137 | quote! { #native_call } |
138 | } else if self.kind == FnKind::Constructor { |
139 | let return_from_factory = if self.catch_unwind { |
140 | quote! { return Ok(std::ptr::null_mut()); } |
141 | } else { |
142 | quote! { return std::ptr::null_mut(); } |
143 | }; |
144 | quote! { |
145 | // constructor function is called from class `factory` |
146 | // so we should skip the original `constructor` logic |
147 | if napi::__private::___CALL_FROM_FACTORY.with(|inner| inner.load(std::sync::atomic::Ordering::Relaxed)) { |
148 | #return_from_factory |
149 | } |
150 | #function_call_inner |
151 | } |
152 | } else { |
153 | function_call_inner |
154 | }; |
155 | |
156 | let function_call = if self.catch_unwind { |
157 | quote! { |
158 | { |
159 | std::panic::catch_unwind(|| { #function_call }) |
160 | .map_err(|e| { |
161 | let message = { |
162 | if let Some(string) = e.downcast_ref::<String>() { |
163 | string.clone() |
164 | } else if let Some(string) = e.downcast_ref::<&str>() { |
165 | string.to_string() |
166 | } else { |
167 | format!("panic from Rust code: {:?}" , e) |
168 | } |
169 | }; |
170 | napi::Error::new(napi::Status::GenericFailure, message) |
171 | }) |
172 | .and_then(|r| r) |
173 | } |
174 | } |
175 | } else { |
176 | quote! { |
177 | #function_call |
178 | } |
179 | }; |
180 | |
181 | (quote! { |
182 | #(#attrs)* |
183 | #[doc(hidden)] |
184 | #[allow(non_snake_case)] |
185 | #[allow(clippy::all)] |
186 | extern "C" fn #intermediate_ident( |
187 | env: napi::bindgen_prelude::sys::napi_env, |
188 | cb: napi::bindgen_prelude::sys::napi_callback_info |
189 | ) -> napi::bindgen_prelude::sys::napi_value { |
190 | unsafe { |
191 | #function_call.unwrap_or_else(|e| { |
192 | napi::bindgen_prelude::JsError::from(e).throw_into(env); |
193 | std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>() |
194 | }) |
195 | } |
196 | } |
197 | |
198 | #register |
199 | }) |
200 | .to_tokens(tokens); |
201 | |
202 | Ok(()) |
203 | } |
204 | } |
205 | |
206 | impl NapiFn { |
207 | fn gen_arg_conversions(&self) -> BindgenResult<ArgConversions> { |
208 | let mut arg_conversions = vec![]; |
209 | let mut args = vec![]; |
210 | let mut refs = vec![]; |
211 | let mut mut_ref_spans = vec![]; |
212 | let make_ref = |input| { |
213 | quote! { |
214 | _args_array[_arg_write_index] = _make_ref( |
215 | ::std::ptr::NonNull::new(#input) |
216 | .ok_or_else(|| napi::Error::new(napi::Status::InvalidArg, "referenced ptr is null" .to_owned()))? |
217 | )?; |
218 | _arg_write_index += 1; |
219 | } |
220 | }; |
221 | |
222 | // fetch this |
223 | if let Some(parent) = &self.parent { |
224 | match self.fn_self { |
225 | Some(FnSelf::Ref) => { |
226 | refs.push(make_ref(quote! { cb.this })); |
227 | arg_conversions.push(quote! { |
228 | let this_ptr = unsafe { cb.unwrap_raw::<#parent>()? }; |
229 | let this: &#parent = Box::leak(Box::from_raw(this_ptr)); |
230 | }); |
231 | } |
232 | Some(FnSelf::MutRef) => { |
233 | refs.push(make_ref(quote! { cb.this })); |
234 | arg_conversions.push(quote! { |
235 | let this_ptr = unsafe { cb.unwrap_raw::<#parent>()? }; |
236 | let this: &mut #parent = Box::leak(Box::from_raw(this_ptr)); |
237 | }); |
238 | } |
239 | _ => {} |
240 | }; |
241 | } |
242 | |
243 | let mut skipped_arg_count = 0; |
244 | for (i, arg) in self.args.iter().enumerate() { |
245 | let i = i - skipped_arg_count; |
246 | let ident = Ident::new(&format!("arg {}" , i), Span::call_site()); |
247 | |
248 | match &arg.kind { |
249 | NapiFnArgKind::PatType(path) => { |
250 | if &path.ty.to_token_stream().to_string() == "Env" { |
251 | args.push(quote! { napi::bindgen_prelude::Env::from(env) }); |
252 | skipped_arg_count += 1; |
253 | } else { |
254 | let is_in_class = self.parent.is_some(); |
255 | if let syn::Type::Path(path) = path.ty.as_ref() { |
256 | if let Some(p) = path.path.segments.last() { |
257 | if p.ident == "Reference" { |
258 | if !is_in_class { |
259 | bail_span!(p, "`Reference` is only allowed in class methods" ); |
260 | } |
261 | if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { |
262 | args: angle_bracketed_args, |
263 | .. |
264 | }) = &p.arguments |
265 | { |
266 | if let Some(syn::GenericArgument::Type(syn::Type::Path(path))) = |
267 | angle_bracketed_args.first() |
268 | { |
269 | if let Some(p) = path.path.segments.first() { |
270 | if p.ident == *self.parent.as_ref().unwrap() { |
271 | args.push(quote! { |
272 | napi::bindgen_prelude::Reference::from_value_ptr(this_ptr.cast(), env)? |
273 | }); |
274 | skipped_arg_count += 1; |
275 | continue; |
276 | } |
277 | } |
278 | } |
279 | } |
280 | } else if p.ident == "This" { |
281 | if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { |
282 | args: angle_bracketed_args, |
283 | .. |
284 | }) = &p.arguments |
285 | { |
286 | if let Some(syn::GenericArgument::Type(generic_type)) = |
287 | angle_bracketed_args.first() |
288 | { |
289 | if let syn::Type::Path(syn::TypePath { |
290 | path: syn::Path { segments, .. }, |
291 | .. |
292 | }) = generic_type |
293 | { |
294 | if let Some(syn::PathSegment { ident, .. }) = segments.first() { |
295 | if let Some((primitive_type, _)) = |
296 | crate::PRIMITIVE_TYPES.iter().find(|(p, _)| ident == *p) |
297 | { |
298 | bail_span!( |
299 | ident, |
300 | "This type must not be {} \nthis in JavaScript function must be `Object` type or `undefined`" , |
301 | primitive_type |
302 | ); |
303 | } |
304 | args.push( |
305 | quote! { |
306 | { |
307 | <#ident as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.this)? |
308 | } |
309 | }, |
310 | ); |
311 | skipped_arg_count += 1; |
312 | continue; |
313 | } |
314 | } else if let syn::Type::Reference(syn::TypeReference { |
315 | elem, |
316 | mutability, |
317 | .. |
318 | }) = generic_type |
319 | { |
320 | if let syn::Type::Path(syn::TypePath { |
321 | path: syn::Path { segments, .. }, |
322 | .. |
323 | }) = elem.as_ref() |
324 | { |
325 | if let Some(syn::PathSegment { ident, .. }) = segments.first() { |
326 | refs.push(make_ref(quote! { cb.this })); |
327 | let token = if mutability.is_some() { |
328 | mut_ref_spans.push(generic_type.span()); |
329 | quote! { <#ident as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.this)? } |
330 | } else { |
331 | quote! { <#ident as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.this)? } |
332 | }; |
333 | args.push(token); |
334 | skipped_arg_count += 1; |
335 | continue; |
336 | } |
337 | } |
338 | } |
339 | } |
340 | } |
341 | refs.push(make_ref(quote! { cb.this })); |
342 | args.push(quote! { <napi::bindgen_prelude::This as napi::NapiValue>::from_raw_unchecked(env, cb.this) }); |
343 | skipped_arg_count += 1; |
344 | continue; |
345 | } |
346 | } |
347 | } |
348 | let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, path)?; |
349 | if NapiArgType::MutRef == arg_type { |
350 | mut_ref_spans.push(path.ty.span()); |
351 | } |
352 | if arg_type.is_ref() { |
353 | refs.push(make_ref(quote! { cb.get_arg(#i) })); |
354 | } |
355 | arg_conversions.push(arg_conversion); |
356 | args.push(quote! { #ident }); |
357 | } |
358 | } |
359 | NapiFnArgKind::Callback(cb) => { |
360 | arg_conversions.push(self.gen_cb_arg_conversion(&ident, i, cb)); |
361 | args.push(quote! { #ident }); |
362 | } |
363 | } |
364 | } |
365 | |
366 | Ok(ArgConversions { |
367 | arg_conversions, |
368 | args, |
369 | refs, |
370 | mut_ref_spans, |
371 | unsafe_: self.unsafe_, |
372 | }) |
373 | } |
374 | |
375 | /// Returns a type conversion, and a boolean indicating whether this value needs to have a reference created to extend the lifetime |
376 | /// for async functions. |
377 | fn gen_ty_arg_conversion( |
378 | &self, |
379 | arg_name: &Ident, |
380 | index: usize, |
381 | path: &syn::PatType, |
382 | ) -> BindgenResult<(TokenStream, NapiArgType)> { |
383 | let ty = &*path.ty; |
384 | let type_check = if self.return_if_invalid { |
385 | quote! { |
386 | if let Ok(maybe_promise) = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index)) { |
387 | if !maybe_promise.is_null() { |
388 | return Ok(maybe_promise); |
389 | } |
390 | } else { |
391 | return Ok(std::ptr::null_mut()); |
392 | } |
393 | } |
394 | } else if self.strict { |
395 | quote! { |
396 | let maybe_promise = <#ty as napi::bindgen_prelude::ValidateNapiValue>::validate(env, cb.get_arg(#index))?; |
397 | if !maybe_promise.is_null() { |
398 | return Ok(maybe_promise); |
399 | } |
400 | } |
401 | } else { |
402 | quote! {} |
403 | }; |
404 | |
405 | match ty { |
406 | syn::Type::Reference(syn::TypeReference { |
407 | lifetime: Some(lifetime), |
408 | .. |
409 | }) => Err(Diagnostic::span_error( |
410 | lifetime.span(), |
411 | "lifetime is not allowed in napi function arguments" , |
412 | )), |
413 | syn::Type::Reference(syn::TypeReference { |
414 | mutability: Some(_), |
415 | elem, |
416 | .. |
417 | }) => { |
418 | let q = quote! { |
419 | let #arg_name = { |
420 | #type_check |
421 | <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))? |
422 | }; |
423 | }; |
424 | Ok((q, NapiArgType::MutRef)) |
425 | } |
426 | syn::Type::Reference(syn::TypeReference { |
427 | mutability, elem, .. |
428 | }) => { |
429 | if let syn::Type::Slice(slice) = &**elem { |
430 | if let syn::Type::Path(ele) = &*slice.elem { |
431 | if let Some(syn::PathSegment { ident, .. }) = ele.path.segments.first() { |
432 | if TYPEDARRAY_SLICE_TYPES.contains_key(&&*ident.to_string()) { |
433 | let q = quote! { |
434 | let #arg_name = { |
435 | #type_check |
436 | <&mut #elem as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))? |
437 | }; |
438 | }; |
439 | return Ok((q, NapiArgType::Ref)); |
440 | } |
441 | } |
442 | } |
443 | } |
444 | let q = if mutability.is_some() { |
445 | quote! { |
446 | let #arg_name = { |
447 | #type_check |
448 | <#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))? |
449 | } |
450 | } |
451 | } else { |
452 | quote! { |
453 | let #arg_name = { |
454 | #type_check |
455 | <#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))? |
456 | }; |
457 | } |
458 | }; |
459 | Ok(( |
460 | q, |
461 | if mutability.is_some() { |
462 | NapiArgType::MutRef |
463 | } else { |
464 | NapiArgType::Ref |
465 | }, |
466 | )) |
467 | } |
468 | _ => { |
469 | let q = quote! { |
470 | let #arg_name = { |
471 | #type_check |
472 | <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))? |
473 | }; |
474 | }; |
475 | Ok((q, NapiArgType::Value)) |
476 | } |
477 | } |
478 | } |
479 | |
480 | fn gen_cb_arg_conversion(&self, arg_name: &Ident, index: usize, cb: &CallbackArg) -> TokenStream { |
481 | let mut inputs = vec![]; |
482 | let mut arg_conversions = vec![]; |
483 | |
484 | for (i, ty) in cb.args.iter().enumerate() { |
485 | let cb_arg_ident = Ident::new(&format!("callback_arg_ {}" , i), Span::call_site()); |
486 | inputs.push(quote! { #cb_arg_ident: #ty }); |
487 | arg_conversions.push( |
488 | quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #cb_arg_ident)? }, |
489 | ); |
490 | } |
491 | |
492 | let ret = match &cb.ret { |
493 | Some(ty) => { |
494 | quote! { |
495 | let ret = <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, ret_ptr)?; |
496 | |
497 | Ok(ret) |
498 | } |
499 | } |
500 | None => quote! { Ok(()) }, |
501 | }; |
502 | |
503 | quote! { |
504 | napi::bindgen_prelude::assert_type_of!(env, cb.get_arg(#index), napi::bindgen_prelude::ValueType::Function)?; |
505 | let #arg_name = |#(#inputs),*| { |
506 | let args = vec![ |
507 | #(#arg_conversions),* |
508 | ]; |
509 | |
510 | let mut ret_ptr = std::ptr::null_mut(); |
511 | |
512 | napi::bindgen_prelude::check_pending_exception!( |
513 | env, |
514 | napi::bindgen_prelude::sys::napi_call_function( |
515 | env, |
516 | cb.this(), |
517 | cb.get_arg(#index), |
518 | args.len(), |
519 | args.as_ptr(), |
520 | &mut ret_ptr |
521 | ) |
522 | )?; |
523 | |
524 | #ret |
525 | }; |
526 | } |
527 | } |
528 | |
529 | fn gen_fn_receiver(&self) -> TokenStream { |
530 | let name = &self.name; |
531 | |
532 | match self.fn_self { |
533 | Some(FnSelf::Value) => { |
534 | // impossible, panic! in parser |
535 | unreachable!(); |
536 | } |
537 | Some(FnSelf::Ref) | Some(FnSelf::MutRef) => quote! { this.#name }, |
538 | None => match &self.parent { |
539 | Some(class) => quote! { #class::#name }, |
540 | None => quote! { #name }, |
541 | }, |
542 | } |
543 | } |
544 | |
545 | fn gen_fn_return(&self, ret: &Ident) -> TokenStream { |
546 | let js_name = &self.js_name; |
547 | |
548 | if let Some(ty) = &self.ret { |
549 | let ty_string = ty.into_token_stream().to_string(); |
550 | let is_return_self = ty_string == "& Self" || ty_string == "&mut Self" ; |
551 | if self.kind == FnKind::Constructor { |
552 | let parent = self |
553 | .parent |
554 | .as_ref() |
555 | .expect("Parent must exist for constructor" ); |
556 | if self.is_ret_result { |
557 | if self.parent_is_generator { |
558 | quote! { cb.construct_generator::<false, #parent>(#js_name, #ret?) } |
559 | } else { |
560 | quote! { cb.construct::<false, #parent>(#js_name, #ret?) } |
561 | } |
562 | } else if self.parent_is_generator { |
563 | quote! { cb.construct_generator::<false, #parent>(#js_name, #ret) } |
564 | } else { |
565 | quote! { cb.construct::<false, #parent>(#js_name, #ret) } |
566 | } |
567 | } else if self.kind == FnKind::Factory { |
568 | if self.is_ret_result { |
569 | if self.parent_is_generator { |
570 | quote! { cb.generator_factory(#js_name, #ret?) } |
571 | } else if self.is_async { |
572 | quote! { cb.factory(#js_name, #ret) } |
573 | } else { |
574 | quote! { cb.factory(#js_name, #ret?) } |
575 | } |
576 | } else if self.parent_is_generator { |
577 | quote! { cb.generator_factory(#js_name, #ret) } |
578 | } else { |
579 | quote! { cb.factory(#js_name, #ret) } |
580 | } |
581 | } else if self.is_ret_result { |
582 | if self.is_async { |
583 | quote! { |
584 | <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret) |
585 | } |
586 | } else if is_return_self { |
587 | quote! { #ret.map(|_| cb.this) } |
588 | } else { |
589 | quote! { |
590 | match #ret { |
591 | Ok(value) => napi::bindgen_prelude::ToNapiValue::to_napi_value(env, value), |
592 | Err(err) => { |
593 | napi::bindgen_prelude::JsError::from(err).throw_into(env); |
594 | Ok(std::ptr::null_mut()) |
595 | }, |
596 | } |
597 | } |
598 | } |
599 | } else if is_return_self { |
600 | quote! { Ok(cb.this) } |
601 | } else { |
602 | quote! { |
603 | <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #ret) |
604 | } |
605 | } |
606 | } else { |
607 | quote! { |
608 | <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ()) |
609 | } |
610 | } |
611 | } |
612 | |
613 | fn gen_fn_register(&self) -> TokenStream { |
614 | if self.parent.is_some() { |
615 | quote! {} |
616 | } else { |
617 | let name_str = self.name.to_string(); |
618 | let js_name = format!(" {}\0" , &self.js_name); |
619 | let name_len = self.js_name.len(); |
620 | let module_register_name = &self.register_name; |
621 | let intermediate_ident = get_intermediate_ident(&name_str); |
622 | let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref()); |
623 | let cb_name = Ident::new(&format!(" {}_js_function" , name_str), Span::call_site()); |
624 | |
625 | quote! { |
626 | #[allow(non_snake_case)] |
627 | #[allow(clippy::all)] |
628 | unsafe fn #cb_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> { |
629 | let mut fn_ptr = std::ptr::null_mut(); |
630 | |
631 | napi::bindgen_prelude::check_status!( |
632 | napi::bindgen_prelude::sys::napi_create_function( |
633 | env, |
634 | #js_name.as_ptr().cast(), |
635 | #name_len, |
636 | Some(#intermediate_ident), |
637 | std::ptr::null_mut(), |
638 | &mut fn_ptr, |
639 | ), |
640 | "Failed to register function `{}`" , |
641 | #name_str, |
642 | )?; |
643 | napi::bindgen_prelude::register_js_function(#js_name, #cb_name, Some(#intermediate_ident)); |
644 | Ok(fn_ptr) |
645 | } |
646 | |
647 | #[allow(clippy::all)] |
648 | #[allow(non_snake_case)] |
649 | #[cfg(all(not(test), not(feature = "noop" ), not(target_family = "wasm" )))] |
650 | #[napi::bindgen_prelude::ctor] |
651 | fn #module_register_name() { |
652 | napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name); |
653 | } |
654 | |
655 | #[allow(clippy::all)] |
656 | #[allow(non_snake_case)] |
657 | #[cfg(all(not(test), not(feature = "noop" ), target_family = "wasm" ))] |
658 | #[no_mangle] |
659 | extern "C" fn #module_register_name() { |
660 | napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name, #cb_name); |
661 | } |
662 | } |
663 | } |
664 | } |
665 | } |
666 | |
667 | struct ArgConversions { |
668 | pub args: Vec<TokenStream>, |
669 | pub arg_conversions: Vec<TokenStream>, |
670 | pub refs: Vec<TokenStream>, |
671 | pub mut_ref_spans: Vec<Span>, |
672 | pub unsafe_: bool, |
673 | } |
674 | |
675 | #[derive (Debug, PartialEq, Eq)] |
676 | enum NapiArgType { |
677 | Ref, |
678 | MutRef, |
679 | Value, |
680 | } |
681 | |
682 | impl NapiArgType { |
683 | fn is_ref(&self) -> bool { |
684 | matches!(self, NapiArgType::Ref | NapiArgType::MutRef) |
685 | } |
686 | } |
687 | |