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}