script/dom/bindings/
refcounted.rs1use std::cell::RefCell;
26use std::collections::hash_map::Entry::{Occupied, Vacant};
27use std::hash::Hash;
28use std::marker::PhantomData;
29use std::rc::Rc;
30use std::sync::{Arc, Weak};
31
32use js::jsapi::JSTracer;
33use rustc_hash::FxHashMap;
34use script_bindings::script_runtime::CanGc;
35
36use crate::dom::bindings::conversions::ToJSValConvertible;
37use crate::dom::bindings::error::Error;
38use crate::dom::bindings::reflector::{DomObject, Reflector};
39use crate::dom::bindings::root::DomRoot;
40use crate::dom::bindings::trace::trace_reflector;
41use crate::dom::promise::Promise;
42use crate::task::TaskOnce;
43
44mod dummy {
45 use std::cell::RefCell;
47 use std::rc::Rc;
48
49 use rustc_hash::FxHashMap;
50
51 use super::LiveDOMReferences;
52 thread_local!(pub(crate) static LIVE_REFERENCES: Rc<RefCell<LiveDOMReferences>> =
53 Rc::new(RefCell::new(
54 LiveDOMReferences {
55 reflectable_table: RefCell::new(FxHashMap::default()),
56 promise_table: RefCell::new(FxHashMap::default()),
57 }
58 )));
59}
60pub(crate) use self::dummy::LIVE_REFERENCES;
61
62#[derive(MallocSizeOf)]
64struct TrustedReference(
65 #[ignore_malloc_size_of = "This is a shared reference."] *const libc::c_void,
66);
67unsafe impl Send for TrustedReference {}
68
69impl TrustedReference {
70 unsafe fn new(ptr: *const libc::c_void) -> TrustedReference {
74 TrustedReference(ptr)
75 }
76}
77
78pub struct TrustedPromise {
83 dom_object: *const Promise,
84 owner_thread: *const libc::c_void,
85}
86
87unsafe impl Send for TrustedPromise {}
88
89impl TrustedPromise {
90 pub(crate) fn new(promise: Rc<Promise>) -> TrustedPromise {
94 LIVE_REFERENCES.with(|r| {
95 let live_references = &*r.borrow();
96 let ptr = &raw const *promise;
97 live_references.addref_promise(promise);
98 TrustedPromise {
99 dom_object: ptr,
100 owner_thread: (live_references) as *const _ as *const libc::c_void,
101 }
102 })
103 }
104
105 pub(crate) fn root(self) -> Rc<Promise> {
109 LIVE_REFERENCES.with(|r| {
110 let live_references = &*r.borrow();
111 assert_eq!(
112 self.owner_thread,
113 live_references as *const _ as *const libc::c_void
114 );
115 match live_references
116 .promise_table
117 .borrow_mut()
118 .entry(self.dom_object)
119 {
120 Occupied(mut entry) => {
121 let promise = {
122 let promises = entry.get_mut();
123 promises
124 .pop()
125 .expect("rooted promise list unexpectedly empty")
126 };
127 if entry.get().is_empty() {
128 entry.remove();
129 }
130 promise
131 },
132 Vacant(_) => unreachable!(),
133 }
134 })
135 }
136
137 pub(crate) fn reject_task(self, error: Error) -> impl TaskOnce {
139 let this = self;
140 task!(reject_promise: move || {
141 debug!("Rejecting promise.");
142 this.root().reject_error(error, CanGc::note());
143 })
144 }
145
146 pub(crate) fn resolve_task<T>(self, value: T) -> impl TaskOnce
148 where
149 T: ToJSValConvertible + Send,
150 {
151 let this = self;
152 task!(resolve_promise: move || {
153 debug!("Resolving promise.");
154 this.root().resolve_native(&value, CanGc::note());
155 })
156 }
157}
158
159#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
164#[derive(MallocSizeOf)]
165pub(crate) struct Trusted<T: DomObject> {
166 #[conditional_malloc_size_of]
169 refcount: Arc<TrustedReference>,
170 #[ignore_malloc_size_of = "These are shared by all `Trusted` types."]
171 owner_thread: *const LiveDOMReferences,
172 phantom: PhantomData<T>,
173}
174
175impl<T: DomObject> std::fmt::Debug for Trusted<T> {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
177 f.write_str("...")
178 }
179}
180
181unsafe impl<T: DomObject> Send for Trusted<T> {}
182
183impl<T: DomObject> Trusted<T> {
184 pub(crate) fn new(ptr: &T) -> Trusted<T> {
188 fn add_live_reference(
189 ptr: *const libc::c_void,
190 ) -> (Arc<TrustedReference>, *const LiveDOMReferences) {
191 LIVE_REFERENCES.with(|r| {
192 let live_references = &*r.borrow();
193 let refcount = unsafe { live_references.addref(ptr) };
194 (refcount, live_references as *const _)
195 })
196 }
197
198 let (refcount, owner_thread) = add_live_reference(ptr as *const T as *const _);
199 Trusted {
200 refcount,
201 owner_thread,
202 phantom: PhantomData,
203 }
204 }
205
206 pub(crate) fn root(&self) -> DomRoot<T> {
210 fn validate(owner_thread: *const LiveDOMReferences) {
211 assert!(LIVE_REFERENCES.with(|r| {
212 let r = r.borrow();
213 let live_references = &*r;
214 owner_thread == live_references
215 }));
216 }
217 validate(self.owner_thread);
218 unsafe { DomRoot::from_ref(&*(self.refcount.0 as *const T)) }
219 }
220}
221
222impl<T: DomObject> Clone for Trusted<T> {
223 fn clone(&self) -> Trusted<T> {
224 Trusted {
225 refcount: self.refcount.clone(),
226 owner_thread: self.owner_thread,
227 phantom: PhantomData,
228 }
229 }
230}
231
232pub(crate) struct LiveDOMReferences {
235 reflectable_table: RefCell<FxHashMap<*const libc::c_void, Weak<TrustedReference>>>,
237 promise_table: RefCell<FxHashMap<*const Promise, Vec<Rc<Promise>>>>,
238}
239
240impl LiveDOMReferences {
241 pub(crate) fn destruct() {
242 LIVE_REFERENCES.with(|r| {
243 let live_references = r.borrow_mut();
244 let _ = live_references.promise_table.take();
245 let _ = live_references.reflectable_table.take();
246 });
247 }
248
249 fn addref_promise(&self, promise: Rc<Promise>) {
250 let mut table = self.promise_table.borrow_mut();
251 table.entry(&*promise).or_default().push(promise)
252 }
253
254 #[expect(clippy::arc_with_non_send_sync)]
258 unsafe fn addref(&self, ptr: *const libc::c_void) -> Arc<TrustedReference> {
259 let mut table = self.reflectable_table.borrow_mut();
260 let capacity = table.capacity();
261 let len = table.len();
262 if (0 < capacity) && (capacity <= len) {
263 trace!("growing refcounted references by {}", len);
264 remove_nulls(&mut table);
265 table.reserve(len);
266 }
267 match table.entry(ptr) {
268 Occupied(mut entry) => match entry.get().upgrade() {
269 Some(refcount) => refcount,
270 None => {
271 let refcount = Arc::new(unsafe { TrustedReference::new(ptr) });
272 entry.insert(Arc::downgrade(&refcount));
273 refcount
274 },
275 },
276 Vacant(entry) => {
277 let refcount = Arc::new(unsafe { TrustedReference::new(ptr) });
278 entry.insert(Arc::downgrade(&refcount));
279 refcount
280 },
281 }
282 }
283}
284
285fn remove_nulls<K: Eq + Hash + Clone, V>(table: &mut FxHashMap<K, Weak<V>>) {
287 let to_remove: Vec<K> = table
288 .iter()
289 .filter(|&(_, value)| Weak::upgrade(value).is_none())
290 .map(|(key, _)| key.clone())
291 .collect();
292 trace!("removing {} refcounted references", to_remove.len());
293 for key in to_remove {
294 table.remove(&key);
295 }
296}
297
298pub(crate) unsafe fn trace_refcounted_objects(tracer: *mut JSTracer) {
300 trace!("tracing live refcounted references");
301 LIVE_REFERENCES.with(|r| {
302 let live_references = &*r.borrow();
303 {
304 let mut table = live_references.reflectable_table.borrow_mut();
305 remove_nulls(&mut table);
306 for obj in table.keys() {
307 unsafe {
308 trace_reflector(tracer, "refcounted", &*(*obj as *const Reflector));
309 }
310 }
311 }
312
313 {
314 let table = live_references.promise_table.borrow_mut();
315 for promise in table.keys() {
316 unsafe {
317 trace_reflector(tracer, "refcounted", (**promise).reflector());
318 }
319 }
320 }
321 });
322}