rquickjs_core/class/
cell.rs

1use super::{Class, JsClass};
2use crate::{result::BorrowError, Ctx, Error, FromJs, IntoJs, Value};
3use std::{
4    cell::{Cell, UnsafeCell},
5    marker::PhantomData,
6    mem::ManuallyDrop,
7    ops::{Deref, DerefMut},
8};
9
10/// A trait to allow classes to choose there borrowing implementation.
11///
12/// # Safety
13/// This trait is not meant to be implemented outside the rquickjs library.
14pub unsafe trait Mutability {
15    #[doc(hidden)]
16    /// The internal type of cell used for this mutability.
17    type Cell<T>;
18
19    #[doc(hidden)]
20    /// Create a new cell.
21    fn new_cell<T>(t: T) -> Self::Cell<T>;
22
23    #[doc(hidden)]
24    /// Will be called before deref
25    ///
26    /// # Safety
27    /// Implementation must ensure that the cell can be immutably borrowed until unborrow is called if
28    /// this function returns without an error.
29    unsafe fn borrow<'a, T>(cell: &'a Self::Cell<T>) -> Result<(), BorrowError>;
30
31    #[doc(hidden)]
32    /// Will be called after the reference returned by deref is no longer in use.
33    ///
34    /// # Safety
35    /// Caller must ensure that the reference returned by deref no longer exists.
36    unsafe fn unborrow<'a, T>(cell: &'a Self::Cell<T>);
37
38    #[doc(hidden)]
39    /// Will be called before deref_mut
40    ///
41    /// # Safety
42    /// Implementation must ensure that the cell can be mutably borrowed until unborrow_mut is called if
43    /// this function returns without an error.
44    unsafe fn borrow_mut<'a, T>(cell: &'a Self::Cell<T>) -> Result<(), BorrowError>;
45
46    #[doc(hidden)]
47    /// Will be called after the reference returned by deref_mut is no longer reachable.
48    ///
49    /// # Safety
50    /// Caller must ensure that the reference returned by deref_mut no longer exists.
51    unsafe fn unborrow_mut<'a, T>(cell: &'a Self::Cell<T>);
52
53    #[doc(hidden)]
54    /// Returns a reference to the cell.
55    ///
56    /// # Safety
57    /// [`borrow`] must first be called on the cell and return without error before calling deref.
58    unsafe fn deref<'a, T>(cell: &'a Self::Cell<T>) -> &'a T;
59
60    #[doc(hidden)]
61    #[allow(clippy::mut_from_ref)]
62    /// Returns a reference to the cell.
63    ///
64    /// # Safety
65    /// [`borrow_mut`] must first be called on the cell and return without error before calling deref.
66    unsafe fn deref_mut<'a, T>(cell: &'a Self::Cell<T>) -> &'a mut T;
67}
68
69/// A marker type used for marking the mutability of a class.
70/// When a class has `Readable` as it Mutable type you can only borrow it immutable.
71pub enum Readable {}
72
73unsafe impl Mutability for Readable {
74    type Cell<T> = T;
75
76    fn new_cell<T>(t: T) -> Self::Cell<T> {
77        t
78    }
79
80    unsafe fn borrow<'a, T>(_cell: &'a Self::Cell<T>) -> Result<(), BorrowError> {
81        Ok(())
82    }
83
84    unsafe fn unborrow<'a, T>(_cell: &'a Self::Cell<T>) {}
85
86    unsafe fn borrow_mut<'a, T>(_cell: &'a Self::Cell<T>) -> Result<(), BorrowError> {
87        Err(BorrowError::NotWritable)
88    }
89
90    unsafe fn unborrow_mut<'a, T>(_cell: &'a Self::Cell<T>) {}
91
92    unsafe fn deref<'a, T>(cell: &'a Self::Cell<T>) -> &'a T {
93        cell
94    }
95
96    unsafe fn deref_mut<'a, T>(_cell: &'a Self::Cell<T>) -> &'a mut T {
97        unreachable!()
98    }
99}
100
101/// A marker type used for marking the mutability of a class.
102/// When a class has `Writable` as it Mutable type you can borrow it both mutability and immutable.
103pub enum Writable {}
104
105pub struct WritableCell<T> {
106    count: Cell<usize>,
107    value: UnsafeCell<T>,
108}
109
110#[doc(hidden)]
111pub struct WriteBorrow<'a, T> {
112    cell: &'a WritableCell<T>,
113    _marker: PhantomData<&'a T>,
114}
115
116impl<'a, T> Deref for WriteBorrow<'a, T> {
117    type Target = T;
118
119    fn deref(&self) -> &Self::Target {
120        unsafe { &(*self.cell.value.get()) }
121    }
122}
123
124impl<'a, T> Drop for WriteBorrow<'a, T> {
125    fn drop(&mut self) {
126        self.cell.count.set(self.cell.count.get() - 1);
127    }
128}
129
130#[doc(hidden)]
131pub struct WriteBorrowMut<'a, T> {
132    cell: &'a WritableCell<T>,
133    _marker: PhantomData<&'a T>,
134}
135
136impl<'a, T> Deref for WriteBorrowMut<'a, T> {
137    type Target = T;
138
139    fn deref(&self) -> &Self::Target {
140        unsafe { &(*self.cell.value.get()) }
141    }
142}
143
144impl<'a, T> DerefMut for WriteBorrowMut<'a, T> {
145    fn deref_mut(&mut self) -> &mut Self::Target {
146        unsafe { &mut (*self.cell.value.get()) }
147    }
148}
149
150impl<'a, T> Drop for WriteBorrowMut<'a, T> {
151    fn drop(&mut self) {
152        self.cell.count.set(0);
153    }
154}
155
156unsafe impl Mutability for Writable {
157    type Cell<T> = WritableCell<T>;
158
159    fn new_cell<T>(t: T) -> Self::Cell<T> {
160        WritableCell {
161            count: Cell::new(0),
162            value: UnsafeCell::new(t),
163        }
164    }
165
166    unsafe fn borrow<'a, T>(cell: &'a Self::Cell<T>) -> Result<(), BorrowError> {
167        let count = cell.count.get();
168        if count == usize::MAX {
169            return Err(BorrowError::AlreadyBorrowed);
170        }
171        cell.count.set(count + 1);
172        Ok(())
173    }
174
175    unsafe fn unborrow<'a, T>(cell: &'a Self::Cell<T>) {
176        cell.count.set(cell.count.get() - 1);
177    }
178
179    unsafe fn borrow_mut<'a, T>(cell: &'a Self::Cell<T>) -> Result<(), BorrowError> {
180        let count = cell.count.get();
181        if count != 0 {
182            return Err(BorrowError::AlreadyBorrowed);
183        }
184        cell.count.set(usize::MAX);
185        Ok(())
186    }
187
188    unsafe fn unborrow_mut<'a, T>(cell: &'a Self::Cell<T>) {
189        cell.count.set(0);
190    }
191
192    unsafe fn deref<'a, T>(cell: &'a Self::Cell<T>) -> &'a T {
193        &*cell.value.get()
194    }
195
196    unsafe fn deref_mut<'a, T>(cell: &'a Self::Cell<T>) -> &'a mut T {
197        &mut *cell.value.get()
198    }
199}
200
201/// A cell type for Rust classes passed to JavaScript.
202///
203/// Implements [`RefCell`](std::cell::RefCell)-like borrow checking.
204pub struct JsCell<'js, T: JsClass<'js>> {
205    pub(crate) cell: <T::Mutable as Mutability>::Cell<T>,
206}
207
208impl<'js, T: JsClass<'js>> JsCell<'js, T> {
209    /// Create a new `JsCell`
210    pub fn new(t: T) -> Self {
211        JsCell {
212            cell: <T::Mutable as Mutability>::new_cell(t),
213        }
214    }
215
216    /// Borrow the contained value immutable.
217    ///
218    /// # Panic
219    /// Panics if the value is already borrowed mutably
220    pub fn borrow<'a>(&'a self) -> Borrow<'a, 'js, T> {
221        unsafe {
222            <T::Mutable as Mutability>::borrow(&self.cell).unwrap();
223            Borrow(&self.cell)
224        }
225    }
226
227    /// Try to borrow the contained value immutable.
228    pub fn try_borrow<'a>(&'a self) -> Result<Borrow<'a, 'js, T>, BorrowError> {
229        unsafe {
230            <T::Mutable as Mutability>::borrow(&self.cell)?;
231            Ok(Borrow(&self.cell))
232        }
233    }
234
235    /// Borrow the contained value mutably.
236    ///
237    /// # Panic
238    /// Panics if the value is already borrowed mutably or the class can't be borrowed mutably.
239    pub fn borrow_mut<'a>(&'a self) -> BorrowMut<'a, 'js, T> {
240        unsafe {
241            <T::Mutable as Mutability>::borrow_mut(&self.cell).unwrap();
242            BorrowMut(&self.cell)
243        }
244    }
245
246    /// Try to borrow the contained value mutably.
247    pub fn try_borrow_mut<'a>(&'a self) -> Result<BorrowMut<'a, 'js, T>, BorrowError> {
248        unsafe {
249            <T::Mutable as Mutability>::borrow_mut(&self.cell)?;
250            Ok(BorrowMut(&self.cell))
251        }
252    }
253}
254
255/// A borrow guard for a borrowed class.
256pub struct Borrow<'a, 'js, T: JsClass<'js> + 'a>(&'a <T::Mutable as Mutability>::Cell<T>);
257
258impl<'a, 'js, T: JsClass<'js> + 'a> Drop for Borrow<'a, 'js, T> {
259    fn drop(&mut self) {
260        unsafe { <T::Mutable as Mutability>::unborrow(self.0) }
261    }
262}
263
264impl<'a, 'js, T: JsClass<'js>> Deref for Borrow<'a, 'js, T> {
265    type Target = T;
266
267    fn deref(&self) -> &Self::Target {
268        unsafe { <T::Mutable as Mutability>::deref(self.0) }
269    }
270}
271
272/// A borrow guard for a mutably borrowed class.
273pub struct BorrowMut<'a, 'js, T: JsClass<'js> + 'a>(&'a <T::Mutable as Mutability>::Cell<T>);
274
275impl<'a, 'js, T: JsClass<'js> + 'a> Drop for BorrowMut<'a, 'js, T> {
276    fn drop(&mut self) {
277        unsafe { <T::Mutable as Mutability>::unborrow_mut(self.0) }
278    }
279}
280
281impl<'a, 'js, T: JsClass<'js>> Deref for BorrowMut<'a, 'js, T> {
282    type Target = T;
283
284    fn deref(&self) -> &Self::Target {
285        unsafe { <T::Mutable as Mutability>::deref(self.0) }
286    }
287}
288
289impl<'a, 'js, T: JsClass<'js>> DerefMut for BorrowMut<'a, 'js, T> {
290    fn deref_mut(&mut self) -> &mut Self::Target {
291        unsafe { <T::Mutable as Mutability>::deref_mut(self.0) }
292    }
293}
294
295/// An owned borrow of a class object which keeps the borrow alive for the duration of the objects lifetime.
296pub struct OwnedBorrow<'js, T: JsClass<'js> + 'js>(ManuallyDrop<Class<'js, T>>);
297
298impl<'js, T: JsClass<'js> + 'js> OwnedBorrow<'js, T> {
299    /// Borrow a class owned.
300    ///
301    /// # Panic
302    /// Panics if the class cannot be borrowed
303    pub fn from_class(class: Class<'js, T>) -> Self {
304        Self::try_from_class(class).unwrap()
305    }
306
307    /// Try to borrow a class owned.
308    pub fn try_from_class(class: Class<'js, T>) -> Result<Self, BorrowError> {
309        unsafe {
310            <T::Mutable as Mutability>::borrow(&class.get_cell().cell)?;
311        }
312        Ok(OwnedBorrow(ManuallyDrop::new(class)))
313    }
314
315    /// Turn the owned borrow back into the class releasing the borrow.
316    pub fn into_inner(mut self) -> Class<'js, T> {
317        unsafe { <T::Mutable as Mutability>::unborrow(&self.0.get_cell().cell) };
318        let res = unsafe { ManuallyDrop::take(&mut self.0) };
319        std::mem::forget(self);
320        res
321    }
322}
323
324impl<'js, T: JsClass<'js> + 'js> Drop for OwnedBorrow<'js, T> {
325    fn drop(&mut self) {
326        unsafe {
327            <T::Mutable as Mutability>::unborrow(&self.0.get_cell().cell);
328            ManuallyDrop::drop(&mut self.0)
329        }
330    }
331}
332
333impl<'js, T: JsClass<'js> + 'js> Deref for OwnedBorrow<'js, T> {
334    type Target = T;
335    fn deref(&self) -> &Self::Target {
336        unsafe { <T::Mutable as Mutability>::deref(&self.0.get_cell().cell) }
337    }
338}
339
340impl<'js, T: JsClass<'js>> FromJs<'js> for OwnedBorrow<'js, T> {
341    fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self, Error> {
342        let cls = Class::from_js(ctx, value)?;
343        OwnedBorrow::try_from_class(cls).map_err(Error::ClassBorrow)
344    }
345}
346
347impl<'js, T: JsClass<'js>> IntoJs<'js> for OwnedBorrow<'js, T> {
348    fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>, Error> {
349        self.into_inner().into_js(ctx)
350    }
351}
352
353/// An owned mutable borrow of a class object which keeps the borrow alive for the duration of the objects lifetime.
354pub struct OwnedBorrowMut<'js, T: JsClass<'js> + 'js>(ManuallyDrop<Class<'js, T>>);
355
356impl<'js, T: JsClass<'js> + 'js> OwnedBorrowMut<'js, T> {
357    /// Borrow a class mutably owned.
358    ///
359    /// # Panic
360    /// Panics if the class cannot be borrowed
361    pub fn from_class(class: Class<'js, T>) -> Self {
362        Self::try_from_class(class).unwrap()
363    }
364
365    /// Try to borrow a class mutably owned.
366    pub fn try_from_class(class: Class<'js, T>) -> Result<Self, BorrowError> {
367        unsafe {
368            <T::Mutable as Mutability>::borrow_mut(&class.get_cell().cell)?;
369        }
370        Ok(OwnedBorrowMut(ManuallyDrop::new(class)))
371    }
372
373    /// Turn the owned borrow back into the class releasing the borrow.
374    pub fn into_inner(mut self) -> Class<'js, T> {
375        unsafe { <T::Mutable as Mutability>::unborrow_mut(&self.0.get_cell().cell) };
376        let res = unsafe { ManuallyDrop::take(&mut self.0) };
377        std::mem::forget(self);
378        res
379    }
380}
381
382impl<'js, T: JsClass<'js> + 'js> Drop for OwnedBorrowMut<'js, T> {
383    fn drop(&mut self) {
384        unsafe {
385            <T::Mutable as Mutability>::unborrow_mut(&self.0.get_cell().cell);
386            ManuallyDrop::drop(&mut self.0)
387        }
388    }
389}
390
391impl<'js, T: JsClass<'js> + 'js> Deref for OwnedBorrowMut<'js, T> {
392    type Target = T;
393    fn deref(&self) -> &Self::Target {
394        unsafe { <T::Mutable as Mutability>::deref(&self.0.get_cell().cell) }
395    }
396}
397
398impl<'js, T: JsClass<'js> + 'js> DerefMut for OwnedBorrowMut<'js, T> {
399    fn deref_mut(&mut self) -> &mut Self::Target {
400        unsafe { <T::Mutable as Mutability>::deref_mut(&self.0.get_cell().cell) }
401    }
402}
403
404impl<'js, T: JsClass<'js>> FromJs<'js> for OwnedBorrowMut<'js, T> {
405    fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self, Error> {
406        let cls = Class::from_js(ctx, value)?;
407        OwnedBorrowMut::try_from_class(cls).map_err(Error::ClassBorrow)
408    }
409}
410
411impl<'js, T: JsClass<'js>> IntoJs<'js> for OwnedBorrowMut<'js, T> {
412    fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>, Error> {
413        self.into_inner().into_js(ctx)
414    }
415}