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