diff --git a/src/conversions.rs b/src/conversions.rs index 5f055ac02..ad3ce7b9a 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -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}; @@ -473,10 +473,10 @@ impl FromJSValConvertible for Option { // https://heycam.github.io/webidl/#es-sequence impl ToJSValConvertible for Vec { 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()); @@ -488,6 +488,33 @@ impl ToJSValConvertible for Vec { } } +/// 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> FromJSValConvertible for Vec { type Config = C; @@ -497,9 +524,11 @@ impl> FromJSValConvertible for Vec Result, ()> { 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(()) @@ -509,7 +538,7 @@ impl> FromJSValConvertible for Vec jsapi::RootKind { jsapi::RootKind::Value } } -impl Rooted { - pub fn new_with_addr(cx: *mut JSContext, initial: T, addr: *const u8) -> Rooted { - let ctxfriend: &mut ContextFriendFields = unsafe { - mem::transmute(cx) - }; - - let kind = T::rootKind() as usize; - let root = Rooted:: { +impl Rooted { + pub fn new_unrooted(initial: T) -> Rooted { + 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 { - 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 { +/// 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 +} + +impl<'a, T> RootedGuard<'a, T> { + pub fn new(cx: *mut JSContext, root: &'a mut Rooted) -> 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 where T: Copy { + unsafe { + Handle::from_marked_location(&self.root.ptr) + } + } + + pub fn handle_mut(&mut self) -> MutableHandle 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 { +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 Handle { pub fn get(&self) -> T { unsafe { *self.ptr } @@ -391,57 +451,6 @@ impl MutableHandle { } } -impl Drop for Rooted { - 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 } }