Skip to main content

mozjs_sys/
jsgc.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 http://mozilla.org/MPL/2.0/. */
4
5use crate::jsapi::{js, JS};
6use crate::jsapi::{jsid, JSFunction, JSObject, JSScript, JSString, JSTracer};
7use crate::jsid::VoidId;
8use std::cell::UnsafeCell;
9use std::collections::VecDeque;
10use std::ffi::{c_char, c_void};
11use std::marker::PhantomData;
12use std::mem;
13use std::ptr;
14
15/// A trait for JS types that can be registered as roots.
16pub trait RootKind {
17    type Vtable;
18    const VTABLE: Self::Vtable;
19    const KIND: JS::RootKind;
20}
21
22impl RootKind for *mut JSObject {
23    type Vtable = ();
24    const VTABLE: Self::Vtable = ();
25    const KIND: JS::RootKind = JS::RootKind::Object;
26}
27
28impl RootKind for *mut JSFunction {
29    type Vtable = ();
30    const VTABLE: Self::Vtable = ();
31    const KIND: JS::RootKind = JS::RootKind::Object;
32}
33
34impl RootKind for *mut JSString {
35    type Vtable = ();
36    const VTABLE: Self::Vtable = ();
37    const KIND: JS::RootKind = JS::RootKind::String;
38}
39
40impl RootKind for *mut JS::Symbol {
41    type Vtable = ();
42    const VTABLE: Self::Vtable = ();
43    const KIND: JS::RootKind = JS::RootKind::Symbol;
44}
45
46impl RootKind for *mut JS::BigInt {
47    type Vtable = ();
48    const VTABLE: Self::Vtable = ();
49    const KIND: JS::RootKind = JS::RootKind::BigInt;
50}
51
52impl RootKind for *mut JSScript {
53    type Vtable = ();
54    const VTABLE: Self::Vtable = ();
55    const KIND: JS::RootKind = JS::RootKind::Script;
56}
57
58impl RootKind for jsid {
59    type Vtable = ();
60    const VTABLE: Self::Vtable = ();
61    const KIND: JS::RootKind = JS::RootKind::Id;
62}
63
64impl RootKind for JS::Value {
65    type Vtable = ();
66    const VTABLE: Self::Vtable = ();
67    const KIND: JS::RootKind = JS::RootKind::Value;
68}
69
70impl<T: Rootable> RootKind for T {
71    type Vtable = *const RootedVFTable;
72    const VTABLE: Self::Vtable = &<Self as Rootable>::VTABLE;
73    const KIND: JS::RootKind = JS::RootKind::Traceable;
74}
75
76/// A vtable for use in RootedTraceable<T>, which must be present for stack roots using
77/// RootKind::Traceable. The C++ tracing implementation uses a virtual trace function
78/// which is only present for C++ Rooted<T> values that use the Traceable root kind.
79#[repr(C)]
80pub struct RootedVFTable {
81    #[cfg(windows)]
82    pub padding: [usize; 1],
83    #[cfg(not(windows))]
84    pub padding: [usize; 2],
85    pub trace: unsafe extern "C" fn(this: *mut c_void, trc: *mut JSTracer, name: *const c_char),
86}
87
88impl RootedVFTable {
89    #[cfg(windows)]
90    pub const PADDING: [usize; 1] = [0];
91    #[cfg(not(windows))]
92    pub const PADDING: [usize; 2] = [0, 0];
93}
94
95/// Marker trait that allows any type that implements the [trace::Traceable] trait to be used
96/// with the [Rooted] type.
97///
98/// `Rooted<T>` relies on dynamic dispatch in C++ when T uses the Traceable RootKind.
99/// This trait initializes the vtable when creating a Rust instance of the Rooted object.
100pub trait Rootable: crate::trace::Traceable + Sized {
101    const VTABLE: RootedVFTable = RootedVFTable {
102        padding: RootedVFTable::PADDING,
103        trace: <Self as Rootable>::trace,
104    };
105
106    unsafe extern "C" fn trace(this: *mut c_void, trc: *mut JSTracer, _name: *const c_char) {
107        let rooted = this as *mut Rooted<Self>;
108        let rooted = rooted.as_mut().unwrap();
109        <Self as crate::trace::Traceable>::trace(&mut rooted.data, trc);
110    }
111}
112
113impl<T: Rootable> Rootable for Option<T> {}
114impl<T: crate::trace::Traceable> Rootable for Vec<T> {}
115impl<T: crate::trace::Traceable> Rootable for VecDeque<T> {}
116impl<T: crate::trace::Traceable> Rootable for Box<T> {}
117
118// The C++ representation of Rooted<T> inherits from StackRootedBase, which
119// contains the actual pointers that get manipulated. The Rust representation
120// also uses the pattern, which is critical to ensuring that the right pointers
121// to Rooted<T> values are used, since some Rooted<T> values are prefixed with
122// a vtable pointer, and we don't want to store pointers to that vtable where
123// C++ expects a StackRootedBase.
124#[repr(C)]
125#[derive(Debug)]
126pub struct RootedBase {
127    pub stack: *mut *mut RootedBase,
128    pub prev: *mut RootedBase,
129}
130
131// Annoyingly, bindgen can't cope with SM's use of templates, so we have to roll our own.
132#[repr(C)]
133#[cfg_attr(
134    feature = "crown",
135    crown::unrooted_must_root_lint::allow_unrooted_interior
136)]
137pub struct Rooted<T: RootKind> {
138    pub vtable: T::Vtable,
139    pub base: RootedBase,
140    pub data: T,
141}
142
143/// Trait that provides a GC-safe default value for the given type, if one exists.
144pub trait Initialize: Sized {
145    /// Create a default value. If there is no meaningful default possible, returns None.
146    /// SAFETY:
147    ///   The default must not be a value that can be meaningfully garbage collected.
148    unsafe fn initial() -> Option<Self>;
149}
150
151impl<T> Initialize for Option<T> {
152    unsafe fn initial() -> Option<Self> {
153        Some(None)
154    }
155}
156
157/// A trait for types which can place appropriate GC barriers.
158/// * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/Garbage_collection#Incremental_marking
159/// * https://dxr.mozilla.org/mozilla-central/source/js/src/gc/Barrier.h
160pub trait GCMethods: Initialize {
161    /// Create a default value
162    unsafe fn initial() -> Self {
163        <Self as Initialize>::initial()
164            .expect("Types used with heap GC methods must have a valid default")
165    }
166
167    /// Place a post-write barrier
168    unsafe fn post_barrier(v: *mut Self, prev: Self, next: Self);
169}
170
171impl Initialize for *mut JSObject {
172    unsafe fn initial() -> Option<*mut JSObject> {
173        Some(ptr::null_mut())
174    }
175}
176
177impl GCMethods for *mut JSObject {
178    unsafe fn post_barrier(v: *mut *mut JSObject, prev: *mut JSObject, next: *mut JSObject) {
179        JS::HeapObjectWriteBarriers(v, prev, next);
180    }
181}
182
183impl Initialize for *mut JSFunction {
184    unsafe fn initial() -> Option<*mut JSFunction> {
185        Some(ptr::null_mut())
186    }
187}
188
189impl GCMethods for *mut JSFunction {
190    unsafe fn post_barrier(v: *mut *mut JSFunction, prev: *mut JSFunction, next: *mut JSFunction) {
191        JS::HeapObjectWriteBarriers(
192            mem::transmute(v),
193            mem::transmute(prev),
194            mem::transmute(next),
195        );
196    }
197}
198
199impl Initialize for *mut JSString {
200    unsafe fn initial() -> Option<*mut JSString> {
201        Some(ptr::null_mut())
202    }
203}
204
205impl GCMethods for *mut JSString {
206    unsafe fn post_barrier(v: *mut *mut JSString, prev: *mut JSString, next: *mut JSString) {
207        JS::HeapStringWriteBarriers(v, prev, next);
208    }
209}
210
211impl Initialize for *mut JS::Symbol {
212    unsafe fn initial() -> Option<*mut JS::Symbol> {
213        Some(ptr::null_mut())
214    }
215}
216
217impl GCMethods for *mut JS::Symbol {
218    unsafe fn post_barrier(_: *mut *mut JS::Symbol, _: *mut JS::Symbol, _: *mut JS::Symbol) {}
219}
220
221impl Initialize for *mut JS::BigInt {
222    unsafe fn initial() -> Option<*mut JS::BigInt> {
223        Some(ptr::null_mut())
224    }
225}
226
227impl GCMethods for *mut JS::BigInt {
228    unsafe fn post_barrier(v: *mut *mut JS::BigInt, prev: *mut JS::BigInt, next: *mut JS::BigInt) {
229        JS::HeapBigIntWriteBarriers(v, prev, next);
230    }
231}
232
233impl Initialize for *mut JSScript {
234    unsafe fn initial() -> Option<*mut JSScript> {
235        Some(ptr::null_mut())
236    }
237}
238
239impl GCMethods for *mut JSScript {
240    unsafe fn post_barrier(v: *mut *mut JSScript, prev: *mut JSScript, next: *mut JSScript) {
241        JS::HeapScriptWriteBarriers(v, prev, next);
242    }
243}
244
245impl Initialize for jsid {
246    unsafe fn initial() -> Option<jsid> {
247        Some(VoidId())
248    }
249}
250
251impl GCMethods for jsid {
252    unsafe fn post_barrier(_: *mut jsid, _: jsid, _: jsid) {}
253}
254
255impl Initialize for JS::Value {
256    unsafe fn initial() -> Option<JS::Value> {
257        Some(JS::Value::default())
258    }
259}
260
261impl GCMethods for JS::Value {
262    unsafe fn post_barrier(v: *mut JS::Value, prev: JS::Value, next: JS::Value) {
263        JS::HeapValueWriteBarriers(v, &prev, &next);
264    }
265}
266
267impl Rootable for JS::PropertyDescriptor {}
268
269impl Initialize for JS::PropertyDescriptor {
270    unsafe fn initial() -> Option<JS::PropertyDescriptor> {
271        Some(JS::PropertyDescriptor::default())
272    }
273}
274
275impl GCMethods for JS::PropertyDescriptor {
276    unsafe fn post_barrier(
277        _: *mut JS::PropertyDescriptor,
278        _: JS::PropertyDescriptor,
279        _: JS::PropertyDescriptor,
280    ) {
281    }
282}
283
284/// A fixed-size array of values, for use inside Rooted<>.
285///
286/// https://searchfox.org/mozilla-central/source/js/public/ValueArray.h#31
287pub struct ValueArray<const N: usize> {
288    elements: [JS::Value; N],
289}
290
291impl<const N: usize> ValueArray<N> {
292    pub fn new(elements: [JS::Value; N]) -> Self {
293        Self { elements }
294    }
295
296    pub fn get_ptr(&self) -> *const JS::Value {
297        self.elements.as_ptr()
298    }
299
300    pub unsafe fn get_mut_ptr(&self) -> *mut JS::Value {
301        self.elements.as_ptr() as *mut _
302    }
303}
304
305impl<const N: usize> Rootable for ValueArray<N> {}
306
307impl<const N: usize> Initialize for ValueArray<N> {
308    unsafe fn initial() -> Option<Self> {
309        Some(Self {
310            elements: [<JS::Value as GCMethods>::initial(); N],
311        })
312    }
313}
314
315/// RootedValueArray roots an internal fixed-size array of Values
316pub type RootedValueArray<const N: usize> = Rooted<ValueArray<N>>;
317
318/// Heap values encapsulate GC concerns of an on-heap reference to a JS
319/// object. This means that every reference to a JS object on heap must
320/// be realized through this structure.
321///
322/// # Safety
323/// For garbage collection to work correctly in SpiderMonkey, modifying the
324/// wrapped value triggers a GC barrier, pointing to the underlying object.
325///
326/// This means that after calling the `set()` function with a non-null or
327/// non-undefined value, the `Heap` wrapper *must not* be moved, since doing
328/// so will invalidate the local reference to wrapped value, still held by
329/// SpiderMonkey.
330///
331/// For safe `Heap` construction with value see `Heap::boxed` function.
332#[cfg_attr(feature = "crown", crown::unrooted_must_root_lint::must_root)]
333#[repr(C)]
334#[derive(Debug)]
335pub struct Heap<T: GCMethods + Copy> {
336    pub ptr: UnsafeCell<T>,
337}
338
339impl<T: GCMethods + Copy> Heap<T> {
340    /// This creates a `Box`-wrapped Heap value. Setting a value inside Heap
341    /// object triggers a barrier, referring to the Heap object location,
342    /// hence why it is not safe to construct a temporary Heap value, assign
343    /// a non-null value and move it (e.g. typical object construction).
344    ///
345    /// Using boxed Heap value guarantees that the underlying Heap value will
346    /// not be moved when constructed.
347    #[cfg_attr(feature = "crown", expect(crown::unrooted_must_root))]
348    pub fn boxed(v: T) -> Box<Heap<T>>
349    where
350        Heap<T>: Default,
351    {
352        let boxed = Box::new(Heap::default());
353        boxed.set(v);
354        boxed
355    }
356
357    pub fn set(&self, v: T) {
358        unsafe {
359            let ptr = self.ptr.get();
360            let prev = *ptr;
361            *ptr = v;
362            T::post_barrier(ptr, prev, v);
363        }
364    }
365
366    pub fn get(&self) -> T {
367        unsafe { *self.ptr.get() }
368    }
369
370    pub fn get_unsafe(&self) -> *mut T {
371        self.ptr.get()
372    }
373
374    /// Retrieves a Handle to the underlying value.
375    ///
376    /// # Safety
377    ///
378    /// This is only safe to do on a rooted object (which Heap is not, it needs
379    /// to be additionally rooted), like RootedGuard, so use this only if you
380    /// know what you're doing.
381    ///
382    /// # Notes
383    ///
384    /// Since Heap values need to be informed when a change to underlying
385    /// value is made (e.g. via `get()`), this does not allow to create
386    /// MutableHandle objects, which can bypass this and lead to crashes.
387    pub unsafe fn handle(&self) -> JS::Handle<T> {
388        JS::Handle::from_marked_location(self.ptr.get() as *const _)
389    }
390}
391
392impl<T> Default for Heap<*mut T>
393where
394    *mut T: GCMethods + Copy,
395{
396    fn default() -> Heap<*mut T> {
397        Heap {
398            ptr: UnsafeCell::new(ptr::null_mut()),
399        }
400    }
401}
402
403impl Default for Heap<JS::Value> {
404    fn default() -> Heap<JS::Value> {
405        Heap {
406            ptr: UnsafeCell::new(JS::Value::default()),
407        }
408    }
409}
410
411impl<T: GCMethods + Copy> Drop for Heap<T> {
412    fn drop(&mut self) {
413        unsafe {
414            let ptr = self.ptr.get();
415            T::post_barrier(ptr, *ptr, <T as GCMethods>::initial());
416        }
417    }
418}
419
420impl<T: GCMethods + Copy + PartialEq> PartialEq for Heap<T> {
421    fn eq(&self, other: &Self) -> bool {
422        self.get() == other.get()
423    }
424}
425
426/// Trait for things that can be converted to handles
427/// For any type `T: IntoHandle` we have an implementation of `From<T>`
428/// for `MutableHandle<T::Target>`. This is a way round the orphan
429/// rule.
430pub trait IntoHandle {
431    /// The type of the handle
432    type Target;
433
434    /// Convert this object to a handle.
435    fn into_handle(self) -> JS::Handle<Self::Target>;
436}
437
438pub trait IntoMutableHandle: IntoHandle {
439    /// Convert this object to a mutable handle.
440    fn into_handle_mut(self) -> JS::MutableHandle<Self::Target>;
441}
442
443impl<T: IntoHandle> From<T> for JS::Handle<T::Target> {
444    fn from(value: T) -> Self {
445        value.into_handle()
446    }
447}
448
449impl<T: IntoMutableHandle> From<T> for JS::MutableHandle<T::Target> {
450    fn from(value: T) -> Self {
451        value.into_handle_mut()
452    }
453}
454
455/// Methods for a CustomAutoRooter
456#[repr(C)]
457pub struct CustomAutoRooterVFTable {
458    #[cfg(windows)]
459    pub padding: [usize; 1],
460    #[cfg(not(windows))]
461    pub padding: [usize; 2],
462    pub trace: unsafe extern "C" fn(this: *mut c_void, trc: *mut JSTracer),
463}
464
465impl CustomAutoRooterVFTable {
466    #[cfg(windows)]
467    pub const PADDING: [usize; 1] = [0];
468    #[cfg(not(windows))]
469    pub const PADDING: [usize; 2] = [0, 0];
470}
471
472#[repr(C)]
473#[derive(Copy, Clone)]
474pub struct StackGCVector<T, AllocPolicy = js::TempAllocPolicy>(PhantomData<(T, AllocPolicy)>, u8);