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