| 1 | use std::cell::{Cell, RefCell}; |
| 2 | use std::collections::HashMap; |
| 3 | use std::ffi::c_void; |
| 4 | use std::ops::{Deref, DerefMut}; |
| 5 | use std::ptr; |
| 6 | use std::rc::{Rc, Weak}; |
| 7 | |
| 8 | use crate::bindgen_prelude::FromNapiValue; |
| 9 | use crate::{bindgen_runtime::ToNapiValue, check_status, Env, Error, Result, Status}; |
| 10 | |
| 11 | type RefInformation = ( |
| 12 | /* wrapped_value */ *mut c_void, |
| 13 | /* napi_ref */ crate::sys::napi_ref, |
| 14 | /* finalize_callback */ *const Cell<*mut dyn FnOnce()>, |
| 15 | ); |
| 16 | |
| 17 | thread_local! { |
| 18 | pub(crate) static REFERENCE_MAP: RefCell<HashMap<*mut c_void, RefInformation>> = RefCell::new(HashMap::default()); |
| 19 | } |
| 20 | |
| 21 | /// ### Experimental feature |
| 22 | /// |
| 23 | /// Create a `reference` from `Class` instance. |
| 24 | /// Unref the `Reference` when the `Reference` is dropped. |
| 25 | pub struct Reference<T: 'static> { |
| 26 | raw: *mut T, |
| 27 | napi_ref: crate::sys::napi_ref, |
| 28 | env: *mut c_void, |
| 29 | finalize_callbacks: Rc<Cell<*mut dyn FnOnce()>>, |
| 30 | } |
| 31 | |
| 32 | unsafe impl<T: Send> Send for Reference<T> {} |
| 33 | unsafe impl<T: Sync> Sync for Reference<T> {} |
| 34 | |
| 35 | impl<T> Drop for Reference<T> { |
| 36 | fn drop(&mut self) { |
| 37 | let rc_strong_count: usize = Rc::strong_count(&self.finalize_callbacks); |
| 38 | let mut ref_count: u32 = 0; |
| 39 | // If Rc strong count == 1, then the referenced object is dropped on GC |
| 40 | // It would happen when the process is exiting |
| 41 | // In general, the `drop` of the `Reference` would happen first |
| 42 | if rc_strong_count > 1 { |
| 43 | let status: i32 = unsafe { |
| 44 | crate::sys::napi_reference_unref( |
| 45 | self.env as crate::sys::napi_env, |
| 46 | self.napi_ref, |
| 47 | &mut ref_count, |
| 48 | ) |
| 49 | }; |
| 50 | debug_assert!( |
| 51 | status == crate::sys::Status::napi_ok, |
| 52 | "Reference unref failed, status code: {}" , |
| 53 | crate::Status::from(status) |
| 54 | ); |
| 55 | }; |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | impl<T: 'static> Reference<T> { |
| 60 | #[doc (hidden)] |
| 61 | #[allow (clippy::not_unsafe_ptr_arg_deref)] |
| 62 | pub fn add_ref(env: crate::sys::napi_env, t: *mut c_void, value: RefInformation) { |
| 63 | REFERENCE_MAP.with(|map| { |
| 64 | if let Some((_, previous_ref, previous_rc)) = map.borrow_mut().insert(t, value) { |
| 65 | unsafe { Rc::from_raw(previous_rc) }; |
| 66 | unsafe { crate::sys::napi_delete_reference(env, previous_ref) }; |
| 67 | } |
| 68 | }); |
| 69 | } |
| 70 | |
| 71 | #[doc (hidden)] |
| 72 | pub unsafe fn from_value_ptr(t: *mut c_void, env: crate::sys::napi_env) -> Result<Self> { |
| 73 | if let Some((wrapped_value, napi_ref, finalize_callbacks_ptr)) = |
| 74 | REFERENCE_MAP.with(|map| map.borrow().get(&t).cloned()) |
| 75 | { |
| 76 | let mut ref_count = 0; |
| 77 | check_status!( |
| 78 | unsafe { crate::sys::napi_reference_ref(env, napi_ref, &mut ref_count) }, |
| 79 | "Failed to ref napi reference" |
| 80 | )?; |
| 81 | let finalize_callbacks_raw = unsafe { Rc::from_raw(finalize_callbacks_ptr) }; |
| 82 | let finalize_callbacks = finalize_callbacks_raw.clone(); |
| 83 | // Leak the raw finalize callbacks |
| 84 | let _ = Rc::into_raw(finalize_callbacks_raw); |
| 85 | Ok(Self { |
| 86 | raw: wrapped_value.cast(), |
| 87 | napi_ref, |
| 88 | env: env.cast(), |
| 89 | finalize_callbacks, |
| 90 | }) |
| 91 | } else { |
| 92 | Err(Error::new( |
| 93 | Status::InvalidArg, |
| 94 | format!("Class for Type {:?} not found" , t), |
| 95 | )) |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | impl<T: 'static> ToNapiValue for Reference<T> { |
| 101 | unsafe fn to_napi_value(env: crate::sys::napi_env, val: Self) -> Result<crate::sys::napi_value> { |
| 102 | let mut result: *mut napi_value__ = ptr::null_mut(); |
| 103 | check_status!( |
| 104 | unsafe { crate::sys::napi_get_reference_value(env, val.napi_ref, &mut result) }, |
| 105 | "Failed to get reference value" |
| 106 | )?; |
| 107 | Ok(result) |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | impl<T: 'static> FromNapiValue for Reference<T> { |
| 112 | unsafe fn from_napi_value( |
| 113 | env: crate::sys::napi_env, |
| 114 | napi_val: crate::sys::napi_value, |
| 115 | ) -> Result<Self> { |
| 116 | let mut value: *mut c_void = ptr::null_mut(); |
| 117 | check_status!( |
| 118 | unsafe { crate::sys::napi_unwrap(env, napi_val, &mut value) }, |
| 119 | "Unwrap value [ {}] from class Reference failed" , |
| 120 | std::any::type_name::<T>(), |
| 121 | )?; |
| 122 | unsafe { Reference::from_value_ptr(t:value.cast(), env) } |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | impl<T: 'static> Reference<T> { |
| 127 | pub fn clone(&self, env: Env) -> Result<Self> { |
| 128 | let mut ref_count = 0; |
| 129 | check_status!( |
| 130 | unsafe { crate::sys::napi_reference_ref(env.0, self.napi_ref, &mut ref_count) }, |
| 131 | "Failed to ref napi reference" |
| 132 | )?; |
| 133 | Ok(Self { |
| 134 | raw: self.raw, |
| 135 | napi_ref: self.napi_ref, |
| 136 | env: env.0 as *mut c_void, |
| 137 | finalize_callbacks: self.finalize_callbacks.clone(), |
| 138 | }) |
| 139 | } |
| 140 | |
| 141 | pub fn downgrade(&self) -> WeakReference<T> { |
| 142 | WeakReference { |
| 143 | raw: self.raw, |
| 144 | napi_ref: self.napi_ref, |
| 145 | finalize_callbacks: Rc::downgrade(&self.finalize_callbacks), |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | /// Safety to share because caller can provide `Env` |
| 150 | pub fn share_with<S: 'static, F: FnOnce(&'static mut T) -> Result<S>>( |
| 151 | self, |
| 152 | #[allow (unused_variables)] env: Env, |
| 153 | f: F, |
| 154 | ) -> Result<SharedReference<T, S>> { |
| 155 | let s = f(Box::leak(unsafe { Box::from_raw(self.raw) }))?; |
| 156 | let s_ptr = Box::into_raw(Box::new(s)); |
| 157 | let prev_drop_fn = unsafe { Box::from_raw(self.finalize_callbacks.get()) }; |
| 158 | let drop_fn = Box::new(move || { |
| 159 | drop(unsafe { Box::from_raw(s_ptr) }); |
| 160 | prev_drop_fn(); |
| 161 | }); |
| 162 | self.finalize_callbacks.set(Box::into_raw(drop_fn)); |
| 163 | Ok(SharedReference { |
| 164 | raw: s_ptr, |
| 165 | owner: self, |
| 166 | }) |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | impl<T: 'static> Deref for Reference<T> { |
| 171 | type Target = T; |
| 172 | |
| 173 | fn deref(&self) -> &Self::Target { |
| 174 | unsafe { Box::leak(Box::from_raw(self.raw)) } |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | impl<T: 'static> DerefMut for Reference<T> { |
| 179 | fn deref_mut(&mut self) -> &mut Self::Target { |
| 180 | unsafe { Box::leak(Box::from_raw(self.raw)) } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | pub struct WeakReference<T: 'static> { |
| 185 | raw: *mut T, |
| 186 | napi_ref: crate::sys::napi_ref, |
| 187 | finalize_callbacks: Weak<Cell<*mut dyn FnOnce()>>, |
| 188 | } |
| 189 | |
| 190 | impl<T> Clone for WeakReference<T> { |
| 191 | fn clone(&self) -> Self { |
| 192 | Self { |
| 193 | raw: self.raw, |
| 194 | napi_ref: self.napi_ref, |
| 195 | finalize_callbacks: self.finalize_callbacks.clone(), |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | impl<T: 'static> ToNapiValue for WeakReference<T> { |
| 201 | unsafe fn to_napi_value(env: crate::sys::napi_env, val: Self) -> Result<crate::sys::napi_value> { |
| 202 | if Weak::strong_count(&val.finalize_callbacks) == 0 { |
| 203 | return Err(Error::new( |
| 204 | Status::GenericFailure, |
| 205 | reason:format!( |
| 206 | "The original reference that WeakReference< {}> is pointing to is dropped" , |
| 207 | std::any::type_name::<T>() |
| 208 | ), |
| 209 | )); |
| 210 | }; |
| 211 | let mut result: *mut napi_value__ = ptr::null_mut(); |
| 212 | check_status!( |
| 213 | unsafe { crate::sys::napi_get_reference_value(env, val.napi_ref, &mut result) }, |
| 214 | "Failed to get reference value" |
| 215 | )?; |
| 216 | Ok(result) |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | impl<T: 'static> WeakReference<T> { |
| 221 | pub fn upgrade(&self, env: Env) -> Result<Option<Reference<T>>> { |
| 222 | if let Some(finalize_callbacks) = self.finalize_callbacks.upgrade() { |
| 223 | let mut ref_count = 0; |
| 224 | check_status!( |
| 225 | unsafe { crate::sys::napi_reference_ref(env.0, self.napi_ref, &mut ref_count) }, |
| 226 | "Failed to ref napi reference" |
| 227 | )?; |
| 228 | Ok(Some(Reference { |
| 229 | raw: self.raw, |
| 230 | napi_ref: self.napi_ref, |
| 231 | env: env.0 as *mut c_void, |
| 232 | finalize_callbacks, |
| 233 | })) |
| 234 | } else { |
| 235 | Ok(None) |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | pub fn get(&self) -> Option<&T> { |
| 240 | if Weak::strong_count(&self.finalize_callbacks) == 0 { |
| 241 | None |
| 242 | } else { |
| 243 | Some(unsafe { Box::leak(Box::from_raw(self.raw)) }) |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | pub fn get_mut(&mut self) -> Option<&mut T> { |
| 248 | if Weak::strong_count(&self.finalize_callbacks) == 0 { |
| 249 | None |
| 250 | } else { |
| 251 | Some(unsafe { Box::leak(Box::from_raw(self.raw)) }) |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | /// ### Experimental feature |
| 257 | /// |
| 258 | /// Create a `SharedReference` from an existed `Reference`. |
| 259 | pub struct SharedReference<T: 'static, S: 'static> { |
| 260 | raw: *mut S, |
| 261 | owner: Reference<T>, |
| 262 | } |
| 263 | |
| 264 | impl<T: 'static, S: 'static> SharedReference<T, S> { |
| 265 | pub fn clone(&self, env: Env) -> Result<Self> { |
| 266 | Ok(SharedReference { |
| 267 | raw: self.raw, |
| 268 | owner: self.owner.clone(env)?, |
| 269 | }) |
| 270 | } |
| 271 | |
| 272 | pub fn clone_owner(&self, env: Env) -> Result<Reference<T>> { |
| 273 | self.owner.clone(env) |
| 274 | } |
| 275 | |
| 276 | /// Safety to share because caller can provide `Env` |
| 277 | pub fn share_with<U: 'static, F: FnOnce(&'static mut S) -> Result<U>>( |
| 278 | self, |
| 279 | #[allow (unused_variables)] env: Env, |
| 280 | f: F, |
| 281 | ) -> Result<SharedReference<T, U>> { |
| 282 | let s = f(Box::leak(unsafe { Box::from_raw(self.raw) }))?; |
| 283 | let raw = Box::into_raw(Box::new(s)); |
| 284 | let prev_drop_fn = unsafe { Box::from_raw(self.owner.finalize_callbacks.get()) }; |
| 285 | let drop_fn = Box::new(move || { |
| 286 | drop(unsafe { Box::from_raw(raw) }); |
| 287 | prev_drop_fn(); |
| 288 | }); |
| 289 | self.owner.finalize_callbacks.set(Box::into_raw(drop_fn)); |
| 290 | Ok(SharedReference { |
| 291 | raw, |
| 292 | owner: self.owner, |
| 293 | }) |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | impl<T: 'static, S: 'static> ToNapiValue for SharedReference<T, S> { |
| 298 | unsafe fn to_napi_value(env: crate::sys::napi_env, val: Self) -> Result<crate::sys::napi_value> { |
| 299 | let mut result: *mut napi_value__ = ptr::null_mut(); |
| 300 | check_status!( |
| 301 | unsafe { crate::sys::napi_get_reference_value(env, val.owner.napi_ref, &mut result) }, |
| 302 | "Failed to get reference value" |
| 303 | )?; |
| 304 | Ok(result) |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | impl<T: 'static, S: 'static> Deref for SharedReference<T, S> { |
| 309 | type Target = S; |
| 310 | |
| 311 | fn deref(&self) -> &Self::Target { |
| 312 | unsafe { Box::leak(Box::from_raw(self.raw)) } |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | impl<T: 'static, S: 'static> DerefMut for SharedReference<T, S> { |
| 317 | fn deref_mut(&mut self) -> &mut Self::Target { |
| 318 | unsafe { Box::leak(Box::from_raw(self.raw)) } |
| 319 | } |
| 320 | } |
| 321 | |