Skip to content
This repository was archived by the owner on Nov 12, 2022. It is now read-only.

Reimplement CustomAutoRooter hierarchy #382

Merged
merged 10 commits into from
Dec 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ cmake = "0.1"
[[test]]
name = "callback"
[[test]]
name = "custom_auto_rooter"
[[test]]
name = "custom_auto_rooter_macro"
[[test]]
name = "enumerate"
[[test]]
name = "evaluate"
Expand Down
6 changes: 6 additions & 0 deletions src/glue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ extern "C" {
pub fn CallUnbarrieredObjectTracer(trc: *mut JSTracer,
objp: *mut *mut JSObject,
name: *const ::libc::c_char);
pub fn CallObjectRootTracer(trc: *mut JSTracer,
objp: *mut *mut JSObject,
name: *const ::libc::c_char);
pub fn CallValueRootTracer(trc: *mut JSTracer,
valp: *mut Value,
name: *const ::libc::c_char);
pub fn GetProxyHandlerFamily() -> *const c_void;

pub fn GetInt8ArrayLengthAndData(obj: *mut JSObject,
Expand Down
12 changes: 12 additions & 0 deletions src/jsglue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,18 @@ CallUnbarrieredObjectTracer(JSTracer* trc, JSObject** objp, const char* name)
js::UnsafeTraceManuallyBarrieredEdge(trc, objp, name);
}

void
CallObjectRootTracer(JSTracer* trc, JSObject** objp, const char* name)
{
JS::UnsafeTraceRoot(trc, objp, name);
}

void
CallValueRootTracer(JSTracer* trc, JS::Value* valp, const char* name)
{
JS::UnsafeTraceRoot(trc, valp, name);
}

#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type) \
void \
Get ## Type ## ArrayLengthAndData(JSObject* obj, uint32_t* length, \
Expand Down
194 changes: 193 additions & 1 deletion src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use consts::{JSCLASS_RESERVED_SLOTS_MASK, JSCLASS_GLOBAL_SLOT_COUNT};
use consts::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
use jsapi;
use jsapi::{AutoIdVector, AutoObjectVector, CallArgs, CompartmentOptions, ContextFriendFields};
use jsapi::{AutoGCRooter, AutoIdVector, AutoObjectVector, CallArgs, CompartmentOptions, ContextFriendFields};
use jsapi::{Evaluate2, Handle, HandleBase, HandleObject, HandleValue, HandleValueArray, Heap};
use jsapi::{HeapObjectPostBarrier, HeapValuePostBarrier, InitSelfHostedCode, IsWindowSlow, JS_BeginRequest};
use jsapi::{JS_DefineFunctions, JS_DefineProperties, JS_DestroyRuntime, JS_EndRequest, JS_ShutDown};
Expand All @@ -36,11 +36,13 @@ use jsapi::{RootedBase, RuntimeOptionsRef, SetWarningReporter, Symbol, ToBoolean
use jsapi::{ToInt32Slow, ToInt64Slow, ToNumberSlow, ToStringSlow, ToUint16Slow};
use jsapi::{ToUint32Slow, ToUint64Slow, ToWindowProxyIfWindow, UndefinedHandleValue};
use jsapi::{Value, jsid, PerThreadDataFriendFields, PerThreadDataFriendFields_RuntimeDummy};
use jsapi::{AutoGCRooter_jspubtd_h_unnamed_1 as AutoGCRooterTag, _vftable_CustomAutoRooter as CustomAutoRooterVFTable};
use jsval::{ObjectValue, UndefinedValue};
use glue::{AppendToAutoObjectVector, CallFunctionTracer, CallIdTracer, CallObjectTracer};
use glue::{CallScriptTracer, CallStringTracer, CallValueTracer, CreateAutoIdVector};
use glue::{CreateAutoObjectVector, CreateCallArgsFromVp, DeleteAutoObjectVector};
use glue::{DestroyAutoIdVector, DeleteCompileOptions, NewCompileOptions, SliceAutoIdVector};
use glue::{CallObjectRootTracer, CallValueRootTracer};
use panic::maybe_resume_unwind;
use default_heapsize;

Expand Down Expand Up @@ -463,6 +465,196 @@ macro_rules! rooted {
}
}

/// Similarly to `Trace` trait, it's used to specify tracing of various types
/// that are used in conjunction with `CustomAutoRooter`.
pub unsafe trait CustomTrace {
fn trace(&self, trc: *mut JSTracer);
}

unsafe impl CustomTrace for *mut JSObject {
fn trace(&self, trc: *mut JSTracer) {
let this = self as *const *mut _ as *mut *mut _;
unsafe { CallObjectRootTracer(trc, this, c_str!("object")); }
}
}

unsafe impl CustomTrace for Value {
fn trace(&self, trc: *mut JSTracer) {
let this = self as *const _ as *mut _;
unsafe { CallValueRootTracer(trc, this, c_str!("any")); }
}
}

unsafe impl<T: CustomTrace> CustomTrace for Option<T> {
fn trace(&self, trc: *mut JSTracer) {
if let Some(ref some) = *self {
some.trace(trc);
}
}
}

unsafe impl<T: CustomTrace> CustomTrace for Vec<T> {
fn trace(&self, trc: *mut JSTracer) {
for elem in self {
elem.trace(trc);
}
}
}

impl AutoGCRooter {
pub fn new_unrooted(tag: AutoGCRooterTag) -> AutoGCRooter {
AutoGCRooter {
down: ptr::null_mut(),
tag_: tag as isize,
stackTop: ptr::null_mut(),
}
}

unsafe fn add_to_root_stack(&mut self, cx: *mut JSContext) {
let autoGCRooters: &mut _ = {
let ctxfriend = cx as *mut ContextFriendFields;
&mut (*ctxfriend).roots.autoGCRooters_
};
self.stackTop = autoGCRooters;
self.down = *autoGCRooters;

assert!(*self.stackTop != self);
*self.stackTop = self;
}

unsafe fn remove_from_root_stack(&mut self) {
assert!(*self.stackTop == self);
*self.stackTop = self.down;
}
}

// This structure reimplements a C++ class that uses virtual dispatch, so
// use C layout to guarantee that vftable in CustomAutoRooter is in right place.
#[repr(C)]
pub struct CustomAutoRooter<T> {
_base: jsapi::CustomAutoRooter,
data: T,
}

impl<T> CustomAutoRooter<T> {
unsafe fn add_to_root_stack(&mut self, cx: *mut JSContext) {
self._base._base.add_to_root_stack(cx);
}

unsafe fn remove_from_root_stack(&mut self) {
self._base._base.remove_from_root_stack();
}
}

/// `CustomAutoRooter` uses dynamic dispatch on the C++ side for custom tracing,
/// so provide trace logic via vftable when creating an object on Rust side.
unsafe trait CustomAutoTraceable: Sized {
const vftable: CustomAutoRooterVFTable = CustomAutoRooterVFTable {
trace: Self::trace,
};

unsafe extern "C" fn trace(this: *mut ::std::os::raw::c_void, trc: *mut JSTracer) {
let this = this as *const Self;
let this = this.as_ref().unwrap();
Self::do_trace(this, trc);
}

/// Used by `CustomAutoTraceable` implementer to trace its contents.
/// Corresponds to virtual `trace` call in a `CustomAutoRooter` subclass (C++).
fn do_trace(&self, trc: *mut JSTracer);
}

unsafe impl<T: CustomTrace> CustomAutoTraceable for CustomAutoRooter<T> {
fn do_trace(&self, trc: *mut JSTracer) {
self.data.trace(trc);
}
}

impl<T: CustomTrace> CustomAutoRooter<T> {
pub fn new(data: T) -> Self {
CustomAutoRooter {
_base: jsapi::CustomAutoRooter {
_vftable: &<Self as CustomAutoTraceable>::vftable,
_base: AutoGCRooter::new_unrooted(AutoGCRooterTag::CUSTOM),
},
data,
}
}

pub fn root<'a>(&'a mut self, cx: *mut JSContext) -> CustomAutoRooterGuard<'a, T> {
CustomAutoRooterGuard::new(cx, self)
}
}

/// An RAII guard used to root underlying data in `CustomAutoRooter` until the
/// guard is dropped (falls out of scope).
/// The underlying data can be accessed through this guard via its Deref and
/// DerefMut implementations.
/// This structure is created by `root` method on `CustomAutoRooter` or
/// by an appropriate macro (e.g. `rooted_vec!` for `SequenceRooter` alias).
pub struct CustomAutoRooterGuard<'a, T: 'a + CustomTrace> {
rooter: &'a mut CustomAutoRooter<T>
}

impl<'a, T: 'a + CustomTrace> CustomAutoRooterGuard<'a, T> {
pub fn new(cx: *mut JSContext, rooter: &'a mut CustomAutoRooter<T>) -> Self {
unsafe {
rooter.add_to_root_stack(cx);
}
CustomAutoRooterGuard {
rooter
}
}

pub fn handle(&self) -> Handle<T> where T: RootKind {
unsafe {
Handle::from_marked_location(&self.rooter.data)
}
}

pub fn handle_mut(&mut self) -> MutableHandle<T> where T: RootKind {
unsafe {
MutableHandle::from_marked_location(&mut self.rooter.data)
}
}
}

impl<'a, T: 'a + CustomTrace> Deref for CustomAutoRooterGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
&self.rooter.data
}
}

impl<'a, T: 'a + CustomTrace> DerefMut for CustomAutoRooterGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.rooter.data
}
}

impl<'a, T: 'a + CustomTrace> Drop for CustomAutoRooterGuard<'a, T> {
fn drop(&mut self) {
unsafe {
self.rooter.remove_from_root_stack();
}
}
}

pub type SequenceRooter<T> = CustomAutoRooter<Vec<T>>;
pub type SequenceRooterGuard<'a, T> = CustomAutoRooterGuard<'a, Vec<T>>;

#[macro_export]
macro_rules! auto_root {
(in($cx:expr) let $name:ident = $init:expr) => {
let mut __root = $crate::rust::CustomAutoRooter::new($init);
let $name = __root.root($cx);
};
(in($cx:expr) let mut $name:ident = $init:expr) => {
let mut __root = $crate::rust::CustomAutoRooter::new($init);
let mut $name = __root.root($cx);
}
}

impl<T> Handle<T> {
pub fn get(&self) -> T
where T: Copy
Expand Down
44 changes: 44 additions & 0 deletions tests/custom_auto_rooter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* 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/. */

extern crate mozjs;
use mozjs::jsapi::JSTracer;
use mozjs::jsapi::JS_GC;
use mozjs::rust::Runtime;
use mozjs::rust::CustomTrace;
use mozjs::rust::CustomAutoRooter;
use std::cell::Cell;

struct TraceCheck {
trace_was_called: Cell<bool>
}

impl TraceCheck {
fn new() -> TraceCheck {
TraceCheck { trace_was_called: Cell::new(false) }
}
}

unsafe impl CustomTrace for TraceCheck {
fn trace(&self, _: *mut JSTracer) {
self.trace_was_called.set(true);
}
}

/// Check if Rust reimplementation of CustomAutoRooter properly appends itself
/// to autoGCRooters stack list and if C++ inheritance was properly simulated
/// by checking if appropriate virtual trace function was called.
#[test]
fn virtual_trace_called() {
let rt = Runtime::new().unwrap();
let (rt, cx) = (rt.rt(), rt.cx());

let mut rooter = CustomAutoRooter::new(TraceCheck::new());
let guard = rooter.root(cx);

unsafe { JS_GC(rt); }

assert!(guard.trace_was_called.get());
}

39 changes: 39 additions & 0 deletions tests/custom_auto_rooter_macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* 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/. */

#[macro_use]
extern crate mozjs;
use mozjs::jsapi::JSTracer;
use mozjs::jsapi::JS_GC;
use mozjs::rust::Runtime;
use mozjs::rust::CustomTrace;
use std::cell::Cell;

struct TraceCheck {
trace_was_called: Cell<bool>
}

impl TraceCheck {
fn new() -> TraceCheck {
TraceCheck { trace_was_called: Cell::new(false) }
}
}

unsafe impl CustomTrace for TraceCheck {
fn trace(&self, _: *mut JSTracer) {
self.trace_was_called.set(true);
}
}

#[test]
fn custom_auto_rooter_macro() {
let rt = Runtime::new().unwrap();
let (rt, cx) = (rt.rt(), rt.cx());

auto_root!(in(cx) let vec = vec![TraceCheck::new(), TraceCheck::new()]);

unsafe { JS_GC(rt); }

vec.iter().for_each(|elem| assert!(elem.trace_was_called.get()));
}