From da0c5f76e4d9f3b6e7c07ce9977f8316094cf381 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sun, 19 Jul 2020 10:51:42 +0200 Subject: [PATCH] Add 'CallbackScope<()>' that doesn't assume an entered Context (#425) Certain callbacks (e.g. `WasmLoadSourceMapCallback`) expect the embedder to return a local handle (a `Local` in this case), but do not provide a `Local` as an argument, nor does it provide any other argument that we might obtain a context from. This is not unexpected - WASM execution as such is not tied to a context, and a `Local` can be created without a context too because it's a primitive value that has no JavaScript prototype. --- src/promise.rs | 1 + src/scope.rs | 238 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 167 insertions(+), 72 deletions(-) diff --git a/src/promise.rs b/src/promise.rs index b1a95ac4..8ea85c9a 100644 --- a/src/promise.rs +++ b/src/promise.rs @@ -200,6 +200,7 @@ pub enum PromiseRejectEvent { PromiseResolveAfterResolved, } +#[derive(Clone, Copy)] #[repr(C)] pub struct PromiseRejectMessage<'msg>([usize; 3], PhantomData<&'msg ()>); diff --git a/src/scope.rs b/src/scope.rs index bb6a36e3..4fd649c0 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -58,6 +58,16 @@ //! inaccessible until the inner scope is dropped. However, the `TryCatch` //! object will nonetheless catch all exception thrown during its lifetime. //! +//! - `CallbackScope<'s, ()>` +//! - 's = lifetime of local handles created in this scope, and the value +//! returned from the callback, and of the scope itself. +//! - A `Context` is _not_ available. Only certain types JavaScript values can +//! be created: primitive values, templates, and instances of `Context`. +//! - Derefs to `HandleScope<'s, ()>`. +//! - This scope type is only to be constructed inside embedder defined +//! callbacks when these are called by V8. +//! - When a scope is created inside, type is erased to `HandleScope<'s, ()>`. +//! //! - `CallbackScope<'s>` //! - 's = lifetime of local handles created in this scope, and the value //! returned from the callback, and of the scope itself. @@ -421,7 +431,11 @@ where /// callbacks in this fashion, so the embedder would never needs to construct /// a CallbackScope. /// -/// A CallbackScope can be created from the following inputs: +/// A `CallbackScope<()>`, without context, can be created from: +/// - `&mut Isolate` +/// - `&mut OwnedIsolate` +/// +/// A `CallbackScope`, with context, can be created from: /// - `Local` /// - `Local` /// - `Local` @@ -430,15 +444,17 @@ where /// - `&FunctionCallbackInfo` /// - `&PropertyCallbackInfo` /// - `&PromiseRejectMessage` -pub struct CallbackScope<'s> { +pub struct CallbackScope<'s, C = Context> { data: NonNull, - _phantom: PhantomData<&'s mut HandleScope<'s>>, + _phantom: PhantomData<&'s mut HandleScope<'s, C>>, } impl<'s> CallbackScope<'s> { - pub unsafe fn new>(param: P) -> Self { - data::ScopeData::get_current_mut(param.get_isolate_mut()) - .new_callback_scope_data(param.maybe_get_current_context()) + #[allow(clippy::new_ret_no_self)] + pub unsafe fn new>(param: P) -> P::NewScope { + let (isolate, context) = param.get_isolate_mut_and_maybe_current_context(); + data::ScopeData::get_current_mut(isolate) + .new_callback_scope_data(context) .as_scope() } } @@ -480,7 +496,7 @@ impl_as!(<'s, 'p, P> ContextScope<'s, P> as Isolate); impl_as!(<'s, C> HandleScope<'s, C> as Isolate); impl_as!(<'s, 'e, C> EscapableHandleScope<'s, 'e, C> as Isolate); impl_as!(<'s, P> TryCatch<'s, P> as Isolate); -impl_as!(<'s> CallbackScope<'s> as Isolate); +impl_as!(<'s, C> CallbackScope<'s, C> as Isolate); impl_as!(<'s, 'p> ContextScope<'s, HandleScope<'p>> as HandleScope<'p, ()>); impl_as!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as HandleScope<'p, ()>); @@ -488,7 +504,7 @@ impl_as!(<'s, C> HandleScope<'s, C> as HandleScope<'s, ()>); impl_as!(<'s, 'e, C> EscapableHandleScope<'s, 'e, C> as HandleScope<'s, ()>); impl_as!(<'s, 'p, C> TryCatch<'s, HandleScope<'p, C>> as HandleScope<'p, ()>); impl_as!(<'s, 'p, 'e, C> TryCatch<'s, EscapableHandleScope<'p, 'e, C>> as HandleScope<'p, ()>); -impl_as!(<'s> CallbackScope<'s> as HandleScope<'s, ()>); +impl_as!(<'s, C> CallbackScope<'s, C> as HandleScope<'s, ()>); impl_as!(<'s, 'p> ContextScope<'s, HandleScope<'p>> as HandleScope<'p>); impl_as!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as HandleScope<'p>); @@ -534,7 +550,7 @@ macro_rules! impl_deref { impl_deref!(<'s, 'p> ContextScope<'s, HandleScope<'p>> as HandleScope<'p>); impl_deref!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e>); -impl_deref!(<'s> HandleScope<'s,()> as Isolate); +impl_deref!(<'s> HandleScope<'s, ()> as Isolate); impl_deref!(<'s> HandleScope<'s> as HandleScope<'s, ()>); impl_deref!(<'s, 'e> EscapableHandleScope<'s, 'e, ()> as HandleScope<'s, ()>); @@ -545,6 +561,7 @@ impl_deref!(<'s, 'p> TryCatch<'s, HandleScope<'p>> as HandleScope<'p>); impl_deref!(<'s, 'p, 'e> TryCatch<'s, EscapableHandleScope<'p, 'e, ()>> as EscapableHandleScope<'p, 'e, ()>); impl_deref!(<'s, 'p, 'e> TryCatch<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e>); +impl_deref!(<'s> CallbackScope<'s, ()> as HandleScope<'s, ()>); impl_deref!(<'s> CallbackScope<'s> as HandleScope<'s>); macro_rules! impl_scope_drop { @@ -563,7 +580,7 @@ impl_scope_drop!(<'s, 'p, P> ContextScope<'s, P>); impl_scope_drop!(<'s, C> HandleScope<'s, C> ); impl_scope_drop!(<'s, 'e, C> EscapableHandleScope<'s, 'e, C> ); impl_scope_drop!(<'s, P> TryCatch<'s, P> ); -impl_scope_drop!(<'s> CallbackScope<'s> ); +impl_scope_drop!(<'s, C> CallbackScope<'s, C> ); pub unsafe trait Scope: Sized {} @@ -600,7 +617,7 @@ impl ScopeCast for T { mod param { use super::*; - pub trait NewContextScope<'s>: data::GetScopeData { + pub trait NewContextScope<'s>: getter::GetScopeData { type NewScope: Scope; } @@ -624,11 +641,11 @@ mod param { type NewScope =

>::NewScope; } - impl<'s, 'p: 's> NewContextScope<'s> for CallbackScope<'p> { + impl<'s, 'p: 's, C> NewContextScope<'s> for CallbackScope<'p, C> { type NewScope = ContextScope<'s, HandleScope<'p>>; } - pub trait NewHandleScope<'s>: data::GetScopeData { + pub trait NewHandleScope<'s>: getter::GetScopeData { type NewScope: Scope; } @@ -660,11 +677,11 @@ mod param { type NewScope =

>::NewScope; } - impl<'s, 'p: 's> NewHandleScope<'s> for CallbackScope<'p> { - type NewScope = HandleScope<'s>; + impl<'s, 'p: 's, C> NewHandleScope<'s> for CallbackScope<'p, C> { + type NewScope = HandleScope<'s, C>; } - pub trait NewHandleScopeWithContext<'s>: data::GetScopeData { + pub trait NewHandleScopeWithContext<'s>: getter::GetScopeData { fn get_isolate_mut(&mut self) -> &mut Isolate; } @@ -680,7 +697,7 @@ mod param { } } - pub trait NewEscapableHandleScope<'s, 'e: 's>: data::GetScopeData { + pub trait NewEscapableHandleScope<'s, 'e: 's>: getter::GetScopeData { type NewScope: Scope; } @@ -706,11 +723,11 @@ mod param { type NewScope =

>::NewScope; } - impl<'s, 'p: 's> NewEscapableHandleScope<'s, 'p> for CallbackScope<'p> { - type NewScope = EscapableHandleScope<'s, 'p>; + impl<'s, 'p: 's, C> NewEscapableHandleScope<'s, 'p> for CallbackScope<'p, C> { + type NewScope = EscapableHandleScope<'s, 'p, C>; } - pub trait NewTryCatch<'s>: data::GetScopeData { + pub trait NewTryCatch<'s>: getter::GetScopeData { type NewScope: Scope; } @@ -732,59 +749,138 @@ mod param { type NewScope = TryCatch<'s, P>; } - impl<'s, 'p: 's> NewTryCatch<'s> for CallbackScope<'p> { - type NewScope = TryCatch<'s, HandleScope<'p>>; + impl<'s, 'p: 's, C> NewTryCatch<'s> for CallbackScope<'p, C> { + type NewScope = TryCatch<'s, HandleScope<'p, C>>; } - pub trait NewCallbackScope<'s>: Copy + Sized { - fn maybe_get_current_context(self) -> Option> { - None + pub trait NewCallbackScope<'s>: Sized + getter::GetIsolate<'s> { + type NewScope: Scope; + + unsafe fn get_isolate_mut_and_maybe_current_context( + self, + ) -> (&'s mut Isolate, Option>) { + (self.get_isolate_mut(), None) } - fn get_isolate_mut(self) -> &'s mut Isolate; + } + + impl<'s> NewCallbackScope<'s> for &'s mut Isolate { + type NewScope = CallbackScope<'s, ()>; + } + + impl<'s> NewCallbackScope<'s> for &'s mut OwnedIsolate { + type NewScope = CallbackScope<'s, ()>; + } + + impl<'s> NewCallbackScope<'s> for &'s FunctionCallbackInfo { + type NewScope = CallbackScope<'s>; + } + + impl<'s> NewCallbackScope<'s> for &'s PropertyCallbackInfo { + type NewScope = CallbackScope<'s>; } impl<'s> NewCallbackScope<'s> for Local<'s, Context> { - fn maybe_get_current_context(self) -> Option> { - Some(self) - } + type NewScope = CallbackScope<'s>; - fn get_isolate_mut(self) -> &'s mut Isolate { - unsafe { &mut *raw::v8__Context__GetIsolate(&*self) } + unsafe fn get_isolate_mut_and_maybe_current_context( + self, + ) -> (&'s mut Isolate, Option>) { + (getter::GetIsolate::get_isolate_mut(self), Some(self)) } } impl<'s> NewCallbackScope<'s> for Local<'s, Message> { - fn get_isolate_mut(self) -> &'s mut Isolate { - unsafe { &mut *raw::v8__Message__GetIsolate(&*self) } - } + type NewScope = CallbackScope<'s>; } - impl<'s, T> NewCallbackScope<'s> for T - where - T: Copy + Into>, - { - fn get_isolate_mut(self) -> &'s mut Isolate { - let object: Local = self.into(); - unsafe { &mut *raw::v8__Object__GetIsolate(&*object) } - } + impl<'s, T: Into>> NewCallbackScope<'s> for T { + type NewScope = CallbackScope<'s>; } impl<'s> NewCallbackScope<'s> for &'s PromiseRejectMessage<'s> { - fn get_isolate_mut(self) -> &'s mut Isolate { + type NewScope = CallbackScope<'s>; + } +} + +/// The private `getter` module defines traits to look up the related `Isolate` +/// and `ScopeData` for many different types. The implementation of those traits +/// on the types that implement them are also all contained in this module. +mod getter { + pub use super::*; + + pub trait GetIsolate<'s> { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate; + } + + impl<'s> GetIsolate<'s> for &'s mut Isolate { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate { + self + } + } + + impl<'s> GetIsolate<'s> for &'s mut OwnedIsolate { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate { + &mut *self + } + } + + impl<'s> GetIsolate<'s> for &'s FunctionCallbackInfo { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate { + &mut *raw::v8__FunctionCallbackInfo__GetIsolate(self) + } + } + + impl<'s> GetIsolate<'s> for &'s PropertyCallbackInfo { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate { + &mut *raw::v8__PropertyCallbackInfo__GetIsolate(self) + } + } + + impl<'s> GetIsolate<'s> for Local<'s, Context> { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate { + &mut *raw::v8__Context__GetIsolate(&*self) + } + } + + impl<'s> GetIsolate<'s> for Local<'s, Message> { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate { + &mut *raw::v8__Message__GetIsolate(&*self) + } + } + + impl<'s, T: Into>> GetIsolate<'s> for T { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate { + let object: Local = self.into(); + &mut *raw::v8__Object__GetIsolate(&*object) + } + } + + impl<'s> GetIsolate<'s> for &'s PromiseRejectMessage<'s> { + unsafe fn get_isolate_mut(self) -> &'s mut Isolate { let object: Local = self.get_promise().into(); - unsafe { &mut *raw::v8__Object__GetIsolate(&*object) } + &mut *raw::v8__Object__GetIsolate(&*object) } } - impl<'s> NewCallbackScope<'s> for &'s FunctionCallbackInfo { - fn get_isolate_mut(self) -> &'s mut Isolate { - unsafe { &mut *raw::v8__FunctionCallbackInfo__GetIsolate(self) } + pub trait GetScopeData { + fn get_scope_data_mut(&mut self) -> &mut data::ScopeData; + } + + impl GetScopeData for T { + fn get_scope_data_mut(&mut self) -> &mut data::ScopeData { + data::ScopeData::get_mut(self) } } - impl<'s> NewCallbackScope<'s> for &'s PropertyCallbackInfo { - fn get_isolate_mut(self) -> &'s mut Isolate { - unsafe { &mut *raw::v8__PropertyCallbackInfo__GetIsolate(self) } + impl GetScopeData for Isolate { + fn get_scope_data_mut(&mut self) -> &mut data::ScopeData { + data::ScopeData::get_root_mut(self) + } + } + + impl GetScopeData for OwnedIsolate { + fn get_scope_data_mut(&mut self) -> &mut data::ScopeData { + data::ScopeData::get_root_mut(self) } } } @@ -1341,28 +1437,6 @@ pub(crate) mod data { unsafe { ptr::write(self, (init_fn)()) } } } - - pub trait GetScopeData { - fn get_scope_data_mut(&mut self) -> &mut data::ScopeData; - } - - impl GetScopeData for T { - fn get_scope_data_mut(&mut self) -> &mut data::ScopeData { - data::ScopeData::get_mut(self) - } - } - - impl GetScopeData for Isolate { - fn get_scope_data_mut(&mut self) -> &mut data::ScopeData { - data::ScopeData::get_root_mut(self) - } - } - - impl GetScopeData for OwnedIsolate { - fn get_scope_data_mut(&mut self) -> &mut data::ScopeData { - data::ScopeData::get_root_mut(self) - } - } } /// The `raw` module contains prototypes for all the `extern C` functions that @@ -1677,6 +1751,15 @@ mod tests { let d = d.deref_mut(); AssertTypeOf(d).is::(); } + { + let isolate: &mut Isolate = l1_hs.as_mut(); + let l2_cbs = &mut unsafe { CallbackScope::new(isolate) }; + AssertTypeOf(l2_cbs).is::>(); + let d = l2_cbs.deref_mut(); + AssertTypeOf(d).is::>(); + let d = d.deref_mut(); + AssertTypeOf(d).is::(); + } } #[test] @@ -1789,6 +1872,17 @@ mod tests { } } } + { + let l1_cbs = &mut unsafe { CallbackScope::new(&mut *isolate) }; + AssertTypeOf(l1_cbs).is::>(); + let context = Context::new(l1_cbs); + AssertTypeOf(&ContextScope::new(l1_cbs, context)) + .is::>(); + AssertTypeOf(&HandleScope::new(l1_cbs)).is::>(); + AssertTypeOf(&EscapableHandleScope::new(l1_cbs)) + .is::>(); + AssertTypeOf(&TryCatch::new(l1_cbs)).is::>>(); + } { AssertTypeOf(&HandleScope::with_context(isolate, &global_context)) .is::();