summaryrefslogtreecommitdiffstats
path: root/js/rust/src/heap.rs
blob: 7c39feaf27e813bf4a236388027e45426beef941 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

use glue;
use jsapi::root::*;
use rust::GCMethods;
use std::cell::UnsafeCell;
use std::ptr;

/// Types that can be traced.
///
/// This trait is unsafe; if it is implemented incorrectly, the GC may end up
/// collecting objects that are still reachable.
pub unsafe trait Trace {
    unsafe fn trace(&self, trc: *mut JSTracer);
}

/**
 * The Heap<T> class is a heap-stored reference to a JS GC thing. All members of
 * heap classes that refer to GC things should use Heap<T> (or possibly
 * TenuredHeap<T>, described below).
 *
 * Heap<T> is an abstraction that hides some of the complexity required to
 * maintain GC invariants for the contained reference. It uses operator
 * overloading to provide a normal pointer interface, but notifies the GC every
 * time the value it contains is updated. This is necessary for generational GC,
 * which keeps track of all pointers into the nursery.
 *
 * Heap<T> instances must be traced when their containing object is traced to
 * keep the pointed-to GC thing alive.
 *
 * Heap<T> objects should only be used on the heap. GC references stored on the
 * C/C++ stack must use Rooted/Handle/MutableHandle instead.
 *
 * Type T must be a public GC pointer type.
 *
 * Note that the rust version of Heap<T> implements different barriers to the
 * C++ version, which also provides features to help integration with
 * cycle-collected C++ objects. That version has a read barrier which performs
 * gray unmarking and also marks the contents during an incremental GC. This
 * version has a pre-write barrier instead, and this enforces the
 * snapshot-at-the-beginning invariant which is necessary for incremental GC in
 * the absence of the read barrier.
 */
#[repr(C)]
#[derive(Debug)]
pub struct Heap<T: GCMethods + Copy> {
    ptr: UnsafeCell<T>,
}

impl<T: GCMethods + Copy> Heap<T> {
    pub fn new(v: T) -> Heap<T>
    where
        Heap<T>: Default,
    {
        let ptr = Heap::default();
        ptr.set(v);
        ptr
    }

    pub fn set(&self, v: T) {
        unsafe {
            let ptr = self.ptr.get();
            let prev = *ptr;
            *ptr = v;
            T::write_barriers(ptr, prev, v);
        }
    }

    pub fn get(&self) -> T {
        unsafe { *self.ptr.get() }
    }

    pub unsafe fn get_unsafe(&self) -> *mut T {
        self.ptr.get()
    }

    pub fn handle(&self) -> JS::Handle<T> {
        unsafe { JS::Handle::from_marked_location(self.ptr.get() as *const _) }
    }

    pub fn handle_mut(&self) -> JS::MutableHandle<T> {
        unsafe { JS::MutableHandle::from_marked_location(self.ptr.get()) }
    }
}

impl<T: GCMethods + Copy> Clone for Heap<T>
where
    Heap<T>: Default,
{
    fn clone(&self) -> Self {
        Heap::new(self.get())
    }
}

impl<T: GCMethods + Copy + PartialEq> PartialEq for Heap<T> {
    fn eq(&self, other: &Self) -> bool {
        self.get() == other.get()
    }
}

impl<T> Default for Heap<*mut T>
where
    *mut T: GCMethods + Copy,
{
    fn default() -> Heap<*mut T> {
        Heap {
            ptr: UnsafeCell::new(ptr::null_mut()),
        }
    }
}

impl Default for Heap<JS::Value> {
    fn default() -> Heap<JS::Value> {
        Heap {
            ptr: UnsafeCell::new(JS::Value::default()),
        }
    }
}

impl<T: GCMethods + Copy> Drop for Heap<T> {
    fn drop(&mut self) {
        unsafe {
            let prev = self.ptr.get();
            T::write_barriers(prev, *prev, T::initial());
        }
    }
}

// Creates a C string literal `$str`.
macro_rules! c_str {
    ($str:expr) => {
        concat!($str, "\0").as_ptr() as *const ::std::os::raw::c_char
    };
}

unsafe impl Trace for Heap<*mut JSFunction> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        glue::CallFunctionTracer(trc, self as *const _ as *mut Self, c_str!("function"));
    }
}

unsafe impl Trace for Heap<*mut JSObject> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        glue::CallObjectTracer(trc, self as *const _ as *mut Self, c_str!("object"));
    }
}

unsafe impl Trace for Heap<*mut JSScript> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        glue::CallScriptTracer(trc, self as *const _ as *mut Self, c_str!("script"));
    }
}

unsafe impl Trace for Heap<*mut JSString> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        glue::CallStringTracer(trc, self as *const _ as *mut Self, c_str!("string"));
    }
}

#[cfg(feature = "bigint")]
unsafe impl Trace for Heap<*mut JS::BigInt> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        glue::CallBigIntTracer(trc, self as *const _ as *mut Self, c_str!("bigint"));
    }
}

unsafe impl Trace for Heap<JS::Value> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        glue::CallValueTracer(trc, self as *const _ as *mut Self, c_str!("value"));
    }
}

unsafe impl Trace for Heap<jsid> {
    unsafe fn trace(&self, trc: *mut JSTracer) {
        glue::CallIdTracer(trc, self as *const _ as *mut Self, c_str!("id"));
    }
}