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