| 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}; |
| 8 | |
| 9 | thread_local! { |
| 10 | #[doc (hidden)] |
| 11 | /// Determined is `constructor` called from Class `factory` |
| 12 | pub static ___CALL_FROM_FACTORY: AtomicBool = const { 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 | |