@@ -6,28 +6,102 @@ use std::marker::PhantomData;
66use std:: ops:: Deref ;
77use std:: ptr:: NonNull ;
88
9- /// A wrapper for raw JSContext pointers that are strongly associated with
10- /// the [Runtime] type.
9+ pub use crate :: jsapi:: JSContext as RawJSContext ;
10+
11+ /// A wrapper for raw JSContext pointers that are strongly associated with the [Runtime] type.
12+ ///
13+ /// This type is fundamental for safe SpiderMonkey usage.
14+ /// Each (SpiderMonkey) function which takes `&mut JSContext` as argument can trigger GC.
15+ /// SpiderMonkey functions require take `&JSContext` are guaranteed to not trigger GC.
16+ /// We must not hold any unrooted or borrowed data while calling any functions that can trigger GC.
17+ /// That can causes panics or UB.
18+ /// Such types are derived from [NoGC] token which can be though of `&JSContext`,
19+ /// so they are bounded to [JSContext].
20+ ///
21+ /// ```rust
22+ /// use std::marker::PhantomData;
23+ /// use mozjs::context::*;
24+ /// use mozjs::jsapi::JSContext as RawJSContext;
25+ ///
26+ /// struct ShouldNotBeHoldAcrossGC<'a>(PhantomData<&'a ()>);
27+ ///
28+ /// impl<'a> Drop for ShouldNotBeHoldAcrossGC<'a> {
29+ /// fn drop(&mut self) {}
30+ /// }
31+ ///
32+ /// fn something_that_should_not_hold_across_gc<'a>(_no_gc: &NoGC<'a>) -> ShouldNotBeHoldAcrossGC<'a> {
33+ /// ShouldNotBeHoldAcrossGC(PhantomData)
34+ /// }
35+ ///
36+ /// fn SM_function_that_can_trigger_gc(_cx: *mut RawJSContext) {}
37+ ///
38+ /// // this lives in mozjs
39+ /// fn safe_wrapper_to_SM_function_that_can_trigger_gc(cx: &mut JSContext) {
40+ /// unsafe { SM_function_that_can_trigger_gc(cx.raw()) }
41+ /// }
42+ ///
43+ /// fn can_cause_gc(cx: &mut JSContext) {
44+ /// safe_wrapper_to_SM_function_that_can_trigger_gc(cx);
45+ /// {
46+ /// let t = something_that_should_not_hold_across_gc(&cx.no_gc());
47+ /// // do something with it
48+ /// } // t get dropped
49+ /// safe_wrapper_to_SM_function_that_can_trigger_gc(cx); // we can call GC again
50+ /// }
51+ /// ```
52+ ///
53+ /// One cannot call any GC function, while any [NoGC] token is alive,
54+ /// because [NoGC] token borrows [JSContext] (`&JSContext`)
55+ /// and thus prevents calling any function that triggers GC,
56+ /// because they require exclusive access to [JSContext] (`&mut JSContext`).
57+ ///
58+ /// ```compile_fail
59+ /// use std::marker::PhantomData;
60+ /// use mozjs::context::*;
61+ /// use mozjs::jsapi::JSContext as RawJSContext;
62+ ///
63+ /// struct ShouldNotBeHoldAcrossGC<'a>(PhantomData<&'a ()>);
64+ ///
65+ /// impl<'a> Drop for ShouldNotBeHoldAcrossGC<'a> {
66+ /// fn drop(&mut self) {} // make type not trivial, or else compiler can shorten it's lifetime
67+ /// }
68+ ///
69+ /// fn something_that_should_not_hold_across_gc<'a>(_no_gc: &'a NoGC<'a>) -> ShouldNotBeHoldAcrossGC<'a> {
70+ /// ShouldNotBeHoldAcrossGC(PhantomData)
71+ /// }
72+ ///
73+ /// fn safe_wrapper_to_SM_function_that_can_trigger_gc(_cx: &mut JSContext) {
74+ /// }
75+ ///
76+ /// fn can_cause_gc(cx: &mut JSContext) {
77+ /// safe_wrapper_to_SM_function_that_can_trigger_gc(cx);
78+ /// let t = something_that_should_not_hold_across_gc(&cx.no_gc());
79+ /// // this will create compile error, because we cannot hold NoGc across C triggering function.
80+ /// // more specifically we cannot borrow `JSContext` as mutable because it is also borrowed as immutable (NoGC).
81+ /// safe_wrapper_to_SM_function_that_can_trigger_gc(cx);
82+ /// }
83+ /// ```
1184///
12- /// Each function that can trigger GC accepts `&mut JSContext`
13- /// and each value that should not be held across GC is created using [NoGC]
14- /// which borrows [JSContext] and thus prevents calling any function that triggers GC
15- /// (because &mut requires exclusive access).
85+ /// ### WIP
86+ ///
87+ /// This model is still being incrementally introduced, so there are currently some escape hatches.
1688#[ derive( Copy , Clone ) ]
1789pub struct JSContext < ' rt > {
18- pub ( crate ) ptr : NonNull < crate :: jsapi :: JSContext > ,
90+ pub ( crate ) ptr : NonNull < RawJSContext > ,
1991 pub ( crate ) runtime_anchor : PhantomData < & ' rt ( ) > ,
2092}
2193
2294impl < ' rt > JSContext < ' rt > {
23- /// Wrap an existing raw JSContext pointer.
95+ /// Wrap an existing [RawJSContext] pointer.
2496 ///
2597 /// SAFETY:
26- /// - cx must be valid JSContext object.
27- /// - the resulting lifetime must not exceed the actual lifetime of the
28- /// associated JS runtime.
29- /// - only one JSContext can be alive (it's safe to construct only one from ptr provided from callbacks, but you are not allowed to make more from thin air)
30- pub unsafe fn from_ptr ( cx : NonNull < mozjs_sys:: jsapi:: JSContext > ) -> JSContext < ' rt > {
98+ /// - `cx` must be valid [RawJSContext] object.
99+ /// - the resulting lifetime `'rt` must not exceed the actual lifetime of the
100+ /// associated [Runtime].
101+ /// - only one [JSContext] can be alive.
102+ /// This in turn means that [JSContext] always needs to be passed down as an argument,
103+ /// but for the SpiderMonkey callbacks which provide [RawJSContext] it's safe to construct **one** from provided [RawJSContext].
104+ pub unsafe fn from_ptr ( cx : NonNull < RawJSContext > ) -> JSContext < ' rt > {
31105 JSContext {
32106 ptr : cx,
33107 runtime_anchor : PhantomData ,
@@ -39,35 +113,50 @@ impl<'rt> JSContext<'rt> {
39113 /// can be called while this is alive.
40114 #[ inline]
41115 #[ must_use]
42- pub fn no_gc < ' cx : ' rt > ( & ' cx self ) -> & ' cx NoGC < ' cx > {
116+ pub fn no_gc < ' cx > ( & ' cx self ) -> & ' cx NoGC < ' cx > {
43117 & NoGC ( PhantomData )
44118 }
45- }
46-
47- // This will be eventually removed, because it currently breaks safety invariants
48- impl < ' rt > Deref for JSContext < ' rt > {
49- type Target = * mut crate :: jsapi:: JSContext ;
50119
51- fn deref ( & self ) -> & Self :: Target {
52- unsafe {
53- std:: mem:: transmute :: < & NonNull < crate :: jsapi:: JSContext > , & * mut crate :: jsapi:: JSContext > (
54- & self . ptr ,
55- )
56- }
120+ /// Obtain [RawJSContext] mutable pointer.
121+ ///
122+ /// # Safety
123+ ///
124+ /// No [NoGC] tokens should be constructed while returned pointer is available to user.
125+ /// In practices this means that one should use the result
126+ /// as direct argument to SpiderMonkey function and not store it in variable.
127+ ///
128+ /// ```rust
129+ /// use mozjs::context::*;
130+ /// use mozjs::jsapi::JSContext as RawJSContext;
131+ ///
132+ /// fn SM_function_that_can_trigger_gc(_cx: *mut RawJSContext) {}
133+ ///
134+ /// fn can_trigger_gc(cx: &mut JSContext) {
135+ /// unsafe { SM_function_that_can_trigger_gc(cx.raw()) } // returned pointer is immediately used
136+ /// cx.no_gc(); // this is ok because no outstanding raw pointer is alive
137+ /// }
138+ /// ```
139+ pub unsafe fn raw ( & mut self ) -> * mut RawJSContext {
140+ self . ptr . as_ptr ( )
57141 }
58142}
59143
60- /// Token that ensures that no GC can happen while this is alive.
144+ /// Token that ensures that no GC can happen while it is alive.
61145///
62- /// Each GC triggering function require mutable access to [JSContext],
63- /// which cannot be while this exists, because it's lifetime is bounded with [JSContext].
146+ /// Each function that trigger GC require mutable access to [JSContext],
147+ /// so one cannot call them because [NoGC] lifetime is bounded to [JSContext].
148+ ///
149+ /// For more info and examples see [JSContext].
64150pub struct NoGC < ' cx > ( PhantomData < & ' cx ( ) > ) ;
65151
66- /// Special case of [JSContext], which can be passed to functions which will not trigger GC, but still take `*mut JSContext`
67- /// (they usually take `cx: *mut root::JSContext, nogc: *const root::JS::AutoRequireNoGC` like `JS_GetTwoByteStringCharsAndLength`).
68- ///
69- /// Because they do not trigger GC, this can be alive while holding [NoGC].
70- pub struct NoGcJSContext < ' cx > {
71- ptr : NonNull < crate :: jsapi:: JSContext > ,
72- no_gc : PhantomData < & ' cx ( ) > ,
152+ impl < ' rt > Deref for JSContext < ' rt > {
153+ type Target = * mut RawJSContext ;
154+
155+ /// This exists to make migration to [JSContext] easier,
156+ /// but it will be eventually removed as it is **unsafe**.
157+ ///
158+ /// Use [JSContext::raw] instead.
159+ fn deref ( & ' _ self ) -> & ' _ Self :: Target {
160+ unsafe { std:: mem:: transmute :: < & NonNull < RawJSContext > , & * mut RawJSContext > ( & self . ptr ) }
161+ }
73162}
0 commit comments