New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace return_address usage in Rooted with a stack guard and a rooted! macro. #272
Changes from all commits
File filter...
Jump to…
| @@ -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> { | ||
eddyb
Author
Contributor
|
||
| 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> | ||
fitzgen
Member
|
||
| } | ||
|
|
||
| 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 { | ||
|
||
| 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 } | ||
| } | ||
My gut reaction to seeing an unrooted "
Rooted<T>" is terror: it means you could pass around references toRooted<T>(what would be[Mutable]Handle<T>in js/) that are not in fact rooted.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
Rooted<T>is reallyRootable<T>andRootedGuard<T>is the realRooted<T>.