1 | use std::cell::Cell; |
2 | use std::ffi::c_void; |
3 | use std::ptr; |
4 | use std::rc::Rc; |
5 | use std::sync::atomic::{AtomicBool, Ordering}; |
6 | |
7 | use crate::{bindgen_prelude::*, check_status, sys, Result}; |
8 | |
9 | thread_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)] |
16 | struct EmptyStructPlaceholder(u8); |
17 | |
18 | #[doc (hidden)] |
19 | pub 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 | |
26 | impl<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 | |