script_bindings/
refcounted.rs1use std::cell::RefCell;
26use std::collections::hash_map::Entry::{Occupied, Vacant};
27use std::hash::Hash;
28use std::marker::PhantomData;
29use std::sync::{Arc, Weak};
30
31use js::jsapi::JSTracer;
32use rustc_hash::FxHashMap;
33
34thread_local!(pub(super) static LIVE_DOM_REFERENCES: LiveDOMReferences =
35 LiveDOMReferences {
36 reflectable_table: RefCell::new(FxHashMap::default()),
37 }
38);
39
40use crate::root::DomRoot;
41use crate::trace::trace_reflector;
42use crate::{DomObject, Reflector};
43
44#[derive(MallocSizeOf)]
46struct TrustedReference(
47 #[ignore_malloc_size_of = "This is a shared reference."] *const libc::c_void,
48);
49unsafe impl Send for TrustedReference {}
50
51impl TrustedReference {
52 unsafe fn new(ptr: *const libc::c_void) -> TrustedReference {
56 TrustedReference(ptr)
57 }
58}
59
60#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
65#[derive(MallocSizeOf)]
66pub struct Trusted<T: DomObject> {
67 #[conditional_malloc_size_of]
70 refcount: Arc<TrustedReference>,
71 #[ignore_malloc_size_of = "These are shared by all `Trusted` types."]
72 owner_thread: *const LiveDOMReferences,
73 phantom: PhantomData<T>,
74}
75
76impl<T: DomObject> std::fmt::Debug for Trusted<T> {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
78 f.write_str("...")
79 }
80}
81
82unsafe impl<T: DomObject> Send for Trusted<T> {}
83
84impl<T: DomObject> Trusted<T> {
85 pub fn new(ptr: &T) -> Trusted<T> {
89 fn add_live_reference(
90 ptr: *const libc::c_void,
91 ) -> (Arc<TrustedReference>, *const LiveDOMReferences) {
92 LIVE_DOM_REFERENCES.with(|live_references| {
93 let refcount = unsafe { live_references.addref(ptr) };
94 (refcount, live_references as *const _)
95 })
96 }
97
98 let (refcount, owner_thread) = add_live_reference(ptr as *const T as *const _);
99 Trusted {
100 refcount,
101 owner_thread,
102 phantom: PhantomData,
103 }
104 }
105
106 pub fn root(&self) -> DomRoot<T> {
110 fn validate(owner_thread: *const LiveDOMReferences) {
111 assert!(
112 LIVE_DOM_REFERENCES.with(|live_references| { owner_thread == live_references })
113 );
114 }
115 validate(self.owner_thread);
116 unsafe { DomRoot::from_ref(&*(self.refcount.0 as *const T)) }
117 }
118}
119
120impl<T: DomObject> Clone for Trusted<T> {
121 fn clone(&self) -> Trusted<T> {
122 Trusted {
123 refcount: self.refcount.clone(),
124 owner_thread: self.owner_thread,
125 phantom: PhantomData,
126 }
127 }
128}
129
130pub struct LiveDOMReferences {
133 reflectable_table: RefCell<FxHashMap<*const libc::c_void, Weak<TrustedReference>>>,
135}
136
137pub unsafe fn trace_live_domreferences(tracer: *mut JSTracer) {
141 LIVE_DOM_REFERENCES.with(|live_references| {
142 let mut table = live_references.reflectable_table.borrow_mut();
143 remove_nulls(&mut table);
144 for obj in table.keys() {
145 unsafe {
146 trace_reflector(tracer, "refcounted", &*(*obj as *const Reflector));
147 }
148 }
149 })
150}
151
152impl LiveDOMReferences {
153 pub fn destruct() {
154 LIVE_DOM_REFERENCES.with(|live_references| {
155 let _ = live_references.reflectable_table.take();
156 });
157 }
158
159 #[expect(clippy::arc_with_non_send_sync)]
163 unsafe fn addref(&self, ptr: *const libc::c_void) -> Arc<TrustedReference> {
164 let mut table = self.reflectable_table.borrow_mut();
165 let capacity = table.capacity();
166 let len = table.len();
167 if (0 < capacity) && (capacity <= len) {
168 trace!("growing refcounted references by {}", len);
169 remove_nulls(&mut table);
170 table.reserve(len);
171 }
172 match table.entry(ptr) {
173 Occupied(mut entry) => match entry.get().upgrade() {
174 Some(refcount) => refcount,
175 None => {
176 let refcount = Arc::new(unsafe { TrustedReference::new(ptr) });
177 entry.insert(Arc::downgrade(&refcount));
178 refcount
179 },
180 },
181 Vacant(entry) => {
182 let refcount = Arc::new(unsafe { TrustedReference::new(ptr) });
183 entry.insert(Arc::downgrade(&refcount));
184 refcount
185 },
186 }
187 }
188}
189
190fn remove_nulls<K: Eq + Hash + Clone, V>(table: &mut FxHashMap<K, Weak<V>>) {
192 let to_remove: Vec<K> = table
193 .iter()
194 .filter(|&(_, value)| Weak::upgrade(value).is_none())
195 .map(|(key, _)| key.clone())
196 .collect();
197 trace!("removing {} refcounted references", to_remove.len());
198 for key in to_remove {
199 table.remove(&key);
200 }
201}
202
203unsafe impl<T: DomObject> crate::JSTraceable for Trusted<T> {
204 #[inline]
205 unsafe fn trace(&self, _: *mut JSTracer) {
206 }
208}