edon/napi/bindgen_runtime/js_values/
value_ref.rs

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