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};
14
15use crate::assert::assert_in_script;
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 fn clone(&self) -> Self {
195 assert_in_script();
196 Dom { ptr: self.ptr }
197 }
198}
199
200impl<T: DomObject> Dom<T> {
201 pub fn from_ref(obj: &T) -> Dom<T> {
203 assert_in_script();
204 Dom {
205 ptr: ptr::NonNull::from(obj),
206 }
207 }
208
209 pub fn as_rooted(&self) -> DomRoot<T> {
211 DomRoot::from_ref(self)
212 }
213
214 pub fn as_ptr(&self) -> *const T {
215 self.ptr.as_ptr()
216 }
217}
218
219impl<T: DomObject> Deref for Dom<T> {
220 type Target = T;
221
222 fn deref(&self) -> &T {
223 assert_in_script();
224 unsafe { &*self.ptr.as_ptr() }
227 }
228}
229
230unsafe impl<T: DomObject> JSTraceable for Dom<T> {
231 unsafe fn trace(&self, tracer: *mut JSTracer) {
232 let trace_info = if cfg!(debug_assertions) {
233 std::any::type_name::<T>()
234 } else {
235 "DOM object on heap"
236 };
237 unsafe {
238 trace_reflector(tracer, trace_info, (*self.ptr.as_ptr()).reflector());
239 }
240 }
241}
242
243#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
245pub struct MaybeUnreflectedDom<T> {
246 ptr: ptr::NonNull<T>,
247}
248
249impl<T> MaybeUnreflectedDom<T>
250where
251 T: DomObject,
252{
253 pub unsafe fn from_box(value: Box<T>) -> Self {
258 Self {
259 ptr: Box::leak(value).into(),
260 }
261 }
262}
263
264impl<T> Root<MaybeUnreflectedDom<T>>
265where
266 T: DomObject,
267{
268 pub fn as_ptr(&self) -> *const T {
269 self.value.ptr.as_ptr()
270 }
271}
272
273impl<T> Root<MaybeUnreflectedDom<T>>
274where
275 T: MutDomObject,
276{
277 pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
282 let ptr = self.as_ptr();
283 drop(self);
284 let root = DomRoot::from_ref(unsafe { &*ptr });
285 unsafe { root.init_reflector::<T>(obj) };
286 root
287 }
288}
289
290pub type DomRoot<T> = Root<Dom<T>>;
292
293impl<T: Castable> DomRoot<T> {
294 pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
296 where
297 U: Castable,
298 T: DerivedFrom<U>,
299 {
300 unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
301 }
302
303 pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
305 where
306 U: DerivedFrom<T>,
307 {
308 if root.is::<U>() {
309 Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
310 } else {
311 None
312 }
313 }
314}
315
316impl<T: DomObject> DomRoot<T> {
317 pub fn from_ref(unrooted: &T) -> DomRoot<T> {
319 unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
320 }
321
322 pub fn as_traced(&self) -> Dom<T> {
329 Dom::from_ref(self)
330 }
331}
332
333impl<T> MallocSizeOf for DomRoot<T>
334where
335 T: DomObject + MallocSizeOf,
336{
337 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
338 0
339 }
340}
341
342impl<T> PartialEq for DomRoot<T>
343where
344 T: DomObject,
345{
346 fn eq(&self, other: &Self) -> bool {
347 self.value == other.value
348 }
349}
350
351impl<T: DomObject> Eq for DomRoot<T> {}
352
353impl<T: DomObject> Hash for DomRoot<T> {
354 fn hash<H: Hasher>(&self, state: &mut H) {
355 self.value.hash(state);
356 }
357}
358
359impl<T> Clone for DomRoot<T>
360where
361 T: DomObject,
362{
363 fn clone(&self) -> DomRoot<T> {
364 DomRoot::from_ref(self)
365 }
366}
367
368unsafe impl<T> JSTraceable for DomRoot<T>
369where
370 T: DomObject,
371{
372 unsafe fn trace(&self, _: *mut JSTracer) {
373 }
375}
376
377pub struct RootCollection {
384 roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
385}
386
387impl RootCollection {
388 #[expect(clippy::new_without_default)]
390 pub const fn new() -> RootCollection {
391 RootCollection {
392 roots: UnsafeCell::new(vec![]),
393 }
394 }
395
396 unsafe fn root(&self, object: *const dyn JSTraceable) {
398 assert_in_script();
399 unsafe { (*self.roots.get()).push(object) };
400 }
401
402 unsafe fn unroot(&self, object: *const dyn JSTraceable) {
404 assert_in_script();
405 let roots = unsafe { &mut *self.roots.get() };
406 match roots
407 .iter()
408 .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
409 {
410 Some(idx) => {
411 unsafe {
415 let len = roots.len() - 1;
416 if len != idx {
417 let base_ptr = roots.as_mut_ptr();
418 ptr::copy_nonoverlapping(base_ptr.add(len), base_ptr.add(idx), 1);
419 }
420 roots.set_len(len);
421 }
422 },
423 None => panic!("Can't remove a root that was never rooted!"),
424 }
425 }
426}
427
428thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
429
430pub unsafe fn trace_roots(tracer: *mut JSTracer) {
435 trace!("tracing stack roots");
436 STACK_ROOTS.with(|collection| {
437 let collection = unsafe { &*collection.roots.get() };
438 for root in collection {
439 unsafe {
440 (**root).trace(tracer);
441 }
442 }
443 });
444}
445
446pub trait DomSlice<T>
448where
449 T: JSTraceable + DomObject,
450{
451 fn r(&self) -> &[&T];
453}
454
455impl<T> DomSlice<T> for [Dom<T>]
456where
457 T: JSTraceable + DomObject,
458{
459 #[inline]
460 fn r(&self) -> &[&T] {
461 let _ = mem::transmute::<Dom<T>, &T>;
462 unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
463 }
464}
465
466pub fn rooted_heap_handle<'a, T: DomObject, U: GCMethods + Copy>(
471 object: &'a T,
472 f: impl Fn(&'a T) -> &'a Heap<U>,
473) -> Handle<'a, U> {
474 unsafe { Handle::from_raw(f(object).handle()) }
478}