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: usize = Rc::strong_count(&self.finalize_callbacks);
35 let mut ref_count: u32 = 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: i32 = 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: *mut napi_value__ = 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: *mut c_void = 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(t: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 reason: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: *mut napi_value__ = 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: *mut napi_value__ = 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}
318