summaryrefslogtreecommitdiffstats
path: root/js/rust/src/heap.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/rust/src/heap.rs179
1 files changed, 179 insertions, 0 deletions
diff --git a/js/rust/src/heap.rs b/js/rust/src/heap.rs
new file mode 100644
index 0000000000..7c39feaf27
--- /dev/null
+++ b/js/rust/src/heap.rs
@@ -0,0 +1,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"));
+ }
+}