-
Notifications
You must be signed in to change notification settings - Fork 122
Replace return_address usage in Rooted with a stack guard and a rooted! macro. #272
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
|
||
//! Rust wrappers around the raw JS apis | ||
|
||
use libc::{size_t, c_uint, c_char, ptrdiff_t}; | ||
use libc::{size_t, c_uint, c_char}; | ||
use heapsize::HeapSizeOf; | ||
use std::char; | ||
use std::ffi; | ||
|
@@ -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; | ||
|
@@ -26,7 +25,6 @@ use jsapi::{JS_SetErrorReporter, Evaluate2, JSErrorReport}; | |
use jsapi::{JS_SetGCParameter, JSGCParamKey}; | ||
use jsapi::{Heap, HeapObjectPostBarrier, HeapValuePostBarrier}; | ||
use jsapi::{ContextFriendFields}; | ||
use jsapi::{CustomAutoRooter, AutoGCRooter, _vftable_CustomAutoRooter, AutoGCRooter_jspubtd_h_unnamed_1}; | ||
use jsapi::{Rooted, Handle, MutableHandle, MutableHandleBase, RootedBase}; | ||
use jsapi::{MutableHandleValue, HandleValue, HandleObject, HandleBase}; | ||
use jsapi::AutoObjectVector; | ||
|
@@ -270,41 +268,103 @@ 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> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My gut reaction to seeing an unrooted " Is there any way we could use the type system to differentiate between things that are actually rooted vs those that are not (hashtag session types?), or maybe even just rename these types? It seems to me that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You cannot create a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would love to use better names but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose so? But what do you put there in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That trait seems like a good choice to have as a bound in the struct itself. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can do the wrapper that cannot be accessed and fill it with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, that wrapper would cause issues with |
||
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, | ||
}; | ||
} | ||
} | ||
|
||
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] = unsafe { mem::transmute(addr) }; | ||
root | ||
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> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea is that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, borrowed lvalues cannot be moved from in Rust. That's a core guarantee. |
||
} | ||
|
||
impl<'a, T> RootedGuard<'a, T> { | ||
pub fn new(cx: *mut JSContext, root: &'a mut Rooted<T>) -> Self where T: RootKind { | ||
unsafe { | ||
Handle::from_marked_location(&self.ptr) | ||
root.add_to_root_stack(cx); | ||
} | ||
RootedGuard { | ||
root: root | ||
} | ||
} | ||
|
||
pub fn handle(&self) -> Handle<T> where T: Copy { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The methods to create those types requires it. |
||
unsafe { | ||
Handle::from_marked_location(&self.root.ptr) | ||
} | ||
} | ||
|
||
pub fn handle_mut(&mut self) -> MutableHandle<T> where T: Copy { | ||
unsafe { | ||
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 | ||
} | ||
} | ||
|
||
pub fn handle_mut(&mut self) -> MutableHandle<T> { | ||
impl<'a, T> Drop for RootedGuard<'a, T> { | ||
fn drop(&mut self) { | ||
unsafe { | ||
MutableHandle::from_marked_location(&mut self.ptr) | ||
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> { | ||
pub fn get(&self) -> T { | ||
unsafe { *self.ptr } | ||
|
@@ -391,57 +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 CustomAutoRooter { | ||
pub fn new(cx: *mut JSContext, vftable: &'static _vftable_CustomAutoRooter) | ||
-> CustomAutoRooter { | ||
CustomAutoRooter::new_with_addr(cx, vftable, unsafe { return_address() }) | ||
} | ||
|
||
pub fn new_with_addr(cx: *mut JSContext, vftable: &'static _vftable_CustomAutoRooter, addr: *const u8) -> CustomAutoRooter { | ||
let ctxfriend: &mut ContextFriendFields = unsafe { | ||
&mut *(cx as *mut ContextFriendFields) | ||
}; | ||
|
||
let rooter = CustomAutoRooter { | ||
_vftable: vftable as *const _, | ||
_base: AutoGCRooter { | ||
down: ctxfriend.roots.autoGCRooters_, | ||
tag_: AutoGCRooter_jspubtd_h_unnamed_1::CUSTOM as ptrdiff_t, | ||
stackTop: &mut ctxfriend.roots.autoGCRooters_ as *mut _, | ||
} | ||
}; | ||
|
||
ctxfriend.roots.autoGCRooters_ = unsafe { | ||
(addr as *const *const u8).offset(1) as *const AutoGCRooter as *mut _ | ||
}; | ||
rooter | ||
} | ||
} | ||
|
||
impl Drop for CustomAutoRooter { | ||
fn drop(&mut self) { | ||
if self._base.stackTop as usize == mem::POST_DROP_USIZE { | ||
return; | ||
} | ||
unsafe { | ||
assert!(*self._base.stackTop == mem::transmute(&self._base)); | ||
*self._base.stackTop = self._base.down; | ||
} | ||
} | ||
} | ||
|
||
impl Default for jsid { | ||
fn default() -> jsid { JSID_VOID } | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
documentation on this? (explaining why it is necessary perhaps)