rquickjs_core/runtime/
userdata.rs1use alloc::boxed::Box;
2use core::fmt;
3use core::{
4 any::{Any, TypeId},
5 cell::{Cell, UnsafeCell},
6 hash::{BuildHasherDefault, Hasher},
7 mem::ManuallyDrop,
8 ops::Deref,
9};
10#[cfg(not(feature = "std"))]
11use hashbrown::HashMap;
12#[cfg(feature = "std")]
13use std::collections::HashMap;
14
15use crate::JsLifetime;
16
17unsafe fn to_static<'js, T>(this: T) -> T::Changed<'static>
18where
19 T: JsLifetime<'js> + Sized,
20{
21 assert_eq!(
22 core::mem::size_of::<T>(),
23 core::mem::size_of::<T::Changed<'static>>(),
24 "Invalid implementation of JsLifetime, size_of::<T>() != size_of::<T::Changed<'static>>()"
25 );
26 assert_eq!(
27 core::mem::align_of::<T>(),
28 core::mem::align_of::<T::Changed<'static>>(),
29 "Invalid implementation of JsLifetime, align_of::<T>() != align_of::<T::Changed<'static>>()"
30 );
31
32 union Trans<A, B> {
35 from: ManuallyDrop<A>,
36 to: ManuallyDrop<B>,
37 }
38
39 ManuallyDrop::into_inner(
40 (Trans {
41 from: ManuallyDrop::new(this),
42 })
43 .to,
44 )
45}
46
47unsafe fn from_static_box<'js, T>(this: Box<T::Changed<'static>>) -> Box<T>
48where
49 T: JsLifetime<'js> + Sized,
50{
51 assert_eq!(
52 core::mem::size_of::<T>(),
53 core::mem::size_of::<T::Changed<'static>>(),
54 "Invalid implementation of JsLifetime, size_of::<T>() != size_of::<T::Changed<'static>>()"
55 );
56 assert_eq!(
57 core::mem::align_of::<T>(),
58 core::mem::align_of::<T::Changed<'static>>(),
59 "Invalid implementation of JsLifetime, align_of::<T>() != align_of::<T::Changed<'static>>()"
60 );
61
62 Box::from_raw(Box::into_raw(this) as *mut T)
63}
64
65unsafe fn from_static_ref<'a, 'js, T>(this: &'a T::Changed<'static>) -> &'a T
66where
67 T: JsLifetime<'js> + Sized,
68{
69 core::mem::transmute(this)
70}
71
72pub struct UserDataError<T>(pub T);
73
74impl<T> fmt::Display for UserDataError<T> {
75 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76 write!(
77 f,
78 "tried to mutate the user data store while it was being referenced"
79 )
80 }
81}
82
83impl<T> fmt::Debug for UserDataError<T> {
84 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85 fmt::Display::fmt(self, f)
86 }
87}
88
89#[derive(Default)]
90struct IdHasher(u64);
91
92impl Hasher for IdHasher {
93 fn write(&mut self, _: &[u8]) {
94 unreachable!("TypeId calls write_u64");
95 }
96
97 fn write_u64(&mut self, id: u64) {
98 self.0 = id;
99 }
100
101 fn finish(&self) -> u64 {
102 self.0
103 }
104}
105
106#[derive(Default)]
108pub(crate) struct UserDataMap {
109 map: UnsafeCell<HashMap<TypeId, Box<dyn Any>, BuildHasherDefault<IdHasher>>>,
110 count: Cell<usize>,
111}
112
113impl UserDataMap {
114 pub fn insert<'js, U>(&self, data: U) -> Result<Option<Box<U>>, UserDataError<U>>
115 where
116 U: JsLifetime<'js>,
117 U::Changed<'static>: Any,
118 {
119 if self.count.get() > 0 {
120 return Err(UserDataError(data));
121 }
122 let user_static = unsafe { to_static(data) };
123 let id = TypeId::of::<U::Changed<'static>>();
124 let r = unsafe { (*self.map.get()).insert(id, Box::new(user_static)) }.map(|x| {
125 let r = x
126 .downcast()
127 .expect("type confusion! userdata not stored under the right type id");
128 unsafe { from_static_box(r) }
129 });
130 Ok(r)
131 }
132
133 pub fn remove<'js, U>(&self) -> Result<Option<Box<U>>, UserDataError<()>>
134 where
135 U: JsLifetime<'js>,
136 U::Changed<'static>: Any,
137 {
138 if self.count.get() > 0 {
139 return Err(UserDataError(()));
140 }
141 let id = TypeId::of::<U::Changed<'static>>();
142 let r = unsafe { (*self.map.get()).remove(&id) }.map(|x| {
143 let r = x
144 .downcast()
145 .expect("type confusion! userdata not stored under the right type id");
146 unsafe { from_static_box(r) }
147 });
148 Ok(r)
149 }
150
151 pub fn get<'js, U>(&self) -> Option<UserDataGuard<U>>
152 where
153 U: JsLifetime<'js>,
154 U::Changed<'static>: Any,
155 {
156 let id = TypeId::of::<U::Changed<'static>>();
157 unsafe { (*self.map.get()).get(&id) }.map(|x| {
158 self.count.set(self.count.get() + 1);
159 let u = x
160 .downcast_ref()
161 .expect("type confusion! userdata not stored under the right type id");
162
163 let r = unsafe { from_static_ref(u) };
164 UserDataGuard { map: self, r }
165 })
166 }
167
168 pub fn clear(&mut self) {
169 self.map.get_mut().clear()
170 }
171}
172
173pub struct UserDataGuard<'a, U> {
176 map: &'a UserDataMap,
177 r: &'a U,
178}
179
180impl<'a, U> Deref for UserDataGuard<'a, U> {
181 type Target = U;
182
183 fn deref(&self) -> &Self::Target {
184 self.r
185 }
186}
187
188impl<'a, U> Drop for UserDataGuard<'a, U> {
189 fn drop(&mut self) {
190 self.map.count.set(self.map.count.get() - 1)
191 }
192}