edon/napi/bindgen_runtime/js_values/
value_ref.rs1use 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 *mut c_void,
21 libnode_sys::napi_ref,
22 *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
29pub 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 {
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 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 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
283pub 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 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}