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