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;
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}
86
87impl JSContext {
88 /// Wrap an existing [RawJSContext] pointer.
89 ///
90 /// SAFETY:
91 /// - `cx` must be valid [RawJSContext] object.
92 /// - only one [JSContext] can be alive and it should not outlive [Runtime].
93 /// This in turn means that [JSContext] always needs to be passed down as an argument,
94 /// but for the SpiderMonkey callbacks which provide [RawJSContext] it's safe to construct **one** from provided [RawJSContext].
95 pub unsafe fn from_ptr(cx: NonNull<RawJSContext>) -> JSContext {
96 JSContext { ptr: cx }
97 }
98
99 /// Returns [NoGC] token bounded to this [JSContext].
100 /// No function that accepts `&mut JSContext` (read: triggers GC)
101 /// can be called while this is alive.
102 #[inline]
103 #[must_use]
104 pub fn no_gc<'cx>(&'cx self) -> &'cx NoGC {
105 &NoGC(())
106 }
107
108 /// Obtain [RawJSContext] mutable pointer.
109 ///
110 /// # Safety
111 ///
112 /// No [NoGC] tokens should be constructed while returned pointer is available to user.
113 /// In practices this means that one should use the result
114 /// as direct argument to SpiderMonkey function and not store it in variable.
115 ///
116 /// ```rust
117 /// use mozjs::context::*;
118 /// use mozjs::jsapi::JSContext as RawJSContext;
119 ///
120 /// fn SM_function_that_can_trigger_gc(_cx: *mut RawJSContext) {}
121 ///
122 /// fn can_trigger_gc(cx: &mut JSContext) {
123 /// unsafe { SM_function_that_can_trigger_gc(cx.raw_cx()) } // returned pointer is immediately used
124 /// cx.no_gc(); // this is ok because no outstanding raw pointer is alive
125 /// }
126 /// ```
127 pub unsafe fn raw_cx(&mut self) -> *mut RawJSContext {
128 self.ptr.as_ptr()
129 }
130
131 /// Obtain [RawJSContext] mutable pointer, that will not be used for GC.
132 ///
133 /// # Safety
134 ///
135 /// No &mut calls should be done on [JSContext] while returned pointer is available.
136 /// In practices this means that one should use the result
137 /// as direct argument to SpiderMonkey function and not store it in variable.
138 ///
139 /// ```rust
140 /// use mozjs::context::*;
141 /// use mozjs::jsapi::JSContext as RawJSContext;
142 ///
143 /// fn SM_function_that_cannot_trigger_gc(_cx: *mut RawJSContext) {}
144 ///
145 /// fn f(cx: &mut JSContext) {
146 /// unsafe { SM_function_that_cannot_trigger_gc(cx.raw_cx_no_gc()) } // returned pointer is immediately used
147 /// }
148 /// ```
149 pub unsafe fn raw_cx_no_gc(&self) -> *mut RawJSContext {
150 self.ptr.as_ptr()
151 }
152}
153
154impl Deref for JSContext {
155 type Target = NoGC;
156
157 /// Deref [`&JSContext`](JSContext) into [`&NoGC`](NoGC) so that
158 /// one can pass [`&JSContext`](JSContext) to functions that require [`&NoGC`](NoGC).
159 fn deref<'cx>(&'cx self) -> &'cx Self::Target {
160 self.no_gc()
161 }
162}
163
164/// Token that ensures that no GC can happen while it is alive.
165///
166/// This type is similar to `&JSContext`,
167/// but it is used in cases where no actual context is needed.
168///
169/// For more info and examples see [JSContext].
170pub struct NoGC(()); // zero-sized type that cannot be constructed from outside