use std::cell::Cell;
use std::hash::{Hash, Hasher};
use std::ops::Drop;
use std::{mem, ptr};
use js::glue::JS_GetReservedSlot;
use js::jsapi::{JS_SetReservedSlot, JSTracer};
use js::jsval::{PrivateValue, UndefinedValue};
use libc::c_void;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use crate::JSTraceable;
use crate::reflector::DomObject;
use crate::root::DomRoot;
pub(crate) const DOM_WEAK_SLOT: u32 = 1;
#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
pub struct WeakRef<T: WeakReferenceable> {
ptr: ptr::NonNull<WeakBox<T>>,
}
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub struct WeakBox<T: WeakReferenceable> {
pub count: Cell<usize>,
pub value: Cell<Option<ptr::NonNull<T>>>,
}
pub trait WeakReferenceable: DomObject + Sized {
fn downgrade(&self) -> WeakRef<Self> {
unsafe {
let object = self.reflector().get_jsobject().get();
let mut slot = UndefinedValue();
JS_GetReservedSlot(object, DOM_WEAK_SLOT, &mut slot);
let mut ptr = slot.to_private() as *mut WeakBox<Self>;
if ptr.is_null() {
trace!("Creating new WeakBox holder for {:p}.", self);
ptr = Box::into_raw(Box::new(WeakBox {
count: Cell::new(1),
value: Cell::new(Some(ptr::NonNull::from(self))),
}));
let val = PrivateValue(ptr as *const c_void);
JS_SetReservedSlot(object, DOM_WEAK_SLOT, &val);
}
let box_ = &*ptr;
assert!(box_.value.get().is_some());
let new_count = box_.count.get() + 1;
trace!(
"Incrementing WeakBox refcount for {:p} to {}.",
self, new_count
);
box_.count.set(new_count);
WeakRef {
ptr: ptr::NonNull::new_unchecked(ptr),
}
}
}
}
impl<T: WeakReferenceable> Eq for WeakRef<T> {}
impl<T: WeakReferenceable> Hash for WeakRef<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.ptr.hash(state);
}
}
impl<T: WeakReferenceable> WeakRef<T> {
pub fn new(value: &T) -> Self {
value.downgrade()
}
pub fn root(&self) -> Option<DomRoot<T>> {
unsafe { &*self.ptr.as_ptr() }
.value
.get()
.map(|ptr| unsafe { DomRoot::from_ref(&*ptr.as_ptr()) })
}
pub fn is_alive(&self) -> bool {
unsafe { &*self.ptr.as_ptr() }.value.get().is_some()
}
}
impl<T: WeakReferenceable> Clone for WeakRef<T> {
fn clone(&self) -> WeakRef<T> {
unsafe {
let box_ = &*self.ptr.as_ptr();
let new_count = box_.count.get() + 1;
box_.count.set(new_count);
WeakRef { ptr: self.ptr }
}
}
}
impl<T: WeakReferenceable> MallocSizeOf for WeakRef<T> {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}
impl<T: WeakReferenceable> PartialEq for WeakRef<T> {
fn eq(&self, other: &Self) -> bool {
unsafe {
(*self.ptr.as_ptr()).value.get().map(ptr::NonNull::as_ptr) ==
(*other.ptr.as_ptr()).value.get().map(ptr::NonNull::as_ptr)
}
}
}
impl<T: WeakReferenceable> PartialEq<T> for WeakRef<T> {
fn eq(&self, other: &T) -> bool {
unsafe {
match self.ptr.as_ref().value.get() {
Some(ptr) => ptr::eq(ptr.as_ptr(), other),
None => false,
}
}
}
}
unsafe impl<T: WeakReferenceable> JSTraceable for WeakRef<T> {
unsafe fn trace(&self, _: *mut JSTracer) {
}
}
impl<T: WeakReferenceable> Drop for WeakRef<T> {
fn drop(&mut self) {
unsafe {
let (count, value) = {
let weak_box = &*self.ptr.as_ptr();
assert!(weak_box.count.get() > 0);
let count = weak_box.count.get() - 1;
weak_box.count.set(count);
(count, weak_box.value.get())
};
if count == 0 {
assert!(value.is_none());
mem::drop(Box::from_raw(self.ptr.as_ptr()));
}
}
}
}