Skip to main content

mozjs/
cell.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//! A shareable mutable container.
6
7pub use std::cell::{Ref, RefMut};
8use std::cell::{RefCell, UnsafeCell};
9
10use mozjs_sys::trace::Traceable;
11
12use crate::context::NoGC;
13use crate::jsapi::JSTracer;
14
15/// [`std::cell::RefCell`] that statically prevents borrow_mut panics
16/// from occurring by borrowing due to GC using the [`NoGC`] marker.
17///
18/// If dynamic borrow checking is not needed, one should use [`JSCell`] instead.
19/// If inner type is [`Copy`] one should use [`std::cell::Cell`] instead.
20#[derive(Clone, Debug, Default)]
21pub struct JSRefCell<T> {
22    value: RefCell<T>,
23}
24
25impl<T> JSRefCell<T> {
26    /// Create a new `JSRefCell` containing `value`.
27    pub fn new(value: T) -> JSRefCell<T> {
28        JSRefCell {
29            value: RefCell::new(value),
30        }
31    }
32
33    /// Returns a reference to the underlying [`RefCell`].
34    ///
35    /// This is not strictly unsafe, but it is not recommended to use this method unless you know what you are doing.
36    ///
37    /// Mutable borrows from it can cause panics if a GC happens while the borrow is active,
38    /// which is why [`JSRefCell::borrow_mut`] should be used instead.
39    pub fn as_refcell(&self) -> &RefCell<T> {
40        &self.value
41    }
42
43    /// Immutably borrows the wrapped value.
44    ///
45    /// The borrow lasts until the returned `Ref` exits scope. Multiple
46    /// immutable borrows can be taken out at the same time.
47    ///
48    /// This is exactly the same as [`std::cell::RefCell::borrow`].
49    ///
50    /// # Panics
51    ///
52    /// Panics if the value is currently mutably borrowed.
53    #[track_caller]
54    pub fn borrow(&self) -> Ref<'_, T> {
55        self.value.borrow()
56    }
57
58    /// Mutably borrows the wrapped value.
59    ///
60    /// The borrow lasts until the returned `RefMut` exits scope. The value
61    /// cannot be borrowed while this borrow is active.
62    ///
63    /// This is similar to [`std::cell::RefCell::borrow_mut`],
64    /// but it requires a [`&NoGC`][NoGC] token to ensure that no GC can happen while the borrow is active,
65    /// which would otherwise cause a panic when tracing (which calls `borrow`).
66    ///
67    /// ## Examples
68    ///
69    /// In simple cases one can use [`&NoGC`][NoGC] to statically ensure no GC can happen in the whole function:
70    ///
71    /// ```
72    /// use mozjs::context::{JSContext, NoGC};
73    /// use mozjs::cell::JSRefCell;
74    /// fn f(no_gc: &NoGC, cell: &JSRefCell<String>) {
75    ///     let mut mutably_borrowed = cell.borrow_mut(no_gc);
76    /// }
77    /// ```
78    ///
79    /// But in more complex cases, a method might trigger a GC, and thus require a [`&mut JSContext`][crate::context::JSContext].
80    /// In that case [`&JSContext`][crate::context::JSContext] can be used in place of [`&NoGC`][NoGC],
81    /// which will make [`RefMut`] bounded to the lifetime of the [`&JSContext`][crate::context::JSContext]
82    /// and thus prevent any GC from happening while it is alive.
83    ///
84    /// ```
85    /// use mozjs::context::{JSContext, NoGC};
86    /// use mozjs::cell::JSRefCell;
87    /// fn GC(cx: &mut JSContext) {}
88    ///
89    /// fn f(cx: &mut JSContext, cell: &JSRefCell<String>) {
90    ///     {
91    ///         let mut mutably_borrowed = cell.borrow_mut(cx);
92    ///         // do something with mutably_borrowed
93    ///
94    ///         // only &JSContext is available here
95    ///     } // mutably_borrowed goes out of scope here
96    ///     // so one can now use &mut JSContext
97    ///     GC(cx);
98    /// }
99    /// ```
100    ///
101    /// ## Non-Example
102    ///
103    /// ```compile_fail
104    /// use mozjs::context::{JSContext, NoGC};
105    /// use mozjs::cell::JSRefCell;
106    /// fn GC(cx: &mut JSContext) {}
107    ///
108    /// fn f(cx: &mut JSContext, cell: &JSRefCell<String>) {
109    ///     {
110    ///         let mut mutably_borrowed = cell.borrow_mut(cx);
111    ///         // do something with mutably_borrowed
112    ///
113    ///         // here one cannot use anything that might trigger a GC
114    ///         // as that would require &mut JSContext
115    ///         // but there is already existing &JSContext bounded at RefMut
116    ///         GC(cx);
117    ///     } // mutably_borrowed goes out of scope here
118    /// }
119    /// ```
120    ///
121    /// # Panics
122    ///
123    /// Panics if the value is currently borrowed.
124    #[track_caller]
125    pub fn borrow_mut<'a: 'r, 'no_cx: 'r, 'r>(&'a self, _no_gc: &'no_cx NoGC) -> RefMut<'r, T> {
126        self.value.borrow_mut()
127    }
128}
129
130impl<T: Default> JSRefCell<T> {
131    /// Takes the wrapped value, leaving `Default::default()` in its place.
132    ///
133    /// # Panics
134    ///
135    /// Panics if the value is currently borrowed.
136    pub fn take(&self) -> T {
137        self.value.take()
138    }
139}
140
141unsafe impl<T: Traceable> Traceable for JSRefCell<T> {
142    unsafe fn trace(&self, trc: *mut JSTracer) {
143        unsafe { (*self).borrow().trace(trc) };
144    }
145}
146
147/// A cell for interior mutability, that (ab)uses [NoGC]
148/// as a borrow token for ensuring safety.
149///
150/// If inner type is [`Copy`] one should use [`std::cell::Cell`] instead.
151///
152/// ## Examples
153///
154/// ```rust
155/// use mozjs::context::NoGC;
156/// use mozjs::cell::JSCell;
157/// fn f(no_gc: &mut NoGC, cell: &JSCell<String>) {
158///     {
159///         let borrow = cell.borrow(no_gc);
160///     } // borrow goes out of scope here
161///     cell.set(no_gc, "Hello".to_string());
162///     {
163///        let borrow_mut = cell.borrow_mut(no_gc);
164///     } // borrow_mut goes out of scope here
165/// }
166/// ```
167///
168/// ## Non-Examples
169///
170/// These fail to compile:
171///
172/// ```compile_fail
173/// use mozjs::context::NoGC;
174/// use mozjs::cell::JSCell;
175/// fn mut_borrow_after_borrow(no_gc: &mut NoGC, cell: &JSCell<String>) {
176///     let borrow = cell.borrow(no_gc);
177///     cell.set(no_gc, "Hello".to_string());
178///     drop(borrow); // otherwise compiler can shorten borrow's lifetime
179/// }
180/// ```
181///
182/// ```compile_fail
183/// use mozjs::context::NoGC;
184/// use mozjs::cell::JSCell;
185/// fn mut_borrow_after_mut_borrow(no_gc: &mut NoGC, cell: &JSCell<String>) {
186///     let borrow_mut = cell.borrow_mut(no_gc);
187///     cell.set(no_gc, "Hello".to_string());
188///     drop(borrow_mut); // otherwise compiler can shorten borrow_mut's lifetime
189/// }
190/// ```
191///
192/// ```compile_fail
193/// use mozjs::context::NoGC;
194/// use mozjs::cell::JSCell;
195/// fn borrow_after_mut_borrow(no_gc: &mut NoGC, cell: &JSCell<String>) {
196///     let borrow_mut = cell.borrow_mut(no_gc);
197///     let borrow = cell.borrow(no_gc);
198///     drop(borrow_mut); // otherwise compiler can shorten borrow_mut's lifetime
199/// }
200/// ```
201#[derive(Debug)]
202pub struct JSCell<T> {
203    inner: UnsafeCell<T>,
204}
205
206impl<T> JSCell<T> {
207    pub fn new(val: T) -> Self {
208        JSCell {
209            inner: UnsafeCell::new(val),
210        }
211    }
212
213    /// Sets the wrapped value.
214    ///
215    /// By passing a [`&mut NoGC`][NoGC] we statically ensure no other borrows are alive at the same time.
216    ///
217    /// For more information see [JSCell] documentation.
218    pub fn set<'a, 'cx>(&'a self, _exclusive: &'cx mut NoGC, val: T) {
219        // SAFETY: `&mut NoGC` is used as a borrow token to ensure that no other borrows are alive at the same time.
220        unsafe { *self.inner.get() = val }
221    }
222
223    /// Mutably borrows the wrapped value.
224    ///
225    /// By passing a [`&mut NoGC`][NoGC] we statically ensure no other borrows are alive at the same time.
226    ///
227    /// For more information see [JSCell] documentation.
228    pub fn borrow_mut<'a: 'r, 'cx: 'r, 'r>(&'a self, _exclusive: &'cx mut NoGC) -> &'r mut T {
229        // SAFETY: `&mut NoGC` is used as a borrow token to ensure that no other borrows are alive at the same time.
230        unsafe { &mut *self.inner.get() }
231    }
232
233    /// Immutably borrows the wrapped value.
234    ///
235    /// By passing a [`&NoGC`][NoGC] we statically ensure no mutable borrows are alive at the same time.
236    ///
237    /// For more information see [JSCell] documentation.
238    pub fn borrow<'a: 'r, 'no_cx: 'r, 'r>(&'a self, _no_gc: &'no_cx NoGC) -> &'r T {
239        // SAFETY: `&NoGC` is used as a borrow token to ensure that no other mutable borrows are alive at the same time.
240        unsafe { &*self.inner.get() }
241    }
242}
243
244unsafe impl<T: Traceable> Traceable for JSCell<T> {
245    unsafe fn trace(&self, trc: *mut JSTracer) {
246        // SAFETY: Tracing happens as part of GC, which requires &mut JSContext, so there cannot be any borrow alive at the same time.
247        unsafe { (&*self.inner.get()).trace(trc) };
248    }
249}
250
251/// Mutably borrows two different [`JSCell`]s at the same time.
252///
253/// This is impossible to do with normal [`JSCell::borrow_mut`],
254/// because it would require two mutable borrows of the same [`NoGC`] token at the same time:
255///
256/// ```compile_fail
257/// use mozjs::context::NoGC;
258/// use mozjs::cell::JSCell;
259/// fn f(no_gc: &mut NoGC, cell1: &JSCell<String>, cell2: &JSCell<String>) {
260///     let borrow1 = cell1.borrow_mut(no_gc);
261///     let borrow2 = cell2.borrow_mut(no_gc); // cannot borrow `no_gc` as mutable more than once at a time
262///     std::mem::swap(borrow1, borrow2);
263/// }
264/// ```
265///
266/// instead one should do it like this:
267/// ```
268/// use mozjs::context::NoGC;
269/// use mozjs::cell::JSCell;
270/// fn f(no_gc: &mut NoGC, cell1: &JSCell<String>, cell2: &JSCell<String>) {
271///     let (borrow1, borrow2) = mozjs::cell::two_cells_borrow_mut(cell1, cell2, no_gc);
272///     std::mem::swap(borrow1, borrow2);
273/// }
274/// ```
275///
276/// # Panics
277///
278/// Panics if `cell1` and `cell2` are the same cell.
279pub fn two_cells_borrow_mut<'c1, 'c2, 'cx, 'r1, 'r2, T1, T2>(
280    cell1: &'c1 JSCell<T1>,
281    cell2: &'c2 JSCell<T2>,
282    _exclusive: &'cx mut NoGC,
283) -> (&'r1 mut T1, &'r2 mut T2)
284where
285    'c1: 'r1,
286    'c2: 'r2,
287    'cx: 'r1,
288    'cx: 'r2,
289{
290    let c1 = cell1.inner.get();
291    let c2 = cell2.inner.get();
292    assert_ne!(
293        c1 as *const _, c2 as *const _,
294        "Cannot mutably borrow the same cell multiple times at the same time"
295    );
296    // SAFETY: `&mut NoGC` is used as a borrow token to ensure that no other borrows are alive at the same time.
297    unsafe { (&mut *cell1.inner.get(), &mut *cell2.inner.get()) }
298}
299
300/// Mutably borrows three different [`JSCell`]s at the same time.
301///
302/// This is impossible to do with normal [`JSCell::borrow_mut`],
303/// because it would require three mutable borrows of the same [`NoGC`] token at the same time:
304/// ```compile_fail
305/// use mozjs::context::NoGC;
306/// use mozjs::cell::JSCell;
307/// fn f(no_gc: &mut NoGC, cell1: &JSCell<String>, cell2: &JSCell<String>, cell3: &JSCell<String>) {
308///     let borrow1 = cell1.borrow_mut(no_gc);
309///     let borrow2 = cell2.borrow_mut(no_gc);
310///     let borrow3 = cell3.borrow_mut(no_gc); // cannot borrow `no_gc` as mutable more than once at a time
311///     std::mem::swap(borrow1, borrow2);
312///     std::mem::swap(borrow2, borrow3);
313/// }
314/// ```
315///
316/// instead one should do it like this:
317/// ```
318/// use mozjs::context::NoGC;
319/// use mozjs::cell::JSCell;
320/// fn f(no_gc: &mut NoGC, cell1: &JSCell<String>, cell2: &JSCell<String>, cell3: &JSCell<String>) {
321///     let (borrow1, borrow2, borrow3) = mozjs::cell::three_cells_borrow_mut(cell1, cell2, cell3, no_gc);
322///     std::mem::swap(borrow1, borrow2);
323///     std::mem::swap(borrow2, borrow3);
324/// }
325/// ```
326///
327/// # Panics
328/// Panics if any two of `cell1`, `cell2` and `cell3` are the same cell.
329pub fn three_cells_borrow_mut<'c1, 'c2, 'c3, 'cx, 'r1, 'r2, 'r3, T1, T2, T3>(
330    cell1: &'c1 JSCell<T1>,
331    cell2: &'c2 JSCell<T2>,
332    cell3: &'c3 JSCell<T3>,
333    _exclusive: &'cx mut NoGC,
334) -> (&'r1 mut T1, &'r2 mut T2, &'r3 mut T3)
335where
336    'c1: 'r1,
337    'c2: 'r2,
338    'c3: 'r3,
339    'cx: 'r1,
340    'cx: 'r2,
341    'cx: 'r3,
342{
343    let c1 = cell1.inner.get();
344    let c2 = cell2.inner.get();
345    let c3 = cell3.inner.get();
346    assert_ne!(
347        c1 as *const _, c2 as *const _,
348        "Cannot mutably borrow the same cell multiple times at the same time"
349    );
350    assert_ne!(
351        c1 as *const _, c3 as *const _,
352        "Cannot mutably borrow the same cell multiple times at the same time"
353    );
354    assert_ne!(
355        c2 as *const _, c3 as *const _,
356        "Cannot mutably borrow the same cell multiple times at the same time"
357    );
358    // SAFETY: `&mut NoGC` is used as a borrow token to ensure that no other borrows are alive at the same time.
359    unsafe {
360        (
361            &mut *cell1.inner.get(),
362            &mut *cell2.inner.get(),
363            &mut *cell3.inner.get(),
364        )
365    }
366}
367
368#[test]
369fn test_two_cells_borrow_mut() {
370    let cell1 = JSCell::new(1);
371    let cell2 = JSCell::new(2);
372    let mut no_gc = unsafe { NoGC::new() };
373    let (borrow1, borrow2) = two_cells_borrow_mut(&cell1, &cell2, &mut no_gc);
374    *borrow1 += 1;
375    *borrow2 += 1;
376    assert_eq!(*cell1.borrow(&no_gc), 2);
377    assert_eq!(*cell2.borrow(&no_gc), 3);
378}
379
380#[should_panic(expected = "Cannot mutably borrow the same cell multiple times at the same time")]
381#[test]
382fn test_two_same_cells_borrow_mut() {
383    let cell1 = JSCell::new(1);
384    let mut no_gc = unsafe { NoGC::new() };
385    let _ = two_cells_borrow_mut(&cell1, &cell1, &mut no_gc);
386}