pub use std::cell::{Ref, RefMut};
use std::cell::{RefCell, UnsafeCell};
use mozjs_sys::trace::Traceable;
use crate::context::NoGC;
use crate::jsapi::JSTracer;
#[derive(Clone, Debug, Default)]
pub struct JSRefCell<T> {
value: RefCell<T>,
}
impl<T> JSRefCell<T> {
pub fn new(value: T) -> JSRefCell<T> {
JSRefCell {
value: RefCell::new(value),
}
}
pub fn as_refcell(&self) -> &RefCell<T> {
&self.value
}
#[track_caller]
pub fn borrow(&self) -> Ref<'_, T> {
self.value.borrow()
}
#[track_caller]
pub fn borrow_mut<'a: 'r, 'no_cx: 'r, 'r>(&'a self, _no_gc: &'no_cx NoGC) -> RefMut<'r, T> {
self.value.borrow_mut()
}
}
impl<T: Default> JSRefCell<T> {
pub fn take(&self) -> T {
self.value.take()
}
}
unsafe impl<T: Traceable> Traceable for JSRefCell<T> {
unsafe fn trace(&self, trc: *mut JSTracer) {
unsafe { (*self).borrow().trace(trc) };
}
}
#[derive(Debug)]
pub struct JSCell<T> {
inner: UnsafeCell<T>,
}
impl<T> JSCell<T> {
pub fn new(val: T) -> Self {
JSCell {
inner: UnsafeCell::new(val),
}
}
pub fn set<'a, 'cx>(&'a self, _exclusive: &'cx mut NoGC, val: T) {
unsafe { *self.inner.get() = val }
}
pub fn borrow_mut<'a: 'r, 'cx: 'r, 'r>(&'a self, _exclusive: &'cx mut NoGC) -> &'r mut T {
unsafe { &mut *self.inner.get() }
}
pub fn borrow<'a: 'r, 'no_cx: 'r, 'r>(&'a self, _no_gc: &'no_cx NoGC) -> &'r T {
unsafe { &*self.inner.get() }
}
}
unsafe impl<T: Traceable> Traceable for JSCell<T> {
unsafe fn trace(&self, trc: *mut JSTracer) {
unsafe { (&*self.inner.get()).trace(trc) };
}
}
pub fn two_cells_borrow_mut<'c1, 'c2, 'cx, 'r1, 'r2, T1, T2>(
cell1: &'c1 JSCell<T1>,
cell2: &'c2 JSCell<T2>,
_exclusive: &'cx mut NoGC,
) -> (&'r1 mut T1, &'r2 mut T2)
where
'c1: 'r1,
'c2: 'r2,
'cx: 'r1,
'cx: 'r2,
{
let c1 = cell1.inner.get();
let c2 = cell2.inner.get();
assert_ne!(
c1 as *const _, c2 as *const _,
"Cannot mutably borrow the same cell multiple times at the same time"
);
unsafe { (&mut *cell1.inner.get(), &mut *cell2.inner.get()) }
}
pub fn three_cells_borrow_mut<'c1, 'c2, 'c3, 'cx, 'r1, 'r2, 'r3, T1, T2, T3>(
cell1: &'c1 JSCell<T1>,
cell2: &'c2 JSCell<T2>,
cell3: &'c3 JSCell<T3>,
_exclusive: &'cx mut NoGC,
) -> (&'r1 mut T1, &'r2 mut T2, &'r3 mut T3)
where
'c1: 'r1,
'c2: 'r2,
'c3: 'r3,
'cx: 'r1,
'cx: 'r2,
'cx: 'r3,
{
let c1 = cell1.inner.get();
let c2 = cell2.inner.get();
let c3 = cell3.inner.get();
assert_ne!(
c1 as *const _, c2 as *const _,
"Cannot mutably borrow the same cell multiple times at the same time"
);
assert_ne!(
c1 as *const _, c3 as *const _,
"Cannot mutably borrow the same cell multiple times at the same time"
);
assert_ne!(
c2 as *const _, c3 as *const _,
"Cannot mutably borrow the same cell multiple times at the same time"
);
unsafe {
(
&mut *cell1.inner.get(),
&mut *cell2.inner.get(),
&mut *cell3.inner.get(),
)
}
}
#[test]
fn test_two_cells_borrow_mut() {
let cell1 = JSCell::new(1);
let cell2 = JSCell::new(2);
let mut no_gc = unsafe { NoGC::new() };
let (borrow1, borrow2) = two_cells_borrow_mut(&cell1, &cell2, &mut no_gc);
*borrow1 += 1;
*borrow2 += 1;
assert_eq!(*cell1.borrow(&no_gc), 2);
assert_eq!(*cell2.borrow(&no_gc), 3);
}
#[should_panic(expected = "Cannot mutably borrow the same cell multiple times at the same time")]
#[test]
fn test_two_same_cells_borrow_mut() {
let cell1 = JSCell::new(1);
let mut no_gc = unsafe { NoGC::new() };
let _ = two_cells_borrow_mut(&cell1, &cell1, &mut no_gc);
}