script_bindings/
weakref.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Weak-referenceable JS-managed DOM objects.
6//!
7//! IDL interfaces marked as `weakReferenceable` in `Bindings.conf`
8//! automatically implement the `WeakReferenceable` trait in codegen.
9//! The instance object is responsible for setting `None` in its own
10//! own `WeakBox` when it is collected, through the `DOM_WEAK_SLOT`
11//! slot. When all associated `WeakRef` values are dropped, the
12//! `WeakBox` itself is dropped too.
13
14use std::cell::Cell;
15use std::hash::{Hash, Hasher};
16use std::ops::Drop;
17use std::{mem, ptr};
18
19use js::glue::JS_GetReservedSlot;
20use js::jsapi::{JS_SetReservedSlot, JSTracer};
21use js::jsval::{PrivateValue, UndefinedValue};
22use libc::c_void;
23use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
24
25use crate::JSTraceable;
26use crate::reflector::DomObject;
27use crate::root::DomRoot;
28
29/// The index of the slot wherein a pointer to the weak holder cell is
30/// stored for weak-referenceable bindings. We use slot 1 for holding it,
31/// this is unsafe for globals, we disallow weak-referenceable globals
32/// directly in codegen.
33pub(crate) const DOM_WEAK_SLOT: u32 = 1;
34
35/// A weak reference to a JS-managed DOM object.
36#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
37pub struct WeakRef<T: WeakReferenceable> {
38    ptr: ptr::NonNull<WeakBox<T>>,
39}
40
41/// The inner box of weak references, public for the finalization in codegen.
42#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
43pub struct WeakBox<T: WeakReferenceable> {
44    /// The reference count. When it reaches zero, the `value` field should
45    /// have already been set to `None`. The pointee contributes one to the count.
46    pub count: Cell<usize>,
47    /// The pointer to the JS-managed object, set to None when it is collected.
48    pub value: Cell<Option<ptr::NonNull<T>>>,
49}
50
51/// Trait implemented by weak-referenceable interfaces.
52pub trait WeakReferenceable: DomObject + Sized {
53    /// Downgrade a DOM object reference to a weak one.
54    fn downgrade(&self) -> WeakRef<Self> {
55        unsafe {
56            let object = self.reflector().get_jsobject().get();
57            let mut slot = UndefinedValue();
58            JS_GetReservedSlot(object, DOM_WEAK_SLOT, &mut slot);
59            let mut ptr = slot.to_private() as *mut WeakBox<Self>;
60            if ptr.is_null() {
61                trace!("Creating new WeakBox holder for {:p}.", self);
62                ptr = Box::into_raw(Box::new(WeakBox {
63                    count: Cell::new(1),
64                    value: Cell::new(Some(ptr::NonNull::from(self))),
65                }));
66                let val = PrivateValue(ptr as *const c_void);
67                JS_SetReservedSlot(object, DOM_WEAK_SLOT, &val);
68            }
69            let box_ = &*ptr;
70            assert!(box_.value.get().is_some());
71            let new_count = box_.count.get() + 1;
72            trace!(
73                "Incrementing WeakBox refcount for {:p} to {}.",
74                self, new_count
75            );
76            box_.count.set(new_count);
77            WeakRef {
78                ptr: ptr::NonNull::new_unchecked(ptr),
79            }
80        }
81    }
82}
83
84impl<T: WeakReferenceable> Eq for WeakRef<T> {}
85
86impl<T: WeakReferenceable> Hash for WeakRef<T> {
87    fn hash<H: Hasher>(&self, state: &mut H) {
88        self.ptr.hash(state);
89    }
90}
91
92impl<T: WeakReferenceable> WeakRef<T> {
93    /// Create a new weak reference from a `WeakReferenceable` interface instance.
94    /// This is just a convenience wrapper around `<T as WeakReferenceable>::downgrade`
95    /// to not have to import `WeakReferenceable`.
96    pub fn new(value: &T) -> Self {
97        value.downgrade()
98    }
99
100    /// DomRoot a weak reference. Returns `None` if the object was already collected.
101    pub fn root(&self) -> Option<DomRoot<T>> {
102        unsafe { &*self.ptr.as_ptr() }
103            .value
104            .get()
105            .map(|ptr| unsafe { DomRoot::from_ref(&*ptr.as_ptr()) })
106    }
107
108    /// Return whether the weakly-referenced object is still alive.
109    pub fn is_alive(&self) -> bool {
110        unsafe { &*self.ptr.as_ptr() }.value.get().is_some()
111    }
112}
113
114impl<T: WeakReferenceable> Clone for WeakRef<T> {
115    fn clone(&self) -> WeakRef<T> {
116        unsafe {
117            let box_ = &*self.ptr.as_ptr();
118            let new_count = box_.count.get() + 1;
119            box_.count.set(new_count);
120            WeakRef { ptr: self.ptr }
121        }
122    }
123}
124
125impl<T: WeakReferenceable> MallocSizeOf for WeakRef<T> {
126    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
127        0
128    }
129}
130
131impl<T: WeakReferenceable> PartialEq for WeakRef<T> {
132    fn eq(&self, other: &Self) -> bool {
133        unsafe {
134            (*self.ptr.as_ptr()).value.get().map(ptr::NonNull::as_ptr) ==
135                (*other.ptr.as_ptr()).value.get().map(ptr::NonNull::as_ptr)
136        }
137    }
138}
139
140impl<T: WeakReferenceable> PartialEq<T> for WeakRef<T> {
141    fn eq(&self, other: &T) -> bool {
142        unsafe {
143            match self.ptr.as_ref().value.get() {
144                Some(ptr) => ptr::eq(ptr.as_ptr(), other),
145                None => false,
146            }
147        }
148    }
149}
150
151unsafe impl<T: WeakReferenceable> JSTraceable for WeakRef<T> {
152    unsafe fn trace(&self, _: *mut JSTracer) {
153        // Do nothing.
154    }
155}
156
157impl<T: WeakReferenceable> Drop for WeakRef<T> {
158    fn drop(&mut self) {
159        unsafe {
160            let (count, value) = {
161                let weak_box = &*self.ptr.as_ptr();
162                assert!(weak_box.count.get() > 0);
163                let count = weak_box.count.get() - 1;
164                weak_box.count.set(count);
165                (count, weak_box.value.get())
166            };
167            if count == 0 {
168                assert!(value.is_none());
169                mem::drop(Box::from_raw(self.ptr.as_ptr()));
170            }
171        }
172    }
173}