1use std::cell::Cell;
2use std::ffi::c_void;
3use std::ptr;
4use std::rc::Rc;
5use std::sync::atomic::{AtomicBool, Ordering};
6
7use crate::{bindgen_prelude::*, check_status, sys, Result};
8
9thread_local! {
10 #[doc(hidden)]
11 /// Determined is `constructor` called from Class `factory`
12 pub static ___CALL_FROM_FACTORY: AtomicBool = AtomicBool::new(false);
13}
14
15#[repr(transparent)]
16struct EmptyStructPlaceholder(u8);
17
18#[doc(hidden)]
19pub struct CallbackInfo<const N: usize> {
20 env: sys::napi_env,
21 pub this: sys::napi_value,
22 pub args: [sys::napi_value; N],
23 this_reference: sys::napi_ref,
24}
25
26impl<const N: usize> CallbackInfo<N> {
27 #[allow(clippy::not_unsafe_ptr_arg_deref)]
28 pub fn new(
29 env: sys::napi_env,
30 callback_info: sys::napi_callback_info,
31 required_argc: Option<usize>,
32 // for async class factory, the `this` will be used after the async call
33 // so we must create reference for it and use it after async resolved
34 use_after_async: bool,
35 ) -> Result<Self> {
36 let mut this = ptr::null_mut();
37 let mut args = [ptr::null_mut(); N];
38 let mut argc = N;
39
40 unsafe {
41 check_status!(
42 sys::napi_get_cb_info(
43 env,
44 callback_info,
45 &mut argc,
46 args.as_mut_ptr(),
47 &mut this,
48 ptr::null_mut(),
49 ),
50 "Failed to initialize napi function call."
51 )?;
52 };
53
54 if let Some(required_argc) = required_argc {
55 if required_argc > argc {
56 return Err(Error::new(
57 Status::InvalidArg,
58 format!(
59 "{} arguments required by received {}.",
60 required_argc, &argc
61 ),
62 ));
63 }
64 }
65
66 let mut this_reference = ptr::null_mut();
67
68 if use_after_async {
69 check_status!(
70 unsafe { sys::napi_create_reference(env, this, 1, &mut this_reference) },
71 "Failed to create reference for `this` in async class factory"
72 )?;
73 }
74
75 Ok(Self {
76 env,
77 this,
78 args,
79 this_reference,
80 })
81 }
82
83 pub fn get_arg(&self, index: usize) -> sys::napi_value {
84 self.args[index]
85 }
86
87 pub fn this(&self) -> sys::napi_value {
88 self.this
89 }
90
91 fn _construct<const IsEmptyStructHint: bool, T: ObjectFinalize + 'static>(
92 &self,
93 js_name: &str,
94 obj: T,
95 ) -> Result<(sys::napi_value, *mut T)> {
96 let obj = Box::new(obj);
97 let this = self.this();
98 let mut value_ref = Box::into_raw(obj);
99 // for empty struct like `#[napi] struct A;`, the `value_ref` will be `0x1`
100 // and it will be overwritten by the others instance of the same class
101 if IsEmptyStructHint || value_ref as usize == 0x1 {
102 value_ref = Box::into_raw(Box::new(EmptyStructPlaceholder(0))).cast();
103 }
104 let mut object_ref = ptr::null_mut();
105 let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
106 let finalize_callbacks_ptr = Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize))));
107 unsafe {
108 check_status!(
109 sys::napi_wrap(
110 self.env,
111 this,
112 value_ref.cast(),
113 Some(raw_finalize_unchecked::<T>),
114 ptr::null_mut(),
115 &mut object_ref
116 ),
117 "Failed to initialize class `{}`",
118 js_name,
119 )?;
120 };
121
122 Reference::<T>::add_ref(
123 self.env,
124 value_ref.cast(),
125 (value_ref.cast(), object_ref, finalize_callbacks_ptr),
126 );
127 Ok((this, value_ref))
128 }
129
130 pub fn construct<const IsEmptyStructHint: bool, T: ObjectFinalize + 'static>(
131 &self,
132 js_name: &str,
133 obj: T,
134 ) -> Result<sys::napi_value> {
135 self
136 ._construct::<IsEmptyStructHint, T>(js_name, obj)
137 .map(|(v, _)| v)
138 }
139
140 pub fn construct_generator<
141 const IsEmptyStructHint: bool,
142 T: Generator + ObjectFinalize + 'static,
143 >(
144 &self,
145 js_name: &str,
146 obj: T,
147 ) -> Result<sys::napi_value> {
148 let (instance, generator_ptr) = self._construct::<IsEmptyStructHint, T>(js_name, obj)?;
149 crate::__private::create_iterator(self.env, instance, generator_ptr);
150 Ok(instance)
151 }
152
153 pub fn factory<T: ObjectFinalize + 'static>(
154 &self,
155 js_name: &str,
156 obj: T,
157 ) -> Result<sys::napi_value> {
158 self._factory(js_name, obj).map(|(value, _)| value)
159 }
160
161 pub fn generator_factory<T: ObjectFinalize + Generator + 'static>(
162 &self,
163 js_name: &str,
164 obj: T,
165 ) -> Result<sys::napi_value> {
166 let (instance, generator_ptr) = self._factory(js_name, obj)?;
167 crate::__private::create_iterator(self.env, instance, generator_ptr);
168 Ok(instance)
169 }
170
171 fn _factory<T: ObjectFinalize + 'static>(
172 &self,
173 js_name: &str,
174 obj: T,
175 ) -> Result<(sys::napi_value, *mut T)> {
176 let mut this = self.this();
177 let mut instance = ptr::null_mut();
178 if !self.this_reference.is_null() {
179 check_status!(
180 unsafe { sys::napi_get_reference_value(self.env, self.this_reference, &mut this) },
181 "Failed to get reference value for `this` in async class factory"
182 )?;
183 check_status!(
184 unsafe { sys::napi_delete_reference(self.env, self.this_reference) },
185 "Failed to delete reference for `this` in async class factory"
186 )?;
187 }
188 ___CALL_FROM_FACTORY.with(|s| s.store(true, Ordering::Relaxed));
189 let status =
190 unsafe { sys::napi_new_instance(self.env, this, 0, ptr::null_mut(), &mut instance) };
191 ___CALL_FROM_FACTORY.with(|s| s.store(false, Ordering::Relaxed));
192 // Error thrown in `constructor`
193 if status == sys::Status::napi_pending_exception {
194 let mut exception = ptr::null_mut();
195 unsafe { sys::napi_get_and_clear_last_exception(self.env, &mut exception) };
196 unsafe { sys::napi_throw(self.env, exception) };
197 return Ok((ptr::null_mut(), ptr::null_mut()));
198 }
199 check_status!(status, "Failed to create instance of class `{}`", js_name)?;
200 let obj = Box::new(obj);
201 let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
202 let finalize_callbacks_ptr = Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize))));
203 let mut object_ref = ptr::null_mut();
204 let mut value_ref = Box::into_raw(obj);
205
206 // for empty struct like `#[napi] struct A;`, the `value_ref` will be `0x1`
207 // and it will be overwritten by the others instance of the same class
208 if value_ref as usize == 0x1 {
209 value_ref = Box::into_raw(Box::new(EmptyStructPlaceholder(0))).cast();
210 }
211 check_status!(
212 unsafe {
213 sys::napi_wrap(
214 self.env,
215 instance,
216 value_ref.cast(),
217 Some(raw_finalize_unchecked::<T>),
218 ptr::null_mut(),
219 &mut object_ref,
220 )
221 },
222 "Failed to initialize class `{}`",
223 js_name,
224 )?;
225
226 Reference::<T>::add_ref(
227 self.env,
228 value_ref.cast(),
229 (value_ref.cast(), object_ref, finalize_callbacks_ptr),
230 );
231 Ok((instance, value_ref))
232 }
233
234 pub fn unwrap_borrow_mut<T>(&mut self) -> Result<&'static mut T>
235 where
236 T: FromNapiMutRef + TypeName,
237 {
238 unsafe { self.unwrap_raw::<T>() }.map(|raw| Box::leak(unsafe { Box::from_raw(raw) }))
239 }
240
241 pub fn unwrap_borrow<T>(&mut self) -> Result<&'static T>
242 where
243 T: FromNapiRef + TypeName,
244 {
245 unsafe { self.unwrap_raw::<T>() }
246 .map(|raw| Box::leak(unsafe { Box::from_raw(raw) }) as &'static T)
247 }
248
249 #[doc(hidden)]
250 #[inline]
251 pub unsafe fn unwrap_raw<T>(&mut self) -> Result<*mut T>
252 where
253 T: TypeName,
254 {
255 let mut wrapped_val: *mut c_void = std::ptr::null_mut();
256
257 unsafe {
258 check_status!(
259 sys::napi_unwrap(self.env, self.this, &mut wrapped_val),
260 "Failed to unwrap exclusive reference of `{}` type from napi value",
261 T::type_name(),
262 )?;
263
264 Ok(wrapped_val.cast())
265 }
266 }
267}
268