script_bindings/
weakref.rs1use 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
29pub(crate) const DOM_WEAK_SLOT: u32 = 1;
34
35#[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#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
43pub struct WeakBox<T: WeakReferenceable> {
44 pub count: Cell<usize>,
47 pub value: Cell<Option<ptr::NonNull<T>>>,
49}
50
51pub trait WeakReferenceable: DomObject + Sized {
53 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 pub fn new(value: &T) -> Self {
97 value.downgrade()
98 }
99
100 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 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 }
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}