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

Commit

Permalink
Move from using return_address to a macro-mediated rooting solution.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Jun 27, 2016
1 parent 4627586 commit ae62516
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 39 deletions.
39 changes: 34 additions & 5 deletions src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use jsapi::JSPROP_ENUMERATE;
use jsapi::{JSContext, JSObject, JSString, HandleValue, MutableHandleValue};
use jsapi::{JS_NewUCStringCopyN, JS_StringHasLatin1Chars, JS_WrapValue};
use jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetTwoByteStringCharsAndLength};
use jsapi::{JS_NewArrayObject1, JS_DefineElement, RootedValue, RootedObject};
use jsapi::{JS_NewArrayObject1, JS_DefineElement, RootedObject};
use jsapi::{ForOfIterator, ForOfIterator_NonIterableBehavior};
use jsval::{BooleanValue, Int32Value, NullValue, UInt32Value, UndefinedValue};
use jsval::{JSVal, ObjectValue, ObjectOrNullValue, StringValue};
Expand Down Expand Up @@ -473,10 +473,10 @@ impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> {
// https://heycam.github.io/webidl/#es-sequence
impl<T: ToJSValConvertible> ToJSValConvertible for Vec<T> {
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
let js_array = RootedObject::new(cx, JS_NewArrayObject1(cx, self.len() as libc::size_t));
rooted!(in(cx) let js_array = JS_NewArrayObject1(cx, self.len() as libc::size_t));
assert!(!js_array.handle().is_null());

let mut val = RootedValue::new(cx, UndefinedValue());
rooted!(in(cx) let mut val = UndefinedValue());
for (index, obj) in self.iter().enumerate() {
obj.to_jsval(cx, val.handle_mut());

Expand All @@ -488,6 +488,33 @@ impl<T: ToJSValConvertible> ToJSValConvertible for Vec<T> {
}
}

/// Rooting guard for the iterator field of ForOfIterator.
/// Behaves like RootedGuard (roots on creation, unroots on drop),
/// but borrows and allows access to the whole ForOfIterator, so
/// that methods on ForOfIterator can still be used through it.
struct ForOfIteratorGuard<'a> {
root: &'a mut ForOfIterator
}

impl<'a> ForOfIteratorGuard<'a> {
fn new(cx: *mut JSContext, root: &'a mut ForOfIterator) -> Self {
unsafe {
root.iterator.add_to_root_stack(cx);
}
ForOfIteratorGuard {
root: root
}
}
}

impl<'a> Drop for ForOfIteratorGuard<'a> {
fn drop(&mut self) {
unsafe {
self.root.iterator.remove_from_root_stack();
}
}
}

impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T> {
type Config = C;

Expand All @@ -497,9 +524,11 @@ impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T
-> Result<Vec<T>, ()> {
let mut iterator = ForOfIterator {
cx_: cx,
iterator: RootedObject::new(cx, ptr::null_mut()),
iterator: RootedObject::new_unrooted(ptr::null_mut()),
index: ::std::u32::MAX, // NOT_ARRAY
};
let mut iterator = ForOfIteratorGuard::new(cx, &mut iterator);
let iterator = &mut *iterator.root;

if !iterator.init(value, ForOfIterator_NonIterableBehavior::ThrowOnNonIterable) {
return Err(())
Expand All @@ -509,7 +538,7 @@ impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T

loop {
let mut done = false;
let mut val = RootedValue::new(cx, UndefinedValue());
rooted!(in(cx) let mut val = UndefinedValue());
if !iterator.next(val.handle_mut(), &mut done) {
return Err(())
}
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#![crate_name = "js"]
#![crate_type = "rlib"]

#![feature(core_intrinsics)]
#![feature(filling_drop)]
#![feature(link_args)]
#![feature(unsafe_no_drop_flag)]
Expand Down Expand Up @@ -78,12 +77,15 @@ pub mod jsapi {
include!("jsapi_linux_32_debug.rs");
}

#[macro_use]
pub mod rust;

mod consts;
pub mod conversions;
pub mod error;
pub mod glue;
pub mod jsval;
pub mod rust;


pub use consts::*;

Expand Down
113 changes: 81 additions & 32 deletions src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use std::slice;
use std::mem;
use std::u32;
use std::default::Default;
use std::intrinsics::return_address;
use std::ops::{Deref, DerefMut};
use std::cell::UnsafeCell;
use std::marker::PhantomData;
Expand Down Expand Up @@ -269,39 +268,101 @@ impl RootKind for Value {
fn rootKind() -> jsapi::RootKind { jsapi::RootKind::Value }
}

impl<T: RootKind + Copy> Rooted<T> {
pub fn new_with_addr(cx: *mut JSContext, initial: T, addr: *const u8) -> Rooted<T> {
let ctxfriend: &mut ContextFriendFields = unsafe {
mem::transmute(cx)
};

let kind = T::rootKind() as usize;
let root = Rooted::<T> {
impl<T> Rooted<T> {
pub fn new_unrooted(initial: T) -> Rooted<T> {
Rooted {
_base: RootedBase { _phantom0: PhantomData },
stack: &mut ctxfriend.roots.stackRoots_[kind] as *mut _ as *mut _,
prev: ctxfriend.roots.stackRoots_[kind] as *mut _,
stack: ptr::null_mut(),
prev: ptr::null_mut(),
ptr: initial,
};
}
}

ctxfriend.roots.stackRoots_[kind] = unsafe { mem::transmute(addr) };
root
pub unsafe fn add_to_root_stack(&mut self, cx: *mut JSContext) where T: RootKind {
let ctxfriend: &mut ContextFriendFields = mem::transmute(cx);

let kind = T::rootKind() as usize;
self.stack = &mut ctxfriend.roots.stackRoots_[kind] as *mut _ as *mut _;
self.prev = ctxfriend.roots.stackRoots_[kind] as *mut _;

ctxfriend.roots.stackRoots_[kind] = self as *mut _ as usize as _;
}

pub fn new(cx: *mut JSContext, initial: T) -> Rooted<T> {
Rooted::new_with_addr(cx, initial, unsafe { return_address() })
pub unsafe fn remove_from_root_stack(&mut self) {
assert!(*self.stack == mem::transmute(&*self));
*self.stack = self.prev;
}
}

pub fn handle(&self) -> Handle<T> {
/// Rust API for keeping a Rooted value in the context's root stack.
/// Example usage: `rooted!(in(cx) let x = UndefinedValue());`.
/// `RootedGuard::new` also works, but the macro is preferred.
pub struct RootedGuard<'a, T: 'a> {
root: &'a mut Rooted<T>
}

impl<'a, T> RootedGuard<'a, T> {
pub fn new(cx: *mut JSContext, root: &'a mut Rooted<T>) -> Self where T: RootKind {
unsafe {
root.add_to_root_stack(cx);
}
RootedGuard {
root: root
}
}

pub fn handle(&self) -> Handle<T> where T: Copy {
unsafe {
Handle::from_marked_location(&self.ptr)
Handle::from_marked_location(&self.root.ptr)
}
}

pub fn handle_mut(&mut self) -> MutableHandle<T> {
pub fn handle_mut(&mut self) -> MutableHandle<T> where T: Copy {
unsafe {
MutableHandle::from_marked_location(&mut self.ptr)
MutableHandle::from_marked_location(&mut self.root.ptr)
}
}

pub fn get(&self) -> T where T: Copy {
self.root.ptr
}

pub fn set(&mut self, v: T) {
self.root.ptr = v;
}
}

impl<'a, T> Deref for RootedGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
&self.root.ptr
}
}

impl<'a, T> DerefMut for RootedGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.root.ptr
}
}

impl<'a, T> Drop for RootedGuard<'a, T> {
fn drop(&mut self) {
unsafe {
self.root.remove_from_root_stack();
}
}
}

#[macro_export]
macro_rules! rooted {
(in($cx:expr) let $name:ident = $init:expr) => {
let mut __root = $crate::jsapi::Rooted::new_unrooted($init);
let $name = $crate::rust::RootedGuard::new($cx, &mut __root);
};
(in($cx:expr) let mut $name:ident = $init:expr) => {
let mut __root = $crate::jsapi::Rooted::new_unrooted($init);
let mut $name = $crate::rust::RootedGuard::new($cx, &mut __root);
}
}

impl<T: Copy> Handle<T> {
Expand Down Expand Up @@ -390,18 +451,6 @@ impl<T: Copy> MutableHandle<T> {
}
}

impl<T> Drop for Rooted<T> {
fn drop(&mut self) {
unsafe {
if self.stack as usize == mem::POST_DROP_USIZE {
return;
}
assert!(*self.stack == mem::transmute(&*self));
*self.stack = self.prev;
}
}
}

impl Default for jsid {
fn default() -> jsid { JSID_VOID }
}
Expand Down

0 comments on commit ae62516

Please sign in to comment.