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: 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
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: *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
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: *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
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 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
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: *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
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}
321