1use std::cell::UnsafeCell;
6use std::hash::{Hash, Hasher};
7use std::ops::Deref;
8use std::{fmt, mem, ptr};
9
10use js::gc::Traceable as JSTraceable;
11use js::jsapi::{JSObject, JSTracer};
12use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
13use style::thread_state;
14
15use crate::conversions::DerivedFrom;
16use crate::inheritance::Castable;
17use crate::reflector::{DomObject, MutDomObject, Reflector};
18use crate::trace::trace_reflector;
19
20#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
22pub struct Root<T: StableTraceObject> {
23 value: T,
25 root_list: *const RootCollection,
27}
28
29impl<T> Root<T>
30where
31 T: StableTraceObject + 'static,
32{
33 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
39 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, allow(crown::unrooted_must_root))]
78 struct ReflectorStackRoot(Reflector);
79 unsafe impl JSTraceable for ReflectorStackRoot {
80 unsafe fn trace(&self, tracer: *mut JSTracer) {
81 unsafe { trace_reflector(tracer, "on stack", &self.0) };
82 }
83 }
84 unsafe { &*(self.reflector() as *const Reflector as *const ReflectorStackRoot) }
85 }
86}
87
88unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T>
89where
90 T: DomObject,
91{
92 fn stable_trace_object(&self) -> *const dyn JSTraceable {
93 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
97 struct MaybeUnreflectedStackRoot<T>(T);
98 unsafe impl<T> JSTraceable for MaybeUnreflectedStackRoot<T>
99 where
100 T: DomObject,
101 {
102 unsafe fn trace(&self, tracer: *mut JSTracer) {
103 if self.0.reflector().get_jsobject().is_null() {
104 unsafe { self.0.trace(tracer) };
105 } else {
106 unsafe { trace_reflector(tracer, "on stack", self.0.reflector()) };
107 }
108 }
109 }
110 unsafe { &*(self.ptr.as_ptr() as *const T as *const MaybeUnreflectedStackRoot<T>) }
111 }
112}
113
114impl<T> Deref for Root<T>
115where
116 T: Deref + StableTraceObject,
117{
118 type Target = <T as Deref>::Target;
119
120 fn deref(&self) -> &Self::Target {
121 assert_in_script();
122 &self.value
123 }
124}
125
126impl<T> Drop for Root<T>
127where
128 T: StableTraceObject,
129{
130 fn drop(&mut self) {
131 unsafe {
132 (*self.root_list).unroot(self.value.stable_trace_object());
133 }
134 }
135}
136
137impl<T: fmt::Debug + StableTraceObject> fmt::Debug for Root<T> {
138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 self.value.fmt(f)
140 }
141}
142
143impl<T: fmt::Debug + DomObject> fmt::Debug for Dom<T> {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 (**self).fmt(f)
146 }
147}
148
149#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
157#[repr(transparent)]
158pub struct Dom<T> {
159 ptr: ptr::NonNull<T>,
160}
161
162impl<T> MallocSizeOf for Dom<T> {
165 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
166 0
167 }
168}
169
170impl<T> PartialEq for Dom<T> {
171 fn eq(&self, other: &Dom<T>) -> bool {
172 self.ptr.as_ptr() == other.ptr.as_ptr()
173 }
174}
175
176impl<'a, T: DomObject> PartialEq<&'a T> for Dom<T> {
177 fn eq(&self, other: &&'a T) -> bool {
178 *self == Dom::from_ref(*other)
179 }
180}
181
182impl<T> Eq for Dom<T> {}
183
184impl<T> Hash for Dom<T> {
185 fn hash<H: Hasher>(&self, state: &mut H) {
186 self.ptr.as_ptr().hash(state)
187 }
188}
189
190impl<T> Clone for Dom<T> {
191 #[inline]
192 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
193 fn clone(&self) -> Self {
194 assert_in_script();
195 Dom { ptr: self.ptr }
196 }
197}
198
199impl<T: DomObject> Dom<T> {
200 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
202 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 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
258 pub unsafe fn from_box(value: Box<T>) -> Self {
259 Self {
260 ptr: Box::leak(value).into(),
261 }
262 }
263}
264
265impl<T> Root<MaybeUnreflectedDom<T>>
266where
267 T: DomObject,
268{
269 pub fn as_ptr(&self) -> *const T {
270 self.value.ptr.as_ptr()
271 }
272}
273
274impl<T> Root<MaybeUnreflectedDom<T>>
275where
276 T: MutDomObject,
277{
278 pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> {
283 let ptr = self.as_ptr();
284 drop(self);
285 let root = DomRoot::from_ref(unsafe { &*ptr });
286 unsafe { root.init_reflector(obj) };
287 root
288 }
289}
290
291pub type DomRoot<T> = Root<Dom<T>>;
293
294impl<T: Castable> DomRoot<T> {
295 pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
297 where
298 U: Castable,
299 T: DerivedFrom<U>,
300 {
301 unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) }
302 }
303
304 pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
306 where
307 U: DerivedFrom<T>,
308 {
309 if root.is::<U>() {
310 Some(unsafe { mem::transmute::<DomRoot<T>, DomRoot<U>>(root) })
311 } else {
312 None
313 }
314 }
315}
316
317impl<T: DomObject> DomRoot<T> {
318 pub fn from_ref(unrooted: &T) -> DomRoot<T> {
320 unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
321 }
322
323 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
330 pub fn as_traced(&self) -> Dom<T> {
331 Dom::from_ref(self)
332 }
333}
334
335impl<T> MallocSizeOf for DomRoot<T>
336where
337 T: DomObject + MallocSizeOf,
338{
339 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
340 0
341 }
342}
343
344impl<T> PartialEq for DomRoot<T>
345where
346 T: DomObject,
347{
348 fn eq(&self, other: &Self) -> bool {
349 self.value == other.value
350 }
351}
352
353impl<T: DomObject> Eq for DomRoot<T> {}
354
355impl<T: DomObject> Hash for DomRoot<T> {
356 fn hash<H: Hasher>(&self, state: &mut H) {
357 self.value.hash(state);
358 }
359}
360
361impl<T> Clone for DomRoot<T>
362where
363 T: DomObject,
364{
365 fn clone(&self) -> DomRoot<T> {
366 DomRoot::from_ref(self)
367 }
368}
369
370unsafe impl<T> JSTraceable for DomRoot<T>
371where
372 T: DomObject,
373{
374 unsafe fn trace(&self, _: *mut JSTracer) {
375 }
377}
378
379pub struct RootCollection {
386 roots: UnsafeCell<Vec<*const dyn JSTraceable>>,
387}
388
389impl RootCollection {
390 #[allow(clippy::new_without_default)]
392 pub const fn new() -> RootCollection {
393 RootCollection {
394 roots: UnsafeCell::new(vec![]),
395 }
396 }
397
398 unsafe fn root(&self, object: *const dyn JSTraceable) {
400 assert_in_script();
401 unsafe { (*self.roots.get()).push(object) };
402 }
403
404 unsafe fn unroot(&self, object: *const dyn JSTraceable) {
406 assert_in_script();
407 let roots = unsafe { &mut *self.roots.get() };
408 match roots
409 .iter()
410 .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ()))
411 {
412 Some(idx) => {
413 roots.swap_remove(idx);
414 },
415 None => panic!("Can't remove a root that was never rooted!"),
416 }
417 }
418}
419
420thread_local!(pub static STACK_ROOTS: RootCollection = const { RootCollection::new() });
421
422pub unsafe fn trace_roots(tracer: *mut JSTracer) {
427 trace!("tracing stack roots");
428 STACK_ROOTS.with(|collection| {
429 let collection = unsafe { &*collection.roots.get() };
430 for root in collection {
431 unsafe {
432 (**root).trace(tracer);
433 }
434 }
435 });
436}
437
438pub fn assert_in_script() {
439 debug_assert!(thread_state::get().is_script());
440}
441
442pub trait DomSlice<T>
444where
445 T: JSTraceable + DomObject,
446{
447 fn r(&self) -> &[&T];
449}
450
451impl<T> DomSlice<T> for [Dom<T>]
452where
453 T: JSTraceable + DomObject,
454{
455 #[inline]
456 fn r(&self) -> &[&T] {
457 let _ = mem::transmute::<Dom<T>, &T>;
458 unsafe { &*(self as *const [Dom<T>] as *const [&T]) }
459 }
460}