Skip to main content

mozjs/
context.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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::ops::{Deref, DerefMut};
6use std::ptr::NonNull;
7
8pub use crate::jsapi::JSContext as RawJSContext;
9
10/// A wrapper for [raw JSContext pointers](RawJSContext) that are strongly associated with the [Runtime](crate::rust::Runtime) type.
11///
12/// This type is fundamental for safe SpiderMonkey usage.
13/// Each (SpiderMonkey) function which takes [`&mut JSContext`](JSContext) as argument can trigger GC.
14/// SpiderMonkey functions that takes [`&JSContext`](JSContext) are guaranteed to not trigger GC.
15/// We must not hold any unrooted or borrowed data while calling any functions that can trigger GC.
16/// That can causes panics or UB.
17/// For cases where notion of no GC but no actual context is needed, we have [`&NoGC`](NoGC) token.
18///
19/// ```rust
20/// use std::marker::PhantomData;
21/// use mozjs::context::*;
22///
23/// struct ShouldNotBeHoldAcrossGC<'a>(PhantomData<&'a ()>);
24///
25/// impl<'a> Drop for ShouldNotBeHoldAcrossGC<'a> {
26///     fn drop(&mut self) {}
27/// }
28///
29/// fn something_that_should_not_hold_across_gc<'a>(_no_gc: &'a NoGC) -> ShouldNotBeHoldAcrossGC<'a> {
30///     ShouldNotBeHoldAcrossGC(PhantomData)
31/// }
32///
33/// fn SM_function_that_can_trigger_gc(_cx: *mut RawJSContext) {}
34///
35/// // this lives in mozjs
36/// fn safe_wrapper_to_SM_function_that_can_trigger_gc(cx: &mut JSContext) {
37///     unsafe { SM_function_that_can_trigger_gc(cx.raw_cx()) }
38/// }
39///
40/// fn can_cause_gc(cx: &mut JSContext) {
41///     safe_wrapper_to_SM_function_that_can_trigger_gc(cx);
42///     {
43///         let t = something_that_should_not_hold_across_gc(&cx.no_gc());
44///         // do something with it
45///     } // t get dropped
46///     safe_wrapper_to_SM_function_that_can_trigger_gc(cx); // we can call GC again
47/// }
48/// ```
49///
50/// One cannot call any GC function, while any [`&JSContext`](JSContext) or [`&NoGC`](NoGC) is alive,
51/// because they require such functions accept [`&mut JSContext`](JSContext):
52///
53/// ```compile_fail
54/// use std::marker::PhantomData;
55/// use mozjs::context::*;
56/// use mozjs::jsapi::JSContext as RawJSContext;
57///
58/// struct ShouldNotBeHoldAcrossGC<'a>(PhantomData<&'a ()>);
59///
60/// impl<'a> Drop for ShouldNotBeHoldAcrossGC<'a> {
61///     fn drop(&mut self) {} // make type not trivial, or else compiler can shorten it's lifetime
62/// }
63///
64/// fn something_that_should_not_hold_across_gc<'a>(_no_gc: &'a NoGC) -> ShouldNotBeHoldAcrossGC<'a> {
65///     ShouldNotBeHoldAcrossGC(PhantomData)
66/// }
67///
68/// fn safe_wrapper_to_SM_function_that_can_trigger_gc(_cx: &mut JSContext) {
69/// }
70///
71/// fn can_cause_gc(cx: &mut JSContext) {
72///     safe_wrapper_to_SM_function_that_can_trigger_gc(cx);
73///     let t = something_that_should_not_hold_across_gc(&cx.no_gc());
74///     // this will create compile error, because we cannot hold NoGc across C triggering function.
75///     // more specifically we cannot borrow `JSContext` as mutable because it is also borrowed as immutable (NoGC).
76///     safe_wrapper_to_SM_function_that_can_trigger_gc(cx);
77/// }
78/// ```
79///
80/// ### WIP
81///
82/// This model is still being incrementally introduced, so there are currently some escape hatches.
83pub struct JSContext {
84    pub(crate) ptr: NonNull<RawJSContext>,
85    // this is ZST, but we need it to get &mut NoGC safely
86    no_gc: NoGC,
87}
88
89impl JSContext {
90    /// Wrap an existing [RawJSContext] pointer.
91    ///
92    /// SAFETY:
93    /// - `cx` must be valid [RawJSContext] object.
94    /// - only one [JSContext] can be alive and it should not outlive [Runtime].
95    /// This in turn means that [JSContext] always needs to be passed down as an argument,
96    /// but for the SpiderMonkey callbacks which provide [RawJSContext] it's safe to construct **one** from provided [RawJSContext].
97    pub unsafe fn from_ptr(cx: NonNull<RawJSContext>) -> JSContext {
98        JSContext {
99            ptr: cx,
100            no_gc: NoGC(()),
101        }
102    }
103
104    /// Get the `JSContext` for this thread (thin air). This should be rarely used.
105    ///
106    /// SAFETY:
107    /// - only one [JSContext] can be alive and it should not outlive [Runtime].
108    pub unsafe fn get_from_thread() -> Option<JSContext> {
109        crate::rust::Runtime::get().map(|raw_cx| unsafe { JSContext::from_ptr(raw_cx) })
110    }
111
112    /// Returns [NoGC] token bounded to this [JSContext].
113    /// No function that accepts `&mut JSContext` (read: triggers GC)
114    /// can be called while this is alive.
115    #[inline]
116    #[must_use]
117    pub fn no_gc<'cx>(&'cx self) -> &'cx NoGC {
118        &NoGC(())
119    }
120
121    /// Returns [NoGC] token bounded to this [JSContext].
122    /// No function that accepts `&mut JSContext` (read: triggers GC)
123    /// can be called while this is alive.
124    #[inline]
125    #[must_use]
126    pub fn no_gc_mut<'cx>(&'cx mut self) -> &'cx mut NoGC {
127        &mut self.no_gc
128    }
129
130    /// Obtain [RawJSContext] mutable pointer.
131    ///
132    /// # Safety
133    ///
134    /// No [NoGC] tokens should be constructed while returned pointer is available to user.
135    /// In practices this means that one should use the result
136    /// as direct argument to SpiderMonkey function and not store it in variable.
137    ///
138    /// ```rust
139    /// use mozjs::context::*;
140    /// use mozjs::jsapi::JSContext as RawJSContext;
141    ///
142    /// fn SM_function_that_can_trigger_gc(_cx: *mut RawJSContext) {}
143    ///
144    /// fn can_trigger_gc(cx: &mut JSContext) {
145    ///     unsafe { SM_function_that_can_trigger_gc(cx.raw_cx()) } // returned pointer is immediately used
146    ///     cx.no_gc(); // this is ok because no outstanding raw pointer is alive
147    /// }
148    /// ```
149    pub unsafe fn raw_cx(&mut self) -> *mut RawJSContext {
150        self.ptr.as_ptr()
151    }
152
153    /// Obtain [RawJSContext] mutable pointer, that will not be used for GC.
154    ///
155    /// # Safety
156    ///
157    /// No &mut calls should be done on [JSContext] while returned pointer is available.
158    /// In practices this means that one should use the result
159    /// as direct argument to SpiderMonkey function and not store it in variable.
160    ///
161    /// ```rust
162    /// use mozjs::context::*;
163    /// use mozjs::jsapi::JSContext as RawJSContext;
164    ///
165    /// fn SM_function_that_cannot_trigger_gc(_cx: *mut RawJSContext) {}
166    ///
167    /// fn f(cx: &mut JSContext) {
168    ///     unsafe { SM_function_that_cannot_trigger_gc(cx.raw_cx_no_gc()) } // returned pointer is immediately used
169    /// }
170    /// ```
171    pub unsafe fn raw_cx_no_gc(&self) -> *mut RawJSContext {
172        self.ptr.as_ptr()
173    }
174}
175
176impl AsMut<JSContext> for JSContext {
177    fn as_mut(&mut self) -> &mut JSContext {
178        self
179    }
180}
181
182impl Deref for JSContext {
183    type Target = NoGC;
184
185    /// Deref [`&JSContext`](JSContext) into [`&NoGC`](NoGC) so that
186    /// one can pass [`&JSContext`](JSContext) to functions that require [`&NoGC`](NoGC).
187    fn deref<'cx>(&'cx self) -> &'cx Self::Target {
188        self.no_gc()
189    }
190}
191
192impl DerefMut for JSContext {
193    /// Deref [`&mut JSContext`](JSContext) into [`&mut NoGC`](NoGC) so that
194    /// one can pass [`&mut JSContext`](JSContext) to functions that require [`&mut NoGC`](NoGC).
195    fn deref_mut<'cx>(&'cx mut self) -> &'cx mut Self::Target {
196        self.no_gc_mut()
197    }
198}
199
200/// Token that ensures that no GC can happen while it is alive.
201///
202/// This type is similar to [`&JSContext`][JSContext],
203/// but it is used in cases where no actual context is needed.
204///
205/// For more info and examples see [JSContext].
206///
207/// This type can be obtained from [JSContext] (and will be bounded to it) or constructed from thin air (unsafe).
208///
209/// ```compile_fail
210/// fn f() {
211///     // safe construction is not possible
212///     mozjs::context::NoGC(());
213/// }
214/// ```
215pub struct NoGC(()); // zero-sized type that cannot be constructed from outside
216
217impl NoGC {
218    /// Creates new NoGC token from thin air.
219    ///
220    /// This is more safe than constructing [`JSContext`] from thin air as the promise here (of no GC) is weaker,
221    /// but one should still prefer passing [`NoGC`] down as an argument.
222    ///
223    /// # Safety
224    ///
225    /// One must ensure that no [`JSContext`] or existing [`NoGC`] is alive while this [`NoGC`] is alive.
226    pub unsafe fn new() -> Self {
227        NoGC(())
228    }
229}