1use std::cell::UnsafeCell;
6use std::hash::{Hash, Hasher};
7use std::ops::Deref;
8use std::rc::Rc;
9use std::{fmt, mem, ptr};
10
11use js::context::NoGC;
12use js::gc::{Handle, Traceable as JSTraceable};
13use js::jsapi::{Heap, JSObject, JSTracer};
14use js::rust::GCMethods;
15use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
16
17use crate::assert::assert_in_script;
18use crate::conversions::DerivedFrom;
19use crate::dom::UnrootedDom;
20use crate::inheritance::Castable;
21use crate::reflector::{DomObject, MutDomObject};
22use crate::trace::trace_reflector;
23
24#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
26pub struct Root<T: StableTraceObject> {
27 value: T,
29 root_list: *const RootCollection,
31}
32
33impl<T> Root<T>
34where
35 T: StableTraceObject + 'static,
36{
37 pub unsafe fn new(value: T) -> Self {
43 unsafe fn add_to_root_list(object: *const dyn JSTraceable) -> *const RootCollection {
44 assert_in_script();
45 STACK_ROOTS.with(|root_list| {
46 unsafe { root_list.root(object) };
47 root_list as *const _
48 })
49 }
50
51 let root_list = unsafe { add_to_root_list(value.stable_trace_object()) };
52 Root { value, root_list }
53 }
54}
55
56pub unsafe trait StableTraceObject {
67 fn stable_trace_object(&self) -> *const dyn JSTraceable;
70}
71
72unsafe impl<T> StableTraceObject for Dom<T>
73where
74 T: DomObject,
75{
76 fn stable_trace_object(&self) -> *const dyn JSTraceable {
77 self.reflector()
78 }
79}
80
81unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T>
82where
83 T: DomObject,
84{
85 fn stable_trace_object(&self) -> *const dyn JSTraceable {
86 unsafe { self.ptr.as_ref().reflector() }
87 }
88}
89
90impl<T> Deref for Root<T>
91where
92 T: Deref + StableTraceObject,
93{
94 type Target = <T as Deref>::Target;
95
96 fn deref(&self) -> &Self::Target {
97 assert_in_script();
98 &self.value
99 }
100}
101
102impl<T> Drop for Root<T>
103where
104 T: StableTraceObject,
105{
106 fn drop(&mut self) {
107 unsafe {
108 (*self.root_list).unroot(self.value.stable_trace_object());
109 }
110 }
111}
112
113impl<T: fmt::Debug + StableTraceObject> fmt::Debug for Root<T> {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 self.value.fmt(f)
116 }
117}
118
119impl<T: fmt::Debug + DomObject> fmt::Debug for Dom<T> {
120 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121 (**self).fmt(f)
122 }
123}
124
125#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
133#[repr(transparent)]
134pub struct Dom<T> {
135 ptr: ptr::NonNull<T>,
136}
137
138impl<T> MallocSizeOf for Dom<T> {
141 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
142 0
143 }
144}
145
146impl<T> PartialEq for Dom<T> {
148 fn eq(&self, other: &Dom<T>) -> bool {
149 self.ptr.as_ptr() == other.ptr.as_ptr()
150 }
151}
152
153impl<'a, T: DomObject> PartialEq<&'a T> for Dom<T> {
155 fn eq(&self, other: &&'a T) -> bool {
156 *self == Dom::from_ref(*other)
157 }
158}
159
160impl<T> Eq for Dom<T> {}
161
162impl<T> Hash for Dom<T> {
164 fn hash<H: Hasher>(&self, state: &mut H) {
165 self.ptr.as_ptr().hash(state)
166 }
167}
168
169impl<T> Clone for Dom<T> {
170 #[inline]
171 fn clone(&self) -> Self {
172 assert_in_script();
173 Dom { ptr: self.ptr }
174 }
175}
176
177impl<T: DomObject> Dom<T> {
178 pub fn from_ref(obj: &T) -> Dom<T> {
180 assert_in_script();
181 Dom {
182 ptr: ptr::NonNull::from(obj),
183 }
184 }
185
186 pub fn as_rooted(&self) -> DomRoot<T> {
188 DomRoot::from_ref(self)
189 }
190
191 pub fn as_unrooted<'no_gc>(&self, no_gc: &'no_gc NoGC) -> UnrootedDom<'no_gc, T> {
194 UnrootedDom::from_dom(self.clone(), no_gc)
195 }
196
197 pub fn as_ptr(&self) -> *const T {
198 self.ptr.as_ptr()
199 }
200}
201
202impl<T: DomObject> Deref for Dom<T> {
203 type Target = T;
204
205 fn deref(&self) -> &T {
206 assert_in_script();
207 unsafe { &*self.ptr.as_ptr() }
210 }
211}
212
213unsafe impl<T: DomObject> JSTraceable for Dom<T> {
214 unsafe fn trace(&self, tracer: *mut JSTracer) {
215 let trace_info = if cfg!(debug_assertions) {
216 std::any::type_name::<T>()
217 } else {
218 "DOM object on heap"
219 };
220 unsafe {
221 trace_reflector(tracer, trace_info, (*self.ptr.as_ptr()).reflector());
222 }
223 }
224}
225
226#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
228pub struct MaybeUnreflectedDom<T> {
229 ptr: ptr::NonNull<T>,
230}
231
232impl<T> MaybeUnreflectedDom<T>
233where
234 T: DomObject,
235{
236 pub unsafe fn from_box(value: Box<T>) -> Self {
241 Self {
242 ptr: Box::leak(value).into(),
243 }
244 }
245
246 pub unsafe fn from_rc(value: Rc<T>) -> Self {
251 Self {
252 ptr: ptr::NonNull::new(Rc::into_raw(value) as *mut T).unwrap(),
253 }
254 }
255}
256
257impl<T> Root<MaybeUnreflectedDom<T>>
258where
259 T: DomObject,
260{
261 pub fn as_ptr(&self) -> *const T {
262 self.value.ptr.as_ptr()
263 }
264}
265
266impl<T> Root<MaybeUnreflectedDom<T>>
267where
268 T: MutDomObject,
269{
270 pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
275 let ptr = self.as_ptr();
276 drop(self);
277 let root = DomRoot::from_ref(unsafe { &*ptr });
278 unsafe { root.init_reflector::<T>(obj) };
279 root
280 }
281}
282
283pub type DomRoot<T> = Root<Dom<T>>;
285
286impl<T: Castable> DomRoot<T> {
287 pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
289 where
290 U: Castable,
291 T: DerivedFrom<U>,
292 {
293 unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
294 }
295
296 pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
298 where
299 U: DerivedFrom<T>,
300 {
301 if root.is::<U>() {
302 Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
303 } else {
304 None
305 }
306 }
307}
308
309impl<T: DomObject> DomRoot<T> {
310 pub fn from_ref(unrooted: &T) -> DomRoot<T> {
312 unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
313 }
314
315 pub fn as_traced(&self) -> Dom<T> {
322 Dom::from_ref(self)
323 }
324
325 pub fn as_unrooted<'no_gc>(&self, no_gc: &'no_gc NoGC) -> UnrootedDom<'no_gc, T> {
328 self.value.as_unrooted(no_gc)
329 }
330}
331
332impl<T> MallocSizeOf for DomRoot<T>
333where
334 T: DomObject + MallocSizeOf,
335{
336 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
337 0
338 }
339}
340
341impl<T> PartialEq for DomRoot<T>
342where
343 T: DomObject,
344{
345 fn eq(&self, other: &Self) -> bool {
346 self.value == other.value
347 }
348}
349
350impl<T: DomObject> Eq for DomRoot<T> {}
351
352impl<T: DomObject> Hash for DomRoot<T> {
353 fn hash<H: Hasher>(&self, state: &mut H) {
354 self.value.hash(state);
355 }
356}
357
358impl<T> Clone for DomRoot<T>
359where
360 T: DomObject,
361{
362 fn clone(&self) -> DomRoot<T> {
363 DomRoot::from_ref(self)
364 }
365}
366
367unsafe impl<T> JSTraceable for DomRoot<T>
368where
369 T: DomObject,
370{
371 unsafe fn trace(&self, _: *mut JSTracer) {
372 }
374}
375
376pub struct RootCollection {
383 roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
384}
385
386impl RootCollection {
387 #[expect(clippy::new_without_default)]
389 pub const fn new() -> RootCollection {
390 RootCollection {
391 roots: UnsafeCell::new(vec![]),
392 }
393 }
394
395 unsafe fn root(&self, object: *const dyn JSTraceable) {
397 assert_in_script();
398 unsafe { (*self.roots.get()).push(object) };
399 }
400
401 unsafe fn unroot(&self, object: *const dyn JSTraceable) {
403 assert_in_script();
404 let roots = unsafe { &mut *self.roots.get() };
405 match roots
406 .iter()
407 .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
408 {
409 Some(idx) => {
410 unsafe {
414 let len = roots.len() - 1;
415 if len != idx {
416 let base_ptr = roots.as_mut_ptr();
417 ptr::copy_nonoverlapping(base_ptr.add(len), base_ptr.add(idx), 1);
418 }
419 roots.set_len(len);
420 }
421 },
422 None => panic!("Can't remove a root that was never rooted!"),
423 }
424 }
425}
426
427thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
428
429pub unsafe fn trace_roots(tracer: *mut JSTracer) {
434 trace!("tracing stack roots");
435 STACK_ROOTS.with(|collection| {
436 let collection = unsafe { &*collection.roots.get() };
437 for root in collection {
438 unsafe {
439 (**root).trace(tracer);
440 }
441 }
442 });
443}
444
445pub trait DomSlice<T>
447where
448 T: JSTraceable + DomObject,
449{
450 fn r(&self) -> &[&T];
452}
453
454impl<T> DomSlice<T> for [Dom<T>]
455where
456 T: JSTraceable + DomObject,
457{
458 #[inline]
459 fn r(&self) -> &[&T] {
460 let _ = mem::transmute::<Dom<T>, &T>;
461 unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
462 }
463}
464
465pub fn rooted_heap_handle<'a, T: DomObject, U: GCMethods + Copy>(
470 object: &'a T,
471 f: impl Fn(&'a T) -> &'a Heap<U>,
472) -> Handle<'a, U> {
473 unsafe { Handle::from_raw(f(object).handle()) }
477}