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