1use std::cell::UnsafeCell;
6use std::hash::{Hash, Hasher};
7use std::ops::Deref;
8use std::{fmt, mem, ptr};
9
10use js::gc::{Handle, Traceable as JSTraceable};
11use js::jsapi::{Heap, JSObject, JSTracer};
12use js::rust::GCMethods;
13use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
14use style::thread_state;
15
16use crate::conversions::DerivedFrom;
17use crate::inheritance::Castable;
18use crate::reflector::{DomObject, MutDomObject, Reflector};
19use crate::trace::trace_reflector;
20
21#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
23pub struct Root<T: StableTraceObject> {
24 value: T,
26 root_list: *const RootCollection,
28}
29
30impl<T> Root<T>
31where
32 T: StableTraceObject + 'static,
33{
34 pub unsafe fn new(value: T) -> Self {
40 unsafe fn add_to_root_list(object: *const dyn JSTraceable) -> *const RootCollection {
41 assert_in_script();
42 STACK_ROOTS.with(|root_list| {
43 unsafe { root_list.root(object) };
44 root_list as *const _
45 })
46 }
47
48 let root_list = unsafe { add_to_root_list(value.stable_trace_object()) };
49 Root { value, root_list }
50 }
51}
52
53pub unsafe trait StableTraceObject {
64 fn stable_trace_object(&self) -> *const dyn JSTraceable;
67}
68
69unsafe impl<T> StableTraceObject for Dom<T>
70where
71 T: DomObject,
72{
73 fn stable_trace_object(&self) -> *const dyn JSTraceable {
74 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
78 struct ReflectorStackRoot<T>(Reflector<T>);
79 unsafe impl<T> JSTraceable for ReflectorStackRoot<T> {
80 unsafe fn trace(&self, tracer: *mut JSTracer) {
81 unsafe { trace_reflector(tracer, "on stack", &self.0) };
82 }
83 }
84 unsafe {
85 &*(self.reflector() as *const Reflector<T::ReflectorType>
86 as *const ReflectorStackRoot<T::ReflectorType>)
87 }
88 }
89}
90
91unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T>
92where
93 T: DomObject,
94{
95 fn stable_trace_object(&self) -> *const dyn JSTraceable {
96 struct MaybeUnreflectedStackRoot<T>(T);
100 unsafe impl<T> JSTraceable for MaybeUnreflectedStackRoot<T>
101 where
102 T: DomObject,
103 {
104 unsafe fn trace(&self, tracer: *mut JSTracer) {
105 if self.0.reflector().get_jsobject().is_null() {
106 unsafe { self.0.trace(tracer) };
107 } else {
108 unsafe { trace_reflector(tracer, "on stack", self.0.reflector()) };
109 }
110 }
111 }
112 unsafe { &*(self.ptr.as_ptr() as *const T as *const MaybeUnreflectedStackRoot<T>) }
113 }
114}
115
116impl<T> Deref for Root<T>
117where
118 T: Deref + StableTraceObject,
119{
120 type Target = <T as Deref>::Target;
121
122 fn deref(&self) -> &Self::Target {
123 assert_in_script();
124 &self.value
125 }
126}
127
128impl<T> Drop for Root<T>
129where
130 T: StableTraceObject,
131{
132 fn drop(&mut self) {
133 unsafe {
134 (*self.root_list).unroot(self.value.stable_trace_object());
135 }
136 }
137}
138
139impl<T: fmt::Debug + StableTraceObject> fmt::Debug for Root<T> {
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141 self.value.fmt(f)
142 }
143}
144
145impl<T: fmt::Debug + DomObject> fmt::Debug for Dom<T> {
146 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147 (**self).fmt(f)
148 }
149}
150
151#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
159#[repr(transparent)]
160pub struct Dom<T> {
161 ptr: ptr::NonNull<T>,
162}
163
164impl<T> MallocSizeOf for Dom<T> {
167 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
168 0
169 }
170}
171
172impl<T> PartialEq for Dom<T> {
173 fn eq(&self, other: &Dom<T>) -> bool {
174 self.ptr.as_ptr() == other.ptr.as_ptr()
175 }
176}
177
178impl<'a, T: DomObject> PartialEq<&'a T> for Dom<T> {
179 fn eq(&self, other: &&'a T) -> bool {
180 *self == Dom::from_ref(*other)
181 }
182}
183
184impl<T> Eq for Dom<T> {}
185
186impl<T> Hash for Dom<T> {
187 fn hash<H: Hasher>(&self, state: &mut H) {
188 self.ptr.as_ptr().hash(state)
189 }
190}
191
192impl<T> Clone for Dom<T> {
193 #[inline]
194 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
195 fn clone(&self) -> Self {
196 assert_in_script();
197 Dom { ptr: self.ptr }
198 }
199}
200
201impl<T: DomObject> Dom<T> {
202 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
204 pub fn from_ref(obj: &T) -> Dom<T> {
205 assert_in_script();
206 Dom {
207 ptr: ptr::NonNull::from(obj),
208 }
209 }
210
211 pub fn as_rooted(&self) -> DomRoot<T> {
213 DomRoot::from_ref(self)
214 }
215
216 pub fn as_ptr(&self) -> *const T {
217 self.ptr.as_ptr()
218 }
219}
220
221impl<T: DomObject> Deref for Dom<T> {
222 type Target = T;
223
224 fn deref(&self) -> &T {
225 assert_in_script();
226 unsafe { &*self.ptr.as_ptr() }
229 }
230}
231
232unsafe impl<T: DomObject> JSTraceable for Dom<T> {
233 unsafe fn trace(&self, tracer: *mut JSTracer) {
234 let trace_info = if cfg!(debug_assertions) {
235 std::any::type_name::<T>()
236 } else {
237 "DOM object on heap"
238 };
239 unsafe {
240 trace_reflector(tracer, trace_info, (*self.ptr.as_ptr()).reflector());
241 }
242 }
243}
244
245#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
247pub struct MaybeUnreflectedDom<T> {
248 ptr: ptr::NonNull<T>,
249}
250
251impl<T> MaybeUnreflectedDom<T>
252where
253 T: DomObject,
254{
255 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
260 pub unsafe fn from_box(value: Box<T>) -> Self {
261 Self {
262 ptr: Box::leak(value).into(),
263 }
264 }
265}
266
267impl<T> Root<MaybeUnreflectedDom<T>>
268where
269 T: DomObject,
270{
271 pub fn as_ptr(&self) -> *const T {
272 self.value.ptr.as_ptr()
273 }
274}
275
276impl<T> Root<MaybeUnreflectedDom<T>>
277where
278 T: MutDomObject,
279{
280 pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
285 let ptr = self.as_ptr();
286 drop(self);
287 let root = DomRoot::from_ref(unsafe { &*ptr });
288 unsafe { root.init_reflector::<T>(obj) };
289 root
290 }
291}
292
293pub type DomRoot<T> = Root<Dom<T>>;
295
296impl<T: Castable> DomRoot<T> {
297 pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
299 where
300 U: Castable,
301 T: DerivedFrom<U>,
302 {
303 unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
304 }
305
306 pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
308 where
309 U: DerivedFrom<T>,
310 {
311 if root.is::<U>() {
312 Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
313 } else {
314 None
315 }
316 }
317}
318
319impl<T: DomObject> DomRoot<T> {
320 pub fn from_ref(unrooted: &T) -> DomRoot<T> {
322 unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
323 }
324
325 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
332 pub fn as_traced(&self) -> Dom<T> {
333 Dom::from_ref(self)
334 }
335}
336
337impl<T> MallocSizeOf for DomRoot<T>
338where
339 T: DomObject + MallocSizeOf,
340{
341 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
342 0
343 }
344}
345
346impl<T> PartialEq for DomRoot<T>
347where
348 T: DomObject,
349{
350 fn eq(&self, other: &Self) -> bool {
351 self.value == other.value
352 }
353}
354
355impl<T: DomObject> Eq for DomRoot<T> {}
356
357impl<T: DomObject> Hash for DomRoot<T> {
358 fn hash<H: Hasher>(&self, state: &mut H) {
359 self.value.hash(state);
360 }
361}
362
363impl<T> Clone for DomRoot<T>
364where
365 T: DomObject,
366{
367 fn clone(&self) -> DomRoot<T> {
368 DomRoot::from_ref(self)
369 }
370}
371
372unsafe impl<T> JSTraceable for DomRoot<T>
373where
374 T: DomObject,
375{
376 unsafe fn trace(&self, _: *mut JSTracer) {
377 }
379}
380
381pub struct RootCollection {
388 roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
389}
390
391impl RootCollection {
392 #[expect(clippy::new_without_default)]
394 pub const fn new() -> RootCollection {
395 RootCollection {
396 roots: UnsafeCell::new(vec![]),
397 }
398 }
399
400 unsafe fn root(&self, object: *const dyn JSTraceable) {
402 assert_in_script();
403 unsafe { (*self.roots.get()).push(object) };
404 }
405
406 unsafe fn unroot(&self, object: *const dyn JSTraceable) {
408 assert_in_script();
409 let roots = unsafe { &mut *self.roots.get() };
410 match roots
411 .iter()
412 .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
413 {
414 Some(idx) => {
415 unsafe {
419 let len = roots.len() - 1;
420 if len != idx {
421 let base_ptr = roots.as_mut_ptr();
422 ptr::copy_nonoverlapping(base_ptr.add(len), base_ptr.add(idx), 1);
423 }
424 roots.set_len(len);
425 }
426 },
427 None => panic!("Can't remove a root that was never rooted!"),
428 }
429 }
430}
431
432thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
433
434pub unsafe fn trace_roots(tracer: *mut JSTracer) {
439 trace!("tracing stack roots");
440 STACK_ROOTS.with(|collection| {
441 let collection = unsafe { &*collection.roots.get() };
442 for root in collection {
443 unsafe {
444 (**root).trace(tracer);
445 }
446 }
447 });
448}
449
450pub fn assert_in_script() {
451 debug_assert!(thread_state::get().is_script());
452}
453
454pub trait DomSlice<T>
456where
457 T: JSTraceable + DomObject,
458{
459 fn r(&self) -> &[&T];
461}
462
463impl<T> DomSlice<T> for [Dom<T>]
464where
465 T: JSTraceable + DomObject,
466{
467 #[inline]
468 fn r(&self) -> &[&T] {
469 let _ = mem::transmute::<Dom<T>, &T>;
470 unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
471 }
472}
473
474pub fn rooted_heap_handle<'a, T: DomObject, U: GCMethods + Copy>(
479 object: &'a T,
480 f: impl Fn(&'a T) -> &'a Heap<U>,
481) -> Handle<'a, U> {
482 unsafe { Handle::from_raw(f(object).handle()) }
486}