napi_calm_down/bindgen_runtime/js_values/
value_ref.rs

1use std::cell::{Cell, RefCell};
2use std::collections::HashMap;
3use std::ffi::c_void;
4use std::ops::{Deref, DerefMut};
5use std::ptr;
6use std::rc::{Rc, Weak};
7
8use crate::bindgen_prelude::FromNapiValue;
9use crate::{bindgen_runtime::ToNapiValue, check_status, Env, Error, Result, Status};
10
11type RefInformation = (
12  /* wrapped_value */ *mut c_void,
13  /* napi_ref */ crate::sys::napi_ref,
14  /* finalize_callback */ *const Cell<*mut dyn FnOnce()>,
15);
16
17thread_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.
25pub 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
32unsafe impl<T: Send> Send for Reference<T> {}
33unsafe impl<T: Sync> Sync for Reference<T> {}
34
35impl<T> Drop for Reference<T> {
36  fn drop(&mut self) {
37    let rc_strong_count = Rc::strong_count(&self.finalize_callbacks);
38    let mut ref_count = 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 = 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
59impl<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
100impl<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 = 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
111impl<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 = 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(value.cast(), env) }
123  }
124}
125
126impl<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
170impl<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
178impl<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
184pub 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
190impl<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
200impl<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        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 = 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
220impl<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`.
259pub struct SharedReference<T: 'static, S: 'static> {
260  raw: *mut S,
261  owner: Reference<T>,
262}
263
264impl<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
297impl<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 = 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
308impl<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
316impl<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}