diff --git a/src/array_buffer.rs b/src/array_buffer.rs index 5f587021..ad2bc7e5 100644 --- a/src/array_buffer.rs +++ b/src/array_buffer.rs @@ -1,9 +1,9 @@ use crate::support::Delete; use crate::support::Opaque; use crate::support::UniqueRef; -use crate::HandleScope; use crate::Isolate; use crate::Local; +use crate::ToLocal; extern "C" { fn v8__ArrayBuffer__Allocator__NewDefaultAllocator() -> *mut Allocator; @@ -117,13 +117,12 @@ impl ArrayBuffer { /// will be deallocated when it is garbage-collected, /// unless the object is externalized. pub fn new<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, byte_length: usize, ) -> Local<'sc, ArrayBuffer> { - unsafe { - let ptr = v8__ArrayBuffer__New(scope.as_mut(), byte_length); - Local::from_raw(ptr).unwrap() - } + let isolate = scope.isolate(); + let ptr = unsafe { v8__ArrayBuffer__New(isolate, byte_length) }; + unsafe { scope.to_local(ptr) }.unwrap() } /// Data length in bytes. @@ -139,12 +138,12 @@ impl ArrayBuffer { /// given isolate and re-try the allocation. If GCs do not help, then the /// function will crash with an out-of-memory error. pub fn new_backing_store<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, byte_length: usize, ) -> UniqueRef { unsafe { UniqueRef::from_raw(v8__ArrayBuffer__NewBackingStore( - scope.as_mut(), + scope.isolate(), byte_length, )) } diff --git a/src/array_buffer_view.rs b/src/array_buffer_view.rs index 4927e413..f8a79a7d 100644 --- a/src/array_buffer_view.rs +++ b/src/array_buffer_view.rs @@ -28,7 +28,7 @@ pub struct ArrayBufferView(Opaque); impl ArrayBufferView { /// Returns underlying ArrayBuffer. pub fn buffer<'sc>(&self) -> Option> { - unsafe { Local::from_raw(v8__ArrayBufferView__Buffer(self)) } + unsafe { Local::from_raw_(v8__ArrayBufferView__Buffer(self)) } } /// Size of a view in bytes. diff --git a/src/callback_scope.rs b/src/callback_scope.rs new file mode 100644 index 00000000..979c92fc --- /dev/null +++ b/src/callback_scope.rs @@ -0,0 +1,52 @@ +use crate::scope::{Scope, Scoped}; +use crate::Context; +use crate::InIsolate; +use crate::Isolate; +use crate::Local; +use crate::Message; +use std::mem::MaybeUninit; + +pub trait GetIsolate +where + Self: Sized, +{ + fn get_isolate(&mut self) -> &mut Isolate; +} + +impl GetIsolate for Message { + fn get_isolate(&mut self) -> &mut Isolate { + self.get_isolate() + } +} + +impl GetIsolate for Context { + fn get_isolate(&mut self) -> &mut Isolate { + self.get_isolate() + } +} + +pub struct CallbackScope<'s, T> { + local: Local<'s, T>, +} + +unsafe impl<'s, T> Scoped<'s> for CallbackScope<'s, T> { + type Args = Local<'s, T>; + fn enter_scope(buf: &mut MaybeUninit, local: Local<'s, T>) { + *buf = MaybeUninit::new(CallbackScope { local }); + } +} + +impl<'s, T> CallbackScope<'s, T> { + pub fn new(local: Local<'s, T>) -> Scope { + Scope::new(local) + } +} + +impl<'s, T> InIsolate for crate::scope::Entered<'s, CallbackScope<'s, T>> +where + T: GetIsolate, +{ + fn isolate(&mut self) -> &mut Isolate { + self.local.get_isolate() + } +} diff --git a/src/context.rs b/src/context.rs index a3b04d50..2e229154 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,9 +1,9 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. use crate::isolate::Isolate; use crate::support::Opaque; -use crate::HandleScope; use crate::Local; use crate::Object; +use crate::ToLocal; extern "C" { fn v8__Context__New(isolate: &Isolate) -> *mut Context; @@ -19,9 +19,10 @@ extern "C" { pub struct Context(Opaque); impl Context { - pub fn new<'sc>(scope: &mut HandleScope<'sc>) -> Local<'sc, Context> { + pub fn new<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, Context> { // TODO: optional arguments; - unsafe { Local::from_raw(v8__Context__New(scope.as_mut())).unwrap() } + let ptr = unsafe { v8__Context__New(scope.isolate()) }; + unsafe { scope.to_local(ptr) }.unwrap() } /// Returns the global proxy object. @@ -34,8 +35,11 @@ impl Context { /// Please note that changes to global proxy object prototype most probably /// would break VM---v8 expects only global object as a prototype of global /// proxy object. - pub fn global<'sc>(&mut self) -> Local<'sc, Object> { - unsafe { Local::from_raw(v8__Context__Global(&mut *self)).unwrap() } + pub fn global<'sc>( + &mut self, + scope: &mut impl ToLocal<'sc>, + ) -> Local<'sc, Object> { + unsafe { scope.to_local(v8__Context__Global(&mut *self)) }.unwrap() } /// Enter this context. After entering a context, all code compiled @@ -53,6 +57,10 @@ impl Context { // TODO: enter/exit should be controlled by a scope. unsafe { v8__Context__Exit(self) }; } + + pub fn get_isolate(&mut self) -> &mut Isolate { + unsafe { v8__Context__GetIsolate(self) } + } } impl AsRef for Context { diff --git a/src/exception.rs b/src/exception.rs index b55e8a56..5059aec6 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -3,9 +3,9 @@ use crate::isolate::Isolate; use crate::support::int; use crate::support::Opaque; -use crate::HandleScope; use crate::Local; use crate::String; +use crate::ToLocal; use crate::Value; extern "C" { @@ -46,13 +46,12 @@ impl StackTrace { pub struct Message(Opaque); impl Message { - pub fn get<'sc>(&self, _scope: &mut HandleScope<'sc>) -> Local<'sc, String> { - unsafe { Local::from_raw(v8__Message__Get(self)) }.unwrap() + pub fn get<'sc>(&self, scope: &mut impl ToLocal<'sc>) -> Local<'sc, String> { + unsafe { scope.to_local(v8__Message__Get(self)) }.unwrap() } - #[allow(clippy::mut_from_ref)] - pub unsafe fn get_isolate(&self) -> &mut Isolate { - v8__Message__GetIsolate(self) + pub fn get_isolate(&mut self) -> &mut Isolate { + unsafe { v8__Message__GetIsolate(self) } } } @@ -60,59 +59,55 @@ impl Message { /// Will try to reconstruct the original stack trace from the exception value, /// or capture the current stack trace if not available. pub fn create_message<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut exception: Local<'sc, Value>, ) -> Local<'sc, Message> { - unsafe { - Local::from_raw(v8__Exception__CreateMessage( - scope.as_mut(), - &mut *exception, - )) - } - .unwrap() + let isolate = scope.isolate(); + let ptr = unsafe { v8__Exception__CreateMessage(isolate, &mut *exception) }; + unsafe { scope.to_local(ptr) }.unwrap() } /// Returns the original stack trace that was captured at the creation time /// of a given exception, or an empty handle if not available. pub fn get_stack_trace<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut exception: Local, ) -> Option> { - unsafe { Local::from_raw(v8__Exception__GetStackTrace(&mut *exception)) } + unsafe { scope.to_local(v8__Exception__GetStackTrace(&mut *exception)) } } pub fn range_error<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut message: Local, ) -> Local<'sc, Value> { - unsafe { Local::from_raw(v8__Exception__RangeError(&mut *message)) }.unwrap() + unsafe { scope.to_local(v8__Exception__RangeError(&mut *message)) }.unwrap() } pub fn reference_error<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut message: Local, ) -> Local<'sc, Value> { - unsafe { Local::from_raw(v8__Exception__ReferenceError(&mut *message)) } + unsafe { scope.to_local(v8__Exception__ReferenceError(&mut *message)) } .unwrap() } pub fn syntax_error<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut message: Local, ) -> Local<'sc, Value> { - unsafe { Local::from_raw(v8__Exception__SyntaxError(&mut *message)) }.unwrap() + unsafe { scope.to_local(v8__Exception__SyntaxError(&mut *message)) }.unwrap() } pub fn type_error<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut message: Local, ) -> Local<'sc, Value> { - unsafe { Local::from_raw(v8__Exception__TypeError(&mut *message)) }.unwrap() + unsafe { scope.to_local(v8__Exception__TypeError(&mut *message)) }.unwrap() } pub fn error<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut message: Local, ) -> Local<'sc, Value> { - unsafe { Local::from_raw(v8__Exception__Error(&mut *message)) }.unwrap() + unsafe { scope.to_local(v8__Exception__Error(&mut *message)) }.unwrap() } diff --git a/src/function.rs b/src/function.rs index b033822b..c2d88b8b 100644 --- a/src/function.rs +++ b/src/function.rs @@ -3,9 +3,10 @@ use std::mem::MaybeUninit; use crate::support::{int, Opaque}; use crate::Context; -use crate::HandleScope; +use crate::InIsolate; use crate::Isolate; use crate::Local; +use crate::ToLocal; use crate::Value; extern "C" { @@ -62,8 +63,8 @@ impl<'cb> ReturnValue<'cb> { } /// Convenience getter for Isolate - pub fn get_isolate(&self) -> &Isolate { - unsafe { v8__ReturnValue__GetIsolate(self).as_ref().unwrap() } + pub fn get_isolate(&mut self) -> &mut Isolate { + unsafe { &mut *v8__ReturnValue__GetIsolate(self) } } /// Getter. Creates a new Local<> so it comes with a certain performance @@ -71,9 +72,9 @@ impl<'cb> ReturnValue<'cb> { /// value. pub fn get<'sc>( &mut self, - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, ) -> Local<'sc, Value> { - unsafe { Local::from_raw(v8__ReturnValue__Get(self)).unwrap() } + unsafe { scope.to_local(v8__ReturnValue__Get(self)) }.unwrap() } } @@ -84,6 +85,15 @@ impl<'cb> ReturnValue<'cb> { #[repr(C)] pub struct FunctionCallbackInfo(Opaque); +impl InIsolate for FunctionCallbackInfo { + #[allow(clippy::cast_ref_to_mut)] + fn isolate(&mut self) -> &mut Isolate { + self.get_isolate() + } +} + +impl<'s> ToLocal<'s> for FunctionCallbackInfo {} + impl FunctionCallbackInfo { /// The ReturnValue for the call. pub fn get_return_value(&self) -> ReturnValue { @@ -96,8 +106,8 @@ impl FunctionCallbackInfo { /// The current Isolate. #[allow(clippy::mut_from_ref)] - pub unsafe fn get_isolate(&self) -> &mut Isolate { - v8__FunctionCallbackInfo__GetIsolate(self) + pub fn get_isolate(&mut self) -> &mut Isolate { + unsafe { v8__FunctionCallbackInfo__GetIsolate(self) } } /// The number of available arguments. @@ -132,26 +142,22 @@ pub struct FunctionTemplate(Opaque); impl FunctionTemplate { /// Creates a function template. pub fn new<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, callback: extern "C" fn(&FunctionCallbackInfo), ) -> Local<'sc, FunctionTemplate> { - unsafe { - Local::from_raw(v8__FunctionTemplate__New(scope.as_mut(), callback)) - .unwrap() - } + let ptr = unsafe { v8__FunctionTemplate__New(scope.isolate(), callback) }; + unsafe { scope.to_local(ptr) }.unwrap() } /// Returns the unique function instance in the current execution context. pub fn get_function<'sc>( &mut self, - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut context: Local, ) -> Option> { unsafe { - Local::from_raw(v8__FunctionTemplate__GetFunction( - &mut *self, - &mut *context, - )) + scope + .to_local(v8__FunctionTemplate__GetFunction(&mut *self, &mut *context)) } } } @@ -165,16 +171,16 @@ impl Function { /// Create a function in the current execution context /// for a given FunctionCallback. pub fn new<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut context: Local, callback: extern "C" fn(&FunctionCallbackInfo), ) -> Option> { - unsafe { Local::from_raw(v8__Function__New(&mut *context, callback)) } + unsafe { scope.to_local(v8__Function__New(&mut *context, callback)) } } pub fn call<'sc>( &mut self, - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut context: Local, mut recv: Local, arc: i32, @@ -185,7 +191,7 @@ impl Function { argv_.push(&mut *arg); } unsafe { - Local::from_raw(v8__Function__Call( + scope.to_local(v8__Function__Call( &mut *self, &mut *context, &mut *recv, diff --git a/src/global.rs b/src/global.rs index 1d9ecd2b..a1377bf8 100644 --- a/src/global.rs +++ b/src/global.rs @@ -1,9 +1,10 @@ use std::mem::transmute; use std::ptr::NonNull; -use crate::HandleScope; +use crate::InIsolate; use crate::Isolate; use crate::Local; +use crate::ToLocal; use crate::Value; extern "C" { @@ -51,10 +52,10 @@ impl Global { /// is non-empty, a new storage cell is created pointing to the same object, /// and no flags are set. pub fn new_from( - isolate: &mut impl AsMut, + scope: &mut impl InIsolate, other: impl AnyHandle, ) -> Self { - let isolate = isolate.as_mut(); + let isolate = scope.isolate(); let other_value = other.read(isolate); Self { value: other_value @@ -72,23 +73,19 @@ impl Global { /// Construct a Local from this global handle. pub fn get<'sc>( &self, - scope: &'_ mut (impl AsMut + AsMut>), + scope: &mut impl ToLocal<'sc>, ) -> Option> { - self.check_isolate(scope.as_mut()); + self.check_isolate(scope.isolate()); match &self.value { None => None, - Some(p) => unsafe { Local::from_raw(p.as_ptr()) }, + Some(p) => unsafe { scope.to_local(p.as_ptr()) }, } } /// If non-empty, destroy the underlying storage cell /// and create a new one with the contents of other if other is non empty. - pub fn set( - &mut self, - isolate: &mut impl AsMut, - other: impl AnyHandle, - ) { - let isolate = isolate.as_mut(); + pub fn set(&mut self, scope: &mut impl InIsolate, other: impl AnyHandle) { + let isolate = scope.isolate(); self.check_isolate(isolate); let other_value = other.read(isolate); match (&mut self.value, &other_value) { @@ -111,8 +108,8 @@ impl Global { /// If non-empty, destroy the underlying storage cell /// IsEmpty() will return true after this call. - pub fn reset(&mut self, isolate: &mut impl AsMut) { - self.set(isolate, None); + pub fn reset(&mut self, scope: &mut impl InIsolate) { + self.set(scope, None); } fn check_isolate(&self, other: &Isolate) { diff --git a/src/handle_scope.rs b/src/handle_scope.rs index 77aa70c3..962f2ca0 100644 --- a/src/handle_scope.rs +++ b/src/handle_scope.rs @@ -1,7 +1,9 @@ -use std::marker::PhantomData; use std::mem::MaybeUninit; use crate::isolate::Isolate; +use crate::scope::Scope; +use crate::scope::Scoped; +use crate::InIsolate; use crate::Local; use crate::Value; @@ -30,101 +32,132 @@ extern "C" { } #[repr(C)] -pub struct HandleScope<'sc>([usize; 3], PhantomData<&'sc mut ()>); +pub struct HandleScope([usize; 3]); -impl<'sc> HandleScope<'sc> { - pub fn enter( - isolate: &mut impl AsMut, - mut f: impl FnMut(&mut HandleScope<'_>) -> (), - ) { - let isolate = isolate.as_mut(); - let mut scope: MaybeUninit = MaybeUninit::uninit(); - unsafe { v8__HandleScope__CONSTRUCT(&mut scope, isolate) }; - let scope = unsafe { &mut *(scope.as_mut_ptr()) }; - f(scope); - - unsafe { v8__HandleScope__DESTRUCT(scope) }; +impl HandleScope { + pub fn new(scope: &mut impl InIsolate) -> Scope { + Scope::new(scope.isolate()) } } -impl<'sc> AsRef> for HandleScope<'sc> { +unsafe impl<'s> Scoped<'s> for HandleScope { + type Args = &'s mut Isolate; + + fn enter_scope(buf: &mut MaybeUninit, isolate: &mut Isolate) { + unsafe { v8__HandleScope__CONSTRUCT(buf, isolate) }; + } +} + +impl Drop for HandleScope { + fn drop(&mut self) { + unsafe { v8__HandleScope__DESTRUCT(self) } + } +} + +impl AsRef for HandleScope { fn as_ref(&self) -> &Self { self } } -impl<'sc> AsMut> for HandleScope<'sc> { +impl AsMut for HandleScope { fn as_mut(&mut self) -> &mut Self { self } } -impl<'sc> AsRef for HandleScope<'sc> { +impl AsRef for HandleScope { fn as_ref(&self) -> &Isolate { unsafe { v8__HandleScope__GetIsolate(self) } } } -impl<'sc> AsMut for HandleScope<'sc> { +impl AsMut for HandleScope { fn as_mut(&mut self) -> &mut Isolate { unsafe { v8__HandleScope__GetIsolate(self) } } } -#[repr(C)] /// A HandleScope which first allocates a handle in the current scope /// which will be later filled with the escape value. -pub struct EscapableHandleScope<'sc>([usize; 4], PhantomData<&'sc mut ()>); +#[repr(C)] +pub struct EscapableHandleScope([usize; 4]); -impl<'sc> EscapableHandleScope<'sc> { - pub fn new(isolate: &mut impl AsMut) -> Self { - let isolate = isolate.as_mut(); - let mut scope: MaybeUninit = MaybeUninit::uninit(); - unsafe { - v8__EscapableHandleScope__CONSTRUCT(&mut scope, isolate); - scope.assume_init() - } +impl EscapableHandleScope { + pub fn new(scope: &mut impl InIsolate) -> Scope { + Scope::new(scope.isolate()) } /// Pushes the value into the previous scope and returns a handle to it. /// Cannot be called twice. - pub fn escape<'parent>( - &mut self, - mut value: Local<'sc, Value>, - ) -> Local<'parent, Value> { + pub fn escape<'parent, T>(&mut self, value: Local) -> Local<'parent, T> { unsafe { - Local::from_raw(v8__EscapableHandleScope__Escape(self, &mut *value)) - .unwrap() + Local::from_raw_(v8__EscapableHandleScope__Escape( + self, + value.as_ptr() as *mut Value, + ) as *mut T) } + .unwrap() } } -impl<'sc> Drop for EscapableHandleScope<'sc> { +unsafe impl<'s> Scoped<'s> for EscapableHandleScope { + type Args = &'s mut Isolate; + + fn enter_scope(buf: &mut MaybeUninit, isolate: &mut Isolate) { + unsafe { v8__EscapableHandleScope__CONSTRUCT(buf, isolate) }; + } +} + +impl Drop for EscapableHandleScope { fn drop(&mut self) { unsafe { v8__EscapableHandleScope__DESTRUCT(self) } } } -impl<'sc> AsRef> for EscapableHandleScope<'sc> { +impl AsRef for EscapableHandleScope { fn as_ref(&self) -> &Self { self } } -impl<'sc> AsMut> for EscapableHandleScope<'sc> { +impl AsMut for EscapableHandleScope { fn as_mut(&mut self) -> &mut Self { self } } -impl<'sc> AsRef for EscapableHandleScope<'sc> { +impl AsRef for EscapableHandleScope { fn as_ref(&self) -> &Isolate { unsafe { v8__EscapableHandleScope__GetIsolate(self) } } } -impl<'sc> AsMut for EscapableHandleScope<'sc> { +impl AsMut for EscapableHandleScope { fn as_mut(&mut self) -> &mut Isolate { unsafe { v8__EscapableHandleScope__GetIsolate(self) } } } + +use crate::scope::Entered; + +impl<'s> InIsolate for Entered<'s, HandleScope> { + fn isolate(&mut self) -> &mut Isolate { + unsafe { v8__HandleScope__GetIsolate(self) } + } +} + +impl<'s> InIsolate for Entered<'s, EscapableHandleScope> { + fn isolate(&mut self) -> &mut Isolate { + unsafe { v8__EscapableHandleScope__GetIsolate(self) } + } +} + +pub trait ToLocal<'sc>: InIsolate { + unsafe fn to_local(&mut self, ptr: *mut T) -> Option> { + crate::Local::<'sc, T>::from_raw_(ptr) + } +} + +impl<'s> ToLocal<'s> for crate::scope::Entered<'s, HandleScope> {} +impl<'s> ToLocal<'s> for crate::scope::Entered<'s, EscapableHandleScope> {} diff --git a/src/isolate.rs b/src/isolate.rs index 2b0ee068..4aca45d0 100644 --- a/src/isolate.rs +++ b/src/isolate.rs @@ -162,7 +162,7 @@ impl Isolate { ) -> Local<'sc, Value> { unsafe { let ptr = v8__Isolate__ThrowException(self, &exception); - Local::from_raw(ptr).unwrap() + Local::from_raw_(ptr).unwrap() } } @@ -207,6 +207,13 @@ impl DerefMut for OwnedIsolate { } } +/// Trait for retrieving the current isolate from a scope object. +pub trait InIsolate { + // Do not implement this trait on unscoped Isolate references + // (e.g. OwnedIsolate). + fn isolate(&mut self) -> &mut Isolate; +} + #[repr(C)] pub struct CreateParams(Opaque); diff --git a/src/json.rs b/src/json.rs index 5c7e218d..d56a510a 100644 --- a/src/json.rs +++ b/src/json.rs @@ -22,7 +22,7 @@ pub fn parse<'sc>( mut context: Local<'sc, Context>, mut json_string: Local<'sc, String>, ) -> Option> { - unsafe { Local::from_raw(v8__JSON__Parse(&mut *context, &mut *json_string)) } + unsafe { Local::from_raw_(v8__JSON__Parse(&mut *context, &mut *json_string)) } } /// Tries to stringify the JSON-serializable object `json_object` and returns @@ -32,6 +32,6 @@ pub fn stringify<'sc>( mut json_object: Local<'sc, Value>, ) -> Option> { unsafe { - Local::from_raw(v8__JSON__Stringify(&mut *context, &mut *json_object)) + Local::from_raw_(v8__JSON__Stringify(&mut *context, &mut *json_object)) } } diff --git a/src/lib.rs b/src/lib.rs index e00f152e..a89e11c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ extern crate lazy_static; extern crate libc; mod array_buffer; +mod callback_scope; mod context; mod exception; mod function; @@ -37,6 +38,7 @@ pub mod array_buffer_view; pub mod inspector; pub mod json; pub mod platform; +pub mod scope; pub mod script_compiler; // This module is intentionally named "V8" rather than "v8" to match the // C++ namespace "v8::V8". @@ -46,15 +48,15 @@ pub mod V8; pub use array_buffer::Allocator; pub use array_buffer::ArrayBuffer; pub use array_buffer::BackingStore; +pub use callback_scope::CallbackScope; pub use context::Context; pub use exception::*; pub use function::{ Function, FunctionCallbackInfo, FunctionTemplate, ReturnValue, }; pub use global::Global; -pub use handle_scope::{EscapableHandleScope, HandleScope}; -pub use isolate::Isolate; -pub use isolate::OwnedIsolate; +pub use handle_scope::{EscapableHandleScope, HandleScope, ToLocal}; +pub use isolate::{InIsolate, Isolate, OwnedIsolate}; pub use local::Local; pub use locker::Locker; pub use module::*; diff --git a/src/local.rs b/src/local.rs index e64427f7..5f64b61f 100644 --- a/src/local.rs +++ b/src/local.rs @@ -49,13 +49,17 @@ impl<'sc, T> Clone for Local<'sc, T> { } impl<'sc, T> Local<'sc, T> { - pub(crate) unsafe fn from_raw(ptr: *mut T) -> Option { + pub(crate) unsafe fn from_raw_(ptr: *mut T) -> Option { Some(Self(NonNull::new(ptr)?, PhantomData)) } pub(crate) fn as_non_null(self) -> NonNull { self.0 } + + pub(crate) fn as_ptr(self) -> *mut T { + self.0.as_ptr() + } } impl<'sc, T> Deref for Local<'sc, T> { diff --git a/src/locker.rs b/src/locker.rs index b278f6b5..b63ddb0d 100644 --- a/src/locker.rs +++ b/src/locker.rs @@ -1,5 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::isolate::Isolate; +use crate::InIsolate; +use crate::Isolate; use std::mem::MaybeUninit; extern "C" { @@ -29,6 +30,12 @@ impl Locker { } } +impl InIsolate for Locker { + fn isolate(&mut self) -> &mut Isolate { + unsafe { &mut *self.isolate } + } +} + impl Drop for Locker { fn drop(&mut self) { unsafe { v8__Locker__DESTRUCT(self) } diff --git a/src/module.rs b/src/module.rs index 2db549eb..99d12af5 100644 --- a/src/module.rs +++ b/src/module.rs @@ -4,6 +4,7 @@ use crate::support::Opaque; use crate::Context; use crate::Local; use crate::String; +use crate::ToLocal; use crate::Value; use std::mem::MaybeUninit; @@ -86,7 +87,7 @@ impl Module { /// For a module in kErrored status, this returns the corresponding exception. pub fn get_exception(&self) -> Local { - unsafe { Local::from_raw(v8__Module__GetException(self)).unwrap() } + unsafe { Local::from_raw_(v8__Module__GetException(self)).unwrap() } } /// Returns the number of modules requested by this module. @@ -97,7 +98,7 @@ impl Module { /// Returns the ith module specifier in this module. /// i must be < self.get_module_requests_length() and >= 0. pub fn get_module_request(&self, i: usize) -> Local { - unsafe { Local::from_raw(v8__Module__GetModuleRequest(self, i)).unwrap() } + unsafe { Local::from_raw_(v8__Module__GetModuleRequest(self, i)).unwrap() } } /// Returns the source location (line number and column number) of the ith @@ -161,10 +162,11 @@ impl Module { /// kErrored and propagate the thrown exception (which is then also available /// via |GetException|). #[must_use] - pub fn evaluate( + pub fn evaluate<'sc>( &mut self, + scope: &mut impl ToLocal<'sc>, mut context: Local, - ) -> Option> { - unsafe { Local::from_raw(v8__Module__Evaluate(&mut *self, &mut *context)) } + ) -> Option> { + unsafe { scope.to_local(v8__Module__Evaluate(&mut *self, &mut *context)) } } } diff --git a/src/number.rs b/src/number.rs index a1c552ab..17d875cf 100644 --- a/src/number.rs +++ b/src/number.rs @@ -4,6 +4,7 @@ use crate::isolate::Isolate; use crate::support::Opaque; use crate::value::Value; use crate::Local; +use crate::ToLocal; extern "C" { fn v8__Number__New(isolate: *mut Isolate, value: f64) -> *mut Number; @@ -22,13 +23,11 @@ pub struct Number(Opaque); impl Number { pub fn new<'sc>( - scope: &mut impl AsMut, + scope: &mut impl ToLocal<'sc>, value: f64, ) -> Local<'sc, Number> { - unsafe { - let local = v8__Number__New(scope.as_mut(), value); - Local::from_raw(local).unwrap() - } + let local = unsafe { v8__Number__New(scope.isolate(), value) }; + unsafe { scope.to_local(local) }.unwrap() } pub fn value(&self) -> f64 { @@ -49,23 +48,19 @@ pub struct Integer(Opaque); impl Integer { pub fn new<'sc>( - scope: &mut impl AsMut, + scope: &mut impl ToLocal<'sc>, value: i32, ) -> Local<'sc, Integer> { - unsafe { - let local = v8__Integer__New(scope.as_mut(), value); - Local::from_raw(local).unwrap() - } + let local = unsafe { v8__Integer__New(scope.isolate(), value) }; + unsafe { scope.to_local(local) }.unwrap() } pub fn new_from_unsigned<'sc>( - scope: &mut impl AsMut, + scope: &mut impl ToLocal<'sc>, value: u32, ) -> Local<'sc, Integer> { - unsafe { - let local = v8__Integer__NewFromUnsigned(scope.as_mut(), value); - Local::from_raw(local).unwrap() - } + let local = unsafe { v8__Integer__NewFromUnsigned(scope.isolate(), value) }; + unsafe { scope.to_local(local) }.unwrap() } pub fn value(&self) -> i64 { diff --git a/src/object.rs b/src/object.rs index 4ab2d51a..fc272f3c 100644 --- a/src/object.rs +++ b/src/object.rs @@ -4,9 +4,9 @@ use crate::isolate::Isolate; use crate::support::MaybeBool; use crate::support::Opaque; use crate::Context; -use crate::HandleScope; use crate::Local; use crate::Name; +use crate::ToLocal; use crate::Value; /// A JavaScript object (ECMA-262, 4.3.3) @@ -45,7 +45,7 @@ impl Object { /// All properties will be created as enumerable, configurable /// and writable properties. pub fn new<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut prototype_or_null: Local<'sc, Value>, names: Vec>, values: Vec>, @@ -62,16 +62,16 @@ impl Object { let n = &mut *value; values_.push(n); } - unsafe { - Local::from_raw(v8__Object__New( - scope.as_mut(), + let ptr = unsafe { + v8__Object__New( + scope.isolate(), &mut *prototype_or_null, names_.as_mut_ptr(), values_.as_mut_ptr(), length, - )) - .unwrap() - } + ) + }; + unsafe { scope.to_local(ptr) }.unwrap() } /// Implements CreateDataProperty (ECMA-262, 7.3.4). @@ -92,21 +92,18 @@ impl Object { pub fn get<'a>( &self, + scope: &mut impl ToLocal<'a>, context: Local, key: Local, ) -> Option> { unsafe { let ptr = v8__Object__Get(self, &*context, &*key); - if ptr.is_null() { - None - } else { - Some(Local::from_raw(ptr).unwrap()) - } + scope.to_local(ptr) } } /// Return the isolate to which the Object belongs to. - pub fn get_isolate(&self) -> &Isolate { + pub fn get_isolate(&mut self) -> &Isolate { unsafe { v8__Object__GetIsolate(self) } } } diff --git a/src/primitive_array.rs b/src/primitive_array.rs index ced20e7a..87c567f3 100644 --- a/src/primitive_array.rs +++ b/src/primitive_array.rs @@ -1,10 +1,10 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. use crate::support::int; use crate::support::Opaque; -use crate::HandleScope; use crate::Isolate; use crate::Local; use crate::Primitive; +use crate::ToLocal; extern "C" { fn v8__PrimitiveArray__New( @@ -38,13 +38,12 @@ pub struct PrimitiveArray(Opaque); impl PrimitiveArray { pub fn new<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, length: usize, ) -> Local<'sc, PrimitiveArray> { - unsafe { - let ptr = v8__PrimitiveArray__New(scope.as_mut(), length as int); - Local::from_raw(ptr).unwrap() - } + let ptr = + unsafe { v8__PrimitiveArray__New(scope.isolate(), length as int) }; + unsafe { scope.to_local(ptr) }.unwrap() } pub fn length(&self) -> usize { @@ -53,23 +52,22 @@ impl PrimitiveArray { pub fn set<'sc>( &self, - scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, index: usize, item: Local<'_, Primitive>, ) { unsafe { - v8__PrimitiveArray__Set(self, scope.as_mut(), index as int, &item) + v8__PrimitiveArray__Set(self, scope.isolate(), index as int, &item) } } pub fn get<'sc>( &self, - scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, index: usize, ) -> Local<'sc, Primitive> { - unsafe { - let ptr = v8__PrimitiveArray__Get(self, scope.as_mut(), index as int); - Local::from_raw(ptr).unwrap() - } + let ptr = + unsafe { v8__PrimitiveArray__Get(self, scope.isolate(), index as int) }; + unsafe { scope.to_local(ptr) }.unwrap() } } diff --git a/src/primitives.rs b/src/primitives.rs index bc636a60..c41a6c8f 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -3,6 +3,7 @@ use std::ops::Deref; use crate::isolate::Isolate; use crate::support::Opaque; use crate::Local; +use crate::ToLocal; use crate::Value; /// The superclass of primitive values. See ECMA-262 4.3.2. @@ -28,22 +29,26 @@ extern "C" { fn v8__False(isolate: *mut Isolate) -> *mut Boolean; } -pub fn new_null<'sc>(scope: &mut impl AsMut) -> Local<'sc, Primitive> { - unsafe { Local::from_raw(v8__Null(scope.as_mut())) }.unwrap() +pub fn new_null<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, Primitive> { + let ptr = unsafe { v8__Null(scope.isolate()) }; + unsafe { scope.to_local(ptr) }.unwrap() } pub fn new_undefined<'sc>( - scope: &mut impl AsMut, + scope: &mut impl ToLocal<'sc>, ) -> Local<'sc, Primitive> { - unsafe { Local::from_raw(v8__Undefined(scope.as_mut())) }.unwrap() + let ptr = unsafe { v8__Undefined(scope.isolate()) }; + unsafe { scope.to_local(ptr) }.unwrap() } -pub fn new_true<'sc>(scope: &mut impl AsMut) -> Local<'sc, Boolean> { - unsafe { Local::from_raw(v8__True(scope.as_mut())) }.unwrap() +pub fn new_true<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, Boolean> { + let ptr = unsafe { v8__True(scope.isolate()) }; + unsafe { scope.to_local(ptr) }.unwrap() } -pub fn new_false<'sc>(scope: &mut impl AsMut) -> Local<'sc, Boolean> { - unsafe { Local::from_raw(v8__False(scope.as_mut())) }.unwrap() +pub fn new_false<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, Boolean> { + let ptr = unsafe { v8__False(scope.isolate()) }; + unsafe { scope.to_local(ptr) }.unwrap() } impl Deref for Primitive { diff --git a/src/promise.rs b/src/promise.rs index 4d217a93..a3ddd330 100644 --- a/src/promise.rs +++ b/src/promise.rs @@ -4,8 +4,8 @@ use crate::support::MaybeBool; use crate::support::Opaque; use crate::Context; use crate::Function; -use crate::HandleScope; use crate::Local; +use crate::ToLocal; use crate::Value; extern "C" { @@ -82,9 +82,9 @@ impl Promise { /// be pending. pub fn result<'sc>( &mut self, - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, ) -> Local<'sc, Value> { - unsafe { Local::from_raw(v8__Promise__Result(&mut *self)).unwrap() } + unsafe { scope.to_local(v8__Promise__Result(&mut *self)) }.unwrap() } /// Register a rejection handler with a promise. @@ -96,7 +96,7 @@ impl Promise { mut handler: Local<'sc, Function>, ) -> Option> { unsafe { - Local::from_raw(v8__Promise__Catch( + Local::from_raw_(v8__Promise__Catch( &mut *self, &mut *context, &mut *handler, @@ -113,7 +113,7 @@ impl Promise { mut handler: Local<'sc, Function>, ) -> Option> { unsafe { - Local::from_raw(v8__Promise__Then( + Local::from_raw_(v8__Promise__Then( &mut *self, &mut *context, &mut *handler, @@ -132,7 +132,7 @@ impl Promise { mut on_rejected: Local<'sc, Function>, ) -> Option> { unsafe { - Local::from_raw(v8__Promise__Then2( + Local::from_raw_(v8__Promise__Then2( &mut *self, &mut *context, &mut *on_fulfilled, @@ -148,20 +148,19 @@ pub struct PromiseResolver(Opaque); impl PromiseResolver { /// Create a new resolver, along with an associated promise in pending state. pub fn new<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut context: Local<'sc, Context>, ) -> Option> { - unsafe { Local::from_raw(v8__Promise__Resolver__New(&mut *context)) } + unsafe { scope.to_local(v8__Promise__Resolver__New(&mut *context)) } } /// Extract the associated promise. pub fn get_promise<'sc>( &mut self, - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, ) -> Local<'sc, Promise> { - unsafe { - Local::from_raw(v8__Promise__Resolver__GetPromise(&mut *self)).unwrap() - } + unsafe { scope.to_local(v8__Promise__Resolver__GetPromise(&mut *self)) } + .unwrap() } /// Resolve the associated promise with a given value. @@ -206,7 +205,7 @@ pub struct PromiseRejectMessage<'msg>([usize; 3], PhantomData<&'msg ()>); impl<'msg> PromiseRejectMessage<'msg> { pub fn get_promise(&self) -> Local<'msg, Promise> { unsafe { - Local::from_raw(v8__PromiseRejectMessage__GetPromise(self)).unwrap() + Local::from_raw_(v8__PromiseRejectMessage__GetPromise(self)).unwrap() } } @@ -216,7 +215,7 @@ impl<'msg> PromiseRejectMessage<'msg> { pub fn get_value(&self) -> Local<'msg, Value> { unsafe { - Local::from_raw(v8__PromiseRejectMessage__GetValue(self)).unwrap() + Local::from_raw_(v8__PromiseRejectMessage__GetValue(self)).unwrap() } } } diff --git a/src/property.rs b/src/property.rs index 1cd98509..2065d404 100644 --- a/src/property.rs +++ b/src/property.rs @@ -3,6 +3,7 @@ use crate::support::Opaque; use crate::Local; use crate::Object; use crate::ReturnValue; + use std::mem::MaybeUninit; extern "C" { @@ -30,11 +31,11 @@ impl PropertyCallbackInfo { } #[allow(clippy::mut_from_ref)] - pub unsafe fn get_isolate(&self) -> &mut Isolate { - v8__PropertyCallbackInfo__GetIsolate(self) + pub fn get_isolate(&mut self) -> &mut Isolate { + unsafe { v8__PropertyCallbackInfo__GetIsolate(self) } } pub fn this(&self) -> Local { - unsafe { Local::from_raw(v8__PropertyCallbackInfo__This(self)).unwrap() } + unsafe { Local::from_raw_(v8__PropertyCallbackInfo__This(self)).unwrap() } } } diff --git a/src/scope.rs b/src/scope.rs new file mode 100644 index 00000000..ed360bc4 --- /dev/null +++ b/src/scope.rs @@ -0,0 +1,149 @@ +use std::marker::PhantomData; +use std::mem::size_of; +use std::mem::take; +use std::mem::MaybeUninit; +use std::ops::Deref; +use std::ops::DerefMut; + +// Note: the 's lifetime is there to ensure that after entering a scope once, +// the same scope object can't ever be entered again. + +/// A trait for defining scoped objects. +pub unsafe trait Scoped<'s> +where + Self: Sized, +{ + type Args; + fn enter_scope(buf: &mut MaybeUninit, args: Self::Args) -> (); +} + +/// A RAII scope wrapper object that will, when the `enter()` method is called, +/// initialize and activate the guarded object. +pub struct Scope<'s, S>(ScopeState<'s, S>) +where + S: Scoped<'s>; + +enum ScopeState<'s, S> +where + S: Scoped<'s>, +{ + Empty, + New(S::Args), + Uninit(MaybeUninit), + Ready(Entered<'s, S>), +} + +/// A wrapper around the an instantiated and entered scope object. +#[repr(transparent)] +pub struct Entered<'s, S>(S, PhantomData<&'s ()>); + +impl<'s, S> Scope<'s, S> +where + S: Scoped<'s>, +{ + /// Create a new Scope object in unentered state. + pub(crate) fn new(args: S::Args) -> Self { + Self(ScopeState::New(args)) + } + + /// Initializes the guarded object and returns a mutable reference to it. + /// A scope can only be entered once. + pub fn enter(&'s mut self) -> &'s mut Entered { + assert_eq!(size_of::(), size_of::>()); + assert_eq!(size_of::(), size_of::>()); + + use ScopeState::*; + let state = &mut self.0; + + let args = match take(state) { + New(f) => f, + _ => unreachable!(), + }; + + *state = Uninit(MaybeUninit::uninit()); + let buf = match state { + Uninit(b) => b, + _ => unreachable!(), + }; + + S::enter_scope(buf, args); + + *state = match take(state) { + Uninit(b) => Ready(unsafe { b.assume_init() }.into()), + _ => unreachable!(), + }; + + match state { + Ready(v) => &mut *v, + _ => unreachable!(), + } + } +} + +impl<'s, S> Default for ScopeState<'s, S> +where + S: Scoped<'s>, +{ + fn default() -> Self { + Self::Empty + } +} + +impl<'s, S> From for Entered<'s, S> { + fn from(value: S) -> Self { + Self(value, PhantomData) + } +} + +impl<'s, S> Deref for Entered<'s, S> { + type Target = S; + fn deref(&self) -> &S { + unsafe { &*(self as *const _ as *const S) } + } +} + +impl<'s, S> DerefMut for Entered<'s, S> { + fn deref_mut(&mut self) -> &mut S { + unsafe { &mut *(self as *mut _ as *mut S) } + } +} + +/* +impl<'s, S, T> AsRef for Entered<'s, S> +where + S: AsRef, +{ + fn as_ref(&self) -> &T { + self.deref().as_ref() + } +} + +impl<'s, S, T> AsMut for Entered<'s, S> +where + S: AsMut, +{ + fn as_mut(&mut self) -> &mut T { + self.deref_mut().as_mut() + } +} +*/ + +impl<'s, S, T> AsRef> for Entered<'s, S> +where + S: AsRef, +{ + fn as_ref(&self) -> &Entered<'s, T> { + let t: &T = self.deref().as_ref(); + unsafe { &*(t as *const _ as *const Entered<'s, T>) } + } +} + +impl<'s, S, T> AsMut> for Entered<'s, S> +where + S: AsMut, +{ + fn as_mut(&mut self) -> &mut Entered<'s, T> { + let t: &mut T = self.deref_mut().as_mut(); + unsafe { &mut *(t as *mut _ as *mut Entered<'s, T>) } + } +} diff --git a/src/script.rs b/src/script.rs index d2004cb7..a11fdaa4 100644 --- a/src/script.rs +++ b/src/script.rs @@ -5,10 +5,10 @@ use std::ptr::null; use crate::support::Opaque; use crate::Boolean; use crate::Context; -use crate::HandleScope; use crate::Integer; use crate::Local; use crate::String; +use crate::ToLocal; use crate::Value; /// The origin, within a file, of a script. @@ -45,20 +45,21 @@ pub struct Script(Opaque); impl Script { /// A shorthand for ScriptCompiler::Compile(). pub fn compile<'sc>( - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut context: Local, mut source: Local, origin: Option<&ScriptOrigin>, ) -> Option> { // TODO: use the type system to enforce that a Context has been entered. // TODO: `context` and `source` probably shouldn't be mut. - unsafe { - Local::from_raw(v8__Script__Compile( + let ptr = unsafe { + v8__Script__Compile( &mut *context, &mut *source, origin.map(|r| r as *const _).unwrap_or(null()), - )) - } + ) + }; + unsafe { scope.to_local(ptr) } } /// Runs the script returning the resulting value. It will be run in the @@ -66,10 +67,10 @@ impl Script { /// UnboundScript::BindToCurrentContext()). pub fn run<'sc>( &mut self, - _scope: &mut HandleScope<'sc>, + scope: &mut impl ToLocal<'sc>, mut context: Local, ) -> Option> { - unsafe { Local::from_raw(v8__Script__Run(self, &mut *context)) } + unsafe { scope.to_local(v8__Script__Run(self, &mut *context)) } } } diff --git a/src/script_compiler.rs b/src/script_compiler.rs index 36a4f755..0ae42c57 100644 --- a/src/script_compiler.rs +++ b/src/script_compiler.rs @@ -76,10 +76,10 @@ pub enum NoCacheReason { /// /// Corresponds to the ParseModule abstract operation in the ECMAScript /// specification. -pub fn compile_module( +pub fn compile_module<'a>( isolate: &Isolate, source: Source, -) -> Option> { +) -> Option> { compile_module2( isolate, source, @@ -89,14 +89,14 @@ pub fn compile_module( } /// Same as compile_module with more options. -pub fn compile_module2( +pub fn compile_module2<'a>( isolate: &Isolate, source: Source, options: CompileOptions, no_cache_reason: NoCacheReason, -) -> Option> { +) -> Option> { unsafe { - Local::from_raw(v8__ScriptCompiler__CompileModule( + Local::from_raw_(v8__ScriptCompiler__CompileModule( isolate, &source, options, diff --git a/src/script_or_module.rs b/src/script_or_module.rs index 3423b0a9..c9048834 100644 --- a/src/script_or_module.rs +++ b/src/script_or_module.rs @@ -25,7 +25,7 @@ impl ScriptOrModule { pub fn get_resource_name(&self) -> Local<'_, Value> { unsafe { let ptr = v8__ScriptOrModule__GetResourceName(self); - Local::from_raw(ptr).unwrap() + Local::from_raw_(ptr).unwrap() } } @@ -34,7 +34,7 @@ impl ScriptOrModule { pub fn get_host_defined_options(&self) -> Local<'_, PrimitiveArray> { unsafe { let ptr = v8__ScriptOrModule__GetHostDefinedOptions(self); - Local::from_raw(ptr).unwrap() + Local::from_raw_(ptr).unwrap() } } } diff --git a/src/string.rs b/src/string.rs index 5577515c..24b083cd 100644 --- a/src/string.rs +++ b/src/string.rs @@ -4,11 +4,13 @@ use std::mem::forget; use std::ops::Deref; use std::slice; -use crate::isolate::Isolate; use crate::support::char; use crate::support::int; use crate::support::Opaque; +use crate::InIsolate; +use crate::Isolate; use crate::Local; +use crate::ToLocal; use crate::Value; extern "C" { @@ -66,19 +68,19 @@ pub struct String(Opaque); impl String { pub fn new_from_utf8<'sc>( - scope: &mut impl AsMut, + scope: &mut impl ToLocal<'sc>, buffer: &[u8], new_type: NewStringType, ) -> Option> { - unsafe { - let ptr = v8__String__NewFromUtf8( - scope.as_mut(), + let ptr = unsafe { + v8__String__NewFromUtf8( + scope.isolate(), buffer.as_ptr() as *const char, new_type, buffer.len().try_into().ok()?, - ); - Local::from_raw(ptr) - } + ) + }; + unsafe { scope.to_local(ptr) } } /// Returns the number of characters (UTF-16 code units) in this string. @@ -88,13 +90,13 @@ impl String { /// Returns the number of bytes in the UTF-8 encoded representation of this /// string. - pub fn utf8_length(&self, isolate: &mut impl AsMut) -> usize { - unsafe { v8__String__Utf8Length(self, isolate.as_mut()) as usize } + pub fn utf8_length(&self, scope: &mut impl InIsolate) -> usize { + unsafe { v8__String__Utf8Length(self, scope.isolate()) as usize } } pub fn write_utf8( &self, - isolate: &mut Isolate, + scope: &mut impl InIsolate, buffer: &mut [u8], nchars_ref: Option<&mut usize>, options: WriteOptions, @@ -103,7 +105,7 @@ impl String { let bytes = unsafe { v8__String__WriteUtf8( self, - isolate, + scope.isolate(), buffer.as_mut_ptr() as *mut char, buffer.len().try_into().unwrap_or(int::max_value()), &mut nchars_ref_int, @@ -118,7 +120,7 @@ impl String { // Convenience function not present in the original V8 API. pub fn new<'sc>( - scope: &mut impl AsMut, + scope: &mut impl ToLocal<'sc>, value: &str, ) -> Option> { Self::new_from_utf8(scope, value.as_ref(), NewStringType::Normal) @@ -127,15 +129,14 @@ impl String { // Convenience function not present in the original V8 API. pub fn to_rust_string_lossy( &self, - isolate: &mut impl AsMut, + scope: &mut impl InIsolate, ) -> std::string::String { - let isolate = isolate.as_mut(); - let capacity = self.utf8_length(isolate); + let capacity = self.utf8_length(scope); let mut string = std::string::String::with_capacity(capacity); let data = string.as_mut_ptr(); forget(string); let length = self.write_utf8( - isolate, + scope, unsafe { slice::from_raw_parts_mut(data, capacity) }, None, WriteOptions::NO_NULL_TERMINATION | WriteOptions::REPLACE_INVALID_UTF8, diff --git a/src/try_catch.rs b/src/try_catch.rs index 0732cc6f..e1501da6 100644 --- a/src/try_catch.rs +++ b/src/try_catch.rs @@ -5,10 +5,11 @@ use std::mem::take; use std::mem::MaybeUninit; use crate::Context; -use crate::HandleScope; +use crate::InIsolate; use crate::Isolate; use crate::Local; use crate::Message; +use crate::ToLocal; use crate::Value; extern "C" { @@ -71,9 +72,9 @@ impl<'tc> TryCatch<'tc> { /// stack allocated because the memory location itself is compared against /// JavaScript try/catch blocks. #[allow(clippy::new_ret_no_self)] - pub fn new(scope: &mut impl AsMut) -> TryCatchScope<'tc> { + pub fn new(scope: &mut impl InIsolate) -> TryCatchScope<'tc> { TryCatchScope(TryCatchState::New { - isolate: scope.as_mut(), + isolate: scope.isolate(), }) } @@ -111,17 +112,17 @@ impl<'tc> TryCatch<'tc> { /// /// The returned handle is valid until this TryCatch block has been destroyed. pub fn exception(&self) -> Option> { - unsafe { Local::from_raw(v8__TryCatch__Exception(&self.0)) } + unsafe { Local::from_raw_(v8__TryCatch__Exception(&self.0)) } } /// Returns the .stack property of the thrown object. If no .stack /// property is present an empty handle is returned. pub fn stack_trace<'sc>( &self, - _scope: &mut impl AsMut>, + scope: &mut impl ToLocal<'sc>, context: Local, ) -> Option> { - unsafe { Local::from_raw(v8__TryCatch__StackTrace(&self.0, context)) } + unsafe { scope.to_local(v8__TryCatch__StackTrace(&self.0, context)) } } /// Returns the message associated with this exception. If there is @@ -130,7 +131,7 @@ impl<'tc> TryCatch<'tc> { /// The returned handle is valid until this TryCatch block has been /// destroyed. pub fn message(&self) -> Option> { - unsafe { Local::from_raw(v8__TryCatch__Message(&self.0)) } + unsafe { Local::from_raw_(v8__TryCatch__Message(&self.0)) } } /// Clears any exceptions that may have been caught by this try/catch block. @@ -151,7 +152,7 @@ impl<'tc> TryCatch<'tc> { /// ReThrow; the caller must return immediately to where the exception /// is caught. pub fn rethrow<'a>(&'_ mut self) -> Option> { - unsafe { Local::from_raw(v8__TryCatch__ReThrow(&mut self.0)) } + unsafe { Local::from_raw_(v8__TryCatch__ReThrow(&mut self.0)) } } /// Returns true if verbosity is enabled. diff --git a/tests/compile_fail/handle_scope_lifetimes.rs b/tests/compile_fail/handle_scope_lifetimes.rs new file mode 100644 index 00000000..0b04ba6c --- /dev/null +++ b/tests/compile_fail/handle_scope_lifetimes.rs @@ -0,0 +1,41 @@ +extern crate rusty_v8 as v8; + +pub fn main() { + let mut isolate: v8::scope::Entered<'_, v8::HandleScope> = mock(); + + { + let mut hs = v8::EscapableHandleScope::new(&mut isolate); + let hs = hs.enter(); + let _fail = v8::EscapableHandleScope::new(&mut isolate); + let _local = v8::Integer::new(hs, 123); + } + + { + let mut hs1 = v8::EscapableHandleScope::new(&mut isolate); + let hs1 = hs1.enter(); + let _local1 = v8::Integer::new(hs1, 123); + + let mut hs2 = v8::EscapableHandleScope::new(hs1); + let hs2 = hs2.enter(); + let _fail = v8::Integer::new(hs1, 123); + let _local2 = v8::Integer::new(hs2, 123); + let _local3 = v8::Integer::new(hs2, 123); + } + + let _leak1 = { + let mut hs = v8::EscapableHandleScope::new(&mut isolate); + let hs = hs.enter(); + v8::Integer::new(hs, 456) + }; + + let _leak = { + let mut hs = v8::EscapableHandleScope::new(&mut isolate); + hs.enter() + }; +} + +fn mock() -> T { + unimplemented!() +} + +fn access(_value: T) {} diff --git a/tests/compile_fail/handle_scope_lifetimes.stderr b/tests/compile_fail/handle_scope_lifetimes.stderr new file mode 100644 index 00000000..f4bf7d93 --- /dev/null +++ b/tests/compile_fail/handle_scope_lifetimes.stderr @@ -0,0 +1,44 @@ +error[E0499]: cannot borrow `isolate` as mutable more than once at a time + --> $DIR/handle_scope_lifetimes.rs:9:47 + | +7 | let mut hs = v8::EscapableHandleScope::new(&mut isolate); + | ------------ first mutable borrow occurs here +8 | let hs = hs.enter(); +9 | let _fail = v8::EscapableHandleScope::new(&mut isolate); + | ^^^^^^^^^^^^ second mutable borrow occurs here +10 | let _local = v8::Integer::new(hs, 123); + | -- first borrow later used here + +error[E0499]: cannot borrow `*hs1` as mutable more than once at a time + --> $DIR/handle_scope_lifetimes.rs:20:34 + | +18 | let mut hs2 = v8::EscapableHandleScope::new(hs1); + | --- first mutable borrow occurs here +19 | let hs2 = hs2.enter(); +20 | let _fail = v8::Integer::new(hs1, 123); + | ^^^ second mutable borrow occurs here +21 | let _local2 = v8::Integer::new(hs2, 123); + | --- first borrow later used here + +error[E0597]: `hs` does not live long enough + --> $DIR/handle_scope_lifetimes.rs:27:14 + | +25 | let _leak1 = { + | ------ borrow later stored here +26 | let mut hs = v8::EscapableHandleScope::new(&mut isolate); +27 | let hs = hs.enter(); + | ^^ borrowed value does not live long enough +28 | v8::Integer::new(hs, 456) +29 | }; + | - `hs` dropped here while still borrowed + +error[E0597]: `hs` does not live long enough + --> $DIR/handle_scope_lifetimes.rs:33:5 + | +33 | hs.enter() + | ^^-------- + | | + | borrowed value does not live long enough + | borrow later used here +34 | }; + | - `hs` dropped here while still borrowed diff --git a/tests/compile_fail/try_catch_lifetimes.rs b/tests/compile_fail/try_catch_lifetimes.rs index 6fd1732e..09254419 100644 --- a/tests/compile_fail/try_catch_lifetimes.rs +++ b/tests/compile_fail/try_catch_lifetimes.rs @@ -2,7 +2,7 @@ extern crate rusty_v8 as v8; pub fn main() { let context: v8::Local = mock(); - let scope: &mut v8::HandleScope<'_> = mock(); + let scope: &mut v8::scope::Entered<'_, v8::HandleScope> = mock(); let _leaked = { let mut try_catch = v8::TryCatch::new(scope); diff --git a/tests/test_api.rs b/tests/test_api.rs index f0ffb4dd..85fa2d78 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -4,7 +4,7 @@ extern crate lazy_static; use rusty_v8 as v8; -use rusty_v8::{new_null, FunctionCallbackInfo, HandleScope, Local}; +use rusty_v8::{new_null, FunctionCallbackInfo, InIsolate, Local, ToLocal}; use std::sync::Mutex; lazy_static! { @@ -45,9 +45,14 @@ fn handle_scope_nested() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope1| { - v8::HandleScope::enter(scope1, |_scope2| {}); - }); + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope1 = hs.enter(); + { + let mut hs = v8::HandleScope::new(scope1); + let _scope2 = hs.enter(); + } + } drop(locker); drop(g); } @@ -60,18 +65,22 @@ fn handle_scope_numbers() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope1| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope1 = hs.enter(); let l1 = v8::Integer::new(scope1, -123); let l2 = v8::Integer::new_from_unsigned(scope1, 456); - v8::HandleScope::enter(scope1, |scope2| { + { + let mut hs = v8::HandleScope::new(scope1); + let scope2 = hs.enter(); let l3 = v8::Number::new(scope2, 78.9); assert_eq!(l1.value(), -123); assert_eq!(l2.value(), 456); assert_eq!(l3.value(), 78.9); assert_eq!(v8::Number::value(&l1), -123f64); assert_eq!(v8::Number::value(&l2), 456f64); - }); - }); + } + } drop(locker); drop(g); } @@ -86,35 +95,39 @@ fn global_handles() { let mut g1 = v8::Global::::new(); let mut g2 = v8::Global::::new(); let mut g3 = v8::Global::::new(); - let mut g4 = v8::Global::::new(); + let mut _g4 = v8::Global::::new(); let g5 = v8::Global::::new(); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let l1 = v8::String::new(scope, "bla").unwrap(); let l2 = v8::Integer::new(scope, 123); g1.set(scope, l1); g2.set(scope, l2); g3.set(scope, &g2); - g4 = v8::Global::new_from(scope, l2); - }); - v8::HandleScope::enter(&mut locker, |scope| { + _g4 = v8::Global::new_from(scope, l2); + } + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); assert!(!g1.is_empty()); assert_eq!(g1.get(scope).unwrap().to_rust_string_lossy(scope), "bla"); assert!(!g2.is_empty()); assert_eq!(g2.get(scope).unwrap().value(), 123); assert!(!g3.is_empty()); assert_eq!(g3.get(scope).unwrap().value(), 123); - assert!(!g4.is_empty()); - assert_eq!(g4.get(scope).unwrap().value(), 123); + assert!(!_g4.is_empty()); + assert_eq!(_g4.get(scope).unwrap().value(), 123); assert!(g5.is_empty()); - }); + } g1.reset(&mut locker); assert!(g1.is_empty()); g2.reset(&mut locker); assert!(g2.is_empty()); g3.reset(&mut locker); assert!(g3.is_empty()); - g4.reset(&mut locker); - assert!(g4.is_empty()); + _g4.reset(&mut locker); + assert!(_g4.is_empty()); assert!(g5.is_empty()); } @@ -125,13 +138,15 @@ fn test_string() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let reference = "Hello 🦕 world!"; let local = v8::String::new(scope, reference).unwrap(); assert_eq!(15, local.length()); assert_eq!(17, local.utf8_length(scope)); assert_eq!(reference, local.to_rust_string_lossy(scope)); - }); + } drop(locker); } @@ -144,42 +159,43 @@ fn escapable_handle_scope() { let mut isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); isolate.enter(); - v8::HandleScope::enter(&mut locker, |scope1| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope1 = hs.enter(); // After dropping EscapableHandleScope, we should be able to // read escaped values. let number_val = { - let mut escapable_scope = v8::EscapableHandleScope::new(scope1); + let mut hs = v8::EscapableHandleScope::new(scope1); + let escapable_scope = hs.enter(); let number: Local = - cast(v8::Number::new(&mut escapable_scope, 78.9)); + cast(v8::Number::new(escapable_scope, 78.9)); escapable_scope.escape(number) }; let number: Local = cast(number_val); assert_eq!(number.value(), 78.9); - let str_val = { - let mut escapable_scope = v8::EscapableHandleScope::new(scope1); - let string = - v8::String::new(&mut escapable_scope, "Hello 🦕 world!").unwrap(); - escapable_scope.escape(cast(string)) + let string = { + let mut hs = v8::EscapableHandleScope::new(scope1); + let escapable_scope = hs.enter(); + let string = v8::String::new(escapable_scope, "Hello 🦕 world!").unwrap(); + escapable_scope.escape(string) }; - let string: Local = cast(str_val); assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1)); - let str_val = { - let mut escapable_scope = v8::EscapableHandleScope::new(scope1); + let string = { + let mut hs = v8::EscapableHandleScope::new(scope1); + let escapable_scope = hs.enter(); let nested_str_val = { - let mut nested_escapable_scope = - v8::EscapableHandleScope::new(&mut escapable_scope); + let mut hs = v8::EscapableHandleScope::new(escapable_scope); + let nested_escapable_scope = hs.enter(); let string = - v8::String::new(&mut nested_escapable_scope, "Hello 🦕 world!") - .unwrap(); - nested_escapable_scope.escape(cast(string)) + v8::String::new(nested_escapable_scope, "Hello 🦕 world!").unwrap(); + nested_escapable_scope.escape(string) }; escapable_scope.escape(nested_str_val) }; - let string: Local = cast(str_val); assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1)); - }); + } drop(locker); isolate.exit(); drop(g); @@ -192,7 +208,9 @@ fn array_buffer() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); @@ -204,26 +222,29 @@ fn array_buffer() { assert_eq!(false, bs.is_shared()); context.exit(); - }); + } drop(locker); } fn v8_str<'sc>( - isolate: &mut impl AsMut, + scope: &mut impl v8::ToLocal<'sc>, s: &str, ) -> v8::Local<'sc, v8::String> { - v8::String::new(isolate, s).unwrap() + v8::String::new(scope, s).unwrap() } fn eval<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl v8::InIsolate, context: Local, code: &'static str, ) -> Option> { + let mut hs = v8::EscapableHandleScope::new(scope); + let scope = hs.enter(); let source = v8_str(scope, code); let mut script = v8::Script::compile(&mut *scope, context, source, None).unwrap(); - script.run(scope, context) + let r = script.run(scope, context); + r.map(|v| scope.escape(v)) } #[test] @@ -233,7 +254,9 @@ fn try_catch() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); { @@ -279,7 +302,7 @@ fn try_catch() { assert!(tc1.has_caught()); }; context.exit(); - }); + } } #[test] @@ -289,7 +312,9 @@ fn throw_exception() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); { @@ -303,7 +328,7 @@ fn throw_exception() { .strict_equals(v8_str(scope, "boom").into())); }; context.exit(); - }); + } } #[test] @@ -322,16 +347,21 @@ fn add_message_listener() { _exception: Local, ) { CALL_COUNT.fetch_add(1, Ordering::SeqCst); - let isolate = unsafe { message.get_isolate() }; - v8::HandleScope::enter(isolate, |scope| { + let mut cbs = v8::CallbackScope::new(message); + let scope = cbs.enter(); + { + let mut hs = v8::HandleScope::new(scope); + let scope = hs.enter(); let message_str = message.get(scope); assert_eq!(message_str.to_rust_string_lossy(scope), "Uncaught foo"); - }); + } } isolate.add_message_listener(check_message_0); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |s| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let s = hs.enter(); let mut context = v8::Context::new(s); context.enter(); let source = v8::String::new(s, "throw 'foo'").unwrap(); @@ -339,7 +369,7 @@ fn add_message_listener() { assert!(script.run(s, context).is_none()); assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1); context.exit(); - }); + } drop(locker); drop(g); } @@ -363,39 +393,43 @@ fn set_host_initialize_import_meta_object_callback() { static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); extern "C" fn callback( - mut context: Local, + context: Local, _module: Local, meta: Local, ) { CALL_COUNT.fetch_add(1, Ordering::SeqCst); - let key = v8::String::new(&mut *context, "foo").unwrap(); - let value = v8::String::new(&mut *context, "bar").unwrap(); + let mut cbs = v8::CallbackScope::new(context); + let mut hs = v8::HandleScope::new(cbs.enter()); + let scope = hs.enter(); + let key = v8::String::new(scope, "foo").unwrap(); + let value = v8::String::new(scope, "bar").unwrap(); meta.create_data_property(context, cast(key), value.into()); } isolate.set_host_initialize_import_meta_object_callback(callback); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |s| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let s = hs.enter(); let mut context = v8::Context::new(s); context.enter(); - let source = mock_source(s, "google.com", "import.meta;"); let mut module = v8::script_compiler::compile_module(&isolate, source).unwrap(); let result = module.instantiate_module(context, unexpected_module_resolve_callback); assert!(result.is_some()); - let meta = module.evaluate(context).unwrap(); + let meta = module.evaluate(s, context).unwrap(); assert!(meta.is_object()); let meta: Local = cast(meta); - let key = v8::String::new(&mut *context, "foo").unwrap(); - let expected = v8::String::new(&mut *context, "bar").unwrap(); - let actual = meta.get(context, key.into()).unwrap(); + let key = v8::String::new(s, "foo").unwrap(); + let expected = v8::String::new(s, "bar").unwrap(); + let actual = meta.get(s, context, key.into()).unwrap(); assert!(expected.strict_equals(actual)); assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1); context.exit(); - }); + } drop(locker); drop(g); } @@ -407,8 +441,9 @@ fn script_compile_and_run() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - - v8::HandleScope::enter(&mut locker, |s| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let s = hs.enter(); let mut context = v8::Context::new(s); context.enter(); let source = v8::String::new(s, "'Hello ' + 13 + 'th planet'").unwrap(); @@ -420,7 +455,7 @@ fn script_compile_and_run() { unsafe { std::mem::transmute_copy(&result) }; assert_eq!(result.to_rust_string_lossy(s), "Hello 13th planet"); context.exit(); - }); + } drop(locker); } @@ -432,7 +467,9 @@ fn script_origin() { let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |s| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let s = hs.enter(); let mut context = v8::Context::new(s); context.enter(); @@ -464,7 +501,7 @@ fn script_origin() { source.to_rust_string_lossy(s); let _result = script.run(s, context).unwrap(); context.exit(); - }); + } drop(locker); } @@ -521,7 +558,9 @@ fn test_primitives() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let null = v8::new_null(scope); assert!(!null.is_undefined()); assert!(null.is_null()); @@ -541,7 +580,7 @@ fn test_primitives() { assert!(!false_.is_undefined()); assert!(!false_.is_null()); assert!(!false_.is_null_or_undefined()); - }); + } drop(locker); } @@ -553,7 +592,9 @@ fn exception() { let mut isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); isolate.enter(); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); let reference = "This is a test error"; @@ -572,7 +613,7 @@ fn exception() { ); assert!(v8::get_stack_trace(scope, exception).is_none()); context.exit(); - }); + } drop(locker); isolate.exit(); } @@ -584,7 +625,9 @@ fn json() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |s| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let s = hs.enter(); let mut context = v8::Context::new(s); context.enter(); let json_string = v8_str(s, "{\"a\": 1, \"b\": 2}"); @@ -597,7 +640,7 @@ fn json() { let rust_str = stringified.to_rust_string_lossy(s); assert_eq!("{\"a\":1,\"b\":2}".to_string(), rust_str); context.exit(); - }); + } drop(locker); } @@ -614,7 +657,9 @@ fn object() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); let null: v8::Local = new_null(scope).into(); @@ -629,7 +674,7 @@ fn object() { let object = v8::Object::new(scope, null, names, values, 2); assert!(!object.is_null_or_undefined()); context.exit(); - }); + } drop(locker); } @@ -640,15 +685,18 @@ fn create_data_property() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); eval(scope, context, "var a = {};"); + let key = v8_str(scope, "a"); let obj = context - .global() - .get(context, v8_str(scope, "a").into()) + .global(scope) + .get(scope, context, key.into()) .unwrap(); assert!(obj.is_object()); let obj: Local = cast(obj); @@ -658,11 +706,11 @@ fn create_data_property() { obj.create_data_property(context, cast(key), cast(value)), v8::MaybeBool::JustTrue ); - let actual = obj.get(context, cast(key)).unwrap(); + let actual = obj.get(scope, context, cast(key)).unwrap(); assert!(value.strict_equals(actual)); context.exit(); - }); + } drop(locker); } @@ -673,7 +721,9 @@ fn promise_resolved() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); let maybe_resolver = v8::PromiseResolver::new(scope, context); @@ -698,7 +748,7 @@ fn promise_resolved() { let result_str: v8::Local = cast(result); assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string()); context.exit(); - }); + } drop(locker); } @@ -709,7 +759,9 @@ fn promise_rejected() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); let maybe_resolver = v8::PromiseResolver::new(scope, context); @@ -735,21 +787,27 @@ fn promise_rejected() { let result_str: v8::Local = cast(result); assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string()); context.exit(); - }); + } drop(locker); } extern "C" fn fn_callback(info: &FunctionCallbackInfo) { assert_eq!(info.length(), 0); - let isolate = unsafe { info.get_isolate() }; - v8::HandleScope::enter(isolate, |scope| { - let s = v8::String::new(scope, "Hello callback!").unwrap(); - let value: Local = s.into(); + { let rv = &mut info.get_return_value(); - let rv_value = rv.get(scope); - assert!(rv_value.is_undefined()); - rv.set(value); - }); + #[allow(mutable_transmutes)] + #[allow(clippy::transmute_ptr_to_ptr)] + let info: &mut FunctionCallbackInfo = unsafe { std::mem::transmute(info) }; + { + let mut hs = v8::HandleScope::new(info); + let scope = hs.enter(); + let s = v8::String::new(scope, "Hello callback!").unwrap(); + let value: Local = s.into(); + let rv_value = rv.get(scope); + assert!(rv_value.is_undefined()); + rv.set(value); + } + } } #[test] @@ -759,10 +817,12 @@ fn function() { params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); - let global = context.global(); + let global = context.global(scope); let recv: Local = global.into(); // create function using template let mut fn_template = v8::FunctionTemplate::new(scope, fn_callback); @@ -781,7 +841,7 @@ fn function() { let rust_str = value_str.to_rust_string_lossy(scope); assert_eq!(rust_str, "Hello callback!".to_string()); context.exit(); - }); + } drop(locker); } @@ -790,15 +850,17 @@ extern "C" fn promise_reject_callback(msg: v8::PromiseRejectMessage) { assert_eq!(event, v8::PromiseRejectEvent::PromiseRejectWithNoHandler); let mut promise = msg.get_promise(); assert_eq!(promise.state(), v8::PromiseState::Rejected); - let promise_obj: v8::Local = cast(promise); + let mut promise_obj: v8::Local = cast(promise); let isolate = promise_obj.get_isolate(); let value = msg.get_value(); let mut locker = v8::Locker::new(isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let value_str: v8::Local = cast(value); let rust_str = value_str.to_rust_string_lossy(scope); assert_eq!(rust_str, "promise rejected".to_string()); - }); + } drop(locker); } @@ -811,7 +873,9 @@ fn set_promise_reject_callback() { isolate.set_promise_reject_callback(promise_reject_callback); isolate.enter(); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); let mut resolver = v8::PromiseResolver::new(scope, context).unwrap(); @@ -819,24 +883,24 @@ fn set_promise_reject_callback() { let value: Local = cast(str_); resolver.reject(context, value); context.exit(); - }); + } drop(locker); isolate.exit(); } fn mock_script_origin<'sc>( - isolate: &mut impl AsMut, + scope: &mut impl v8::ToLocal<'sc>, resource_name_: &str, ) -> v8::ScriptOrigin<'sc> { - let resource_name = v8_str(isolate, resource_name_); - let resource_line_offset = v8::Integer::new(isolate, 0); - let resource_column_offset = v8::Integer::new(isolate, 0); - let resource_is_shared_cross_origin = v8::new_true(isolate); - let script_id = v8::Integer::new(isolate, 123); - let source_map_url = v8_str(isolate, "source_map_url"); - let resource_is_opaque = v8::new_true(isolate); - let is_wasm = v8::new_false(isolate); - let is_module = v8::new_true(isolate); + let resource_name = v8_str(scope, resource_name_); + let resource_line_offset = v8::Integer::new(scope, 0); + let resource_column_offset = v8::Integer::new(scope, 0); + let resource_is_shared_cross_origin = v8::new_true(scope); + let script_id = v8::Integer::new(scope, 123); + let source_map_url = v8_str(scope, "source_map_url"); + let resource_is_opaque = v8::new_true(scope); + let is_wasm = v8::new_false(scope); + let is_module = v8::new_true(scope); v8::ScriptOrigin::new( resource_name.into(), resource_line_offset, @@ -850,13 +914,14 @@ fn mock_script_origin<'sc>( ) } -fn mock_source( - isolate: &mut impl AsMut, +fn mock_source<'sc>( + scope: &mut impl ToLocal<'sc>, resource_name: &str, source: &str, ) -> v8::script_compiler::Source { - let script_origin = mock_script_origin(isolate, resource_name); - v8::script_compiler::Source::new(v8_str(isolate, source), &script_origin) + let source_str = v8_str(scope, source); + let script_origin = mock_script_origin(scope, resource_name); + v8::script_compiler::Source::new(source_str, &script_origin) } #[test] @@ -868,7 +933,9 @@ fn script_compiler_source() { isolate.set_promise_reject_callback(promise_reject_callback); isolate.enter(); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); @@ -881,7 +948,7 @@ fn script_compiler_source() { assert!(result.is_some()); context.exit(); - }); + } drop(locker); isolate.exit(); drop(g); @@ -895,7 +962,9 @@ fn module_instantiation_failures1() { let mut isolate = v8::Isolate::new(params); isolate.enter(); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); @@ -933,13 +1002,15 @@ fn module_instantiation_failures1() { let mut try_catch = v8::TryCatch::new(scope); let tc = try_catch.enter(); fn resolve_callback( - mut context: v8::Local, + context: v8::Local, _specifier: v8::Local, _referrer: v8::Local, ) -> *mut v8::Module { - let isolate: &mut v8::Isolate = context.as_mut(); - let e = v8_str(isolate, "boom"); - isolate.throw_exception(e.into()); + let mut cbs = v8::CallbackScope::new(context); + let mut hs = v8::HandleScope::new(cbs.enter()); + let scope = hs.enter(); + let e = v8_str(scope, "boom"); + scope.isolate().throw_exception(e.into()); std::ptr::null_mut() } let result = module.instantiate_module(context, resolve_callback); @@ -953,22 +1024,25 @@ fn module_instantiation_failures1() { } context.exit(); - }); + } drop(locker); isolate.exit(); drop(g); } fn compile_specifier_as_module_resolve_callback( - mut context: v8::Local, + context: v8::Local, specifier: v8::Local, _referrer: v8::Local, ) -> *mut v8::Module { - let isolate: &mut v8::Isolate = context.as_mut(); - let origin = mock_script_origin(isolate, "module.js"); + let mut cbs = v8::CallbackScope::new(context); + let mut hs = v8::EscapableHandleScope::new(cbs.enter()); + let scope = hs.enter(); + let origin = mock_script_origin(scope, "module.js"); let source = v8::script_compiler::Source::new(specifier, &origin); - let module = v8::script_compiler::compile_module(isolate, source).unwrap(); - &mut *cast(module) + let module = + v8::script_compiler::compile_module(scope.isolate(), source).unwrap(); + &mut *cast(scope.escape(module)) } #[test] @@ -979,7 +1053,9 @@ fn module_evaluation() { let mut isolate = v8::Isolate::new(params); isolate.enter(); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); @@ -1002,7 +1078,7 @@ fn module_evaluation() { assert!(result.unwrap()); assert_eq!(v8::ModuleStatus::Instantiated, module.get_status()); - let result = module.evaluate(context); + let result = module.evaluate(scope, context); assert!(result.is_some()); assert_eq!(v8::ModuleStatus::Evaluated, module.get_status()); @@ -1012,7 +1088,7 @@ fn module_evaluation() { assert!(result.strict_equals(expected.into())); context.exit(); - }); + } drop(locker); isolate.exit(); drop(g); @@ -1026,7 +1102,9 @@ fn primitive_array() { let mut isolate = v8::Isolate::new(params); isolate.enter(); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); @@ -1051,7 +1129,7 @@ fn primitive_array() { assert!(array.get(scope, 2).is_number()); context.exit(); - }); + } drop(locker); isolate.exit(); drop(g); @@ -1075,7 +1153,9 @@ fn equality() { let mut isolate = v8::Isolate::new(params); isolate.enter(); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); @@ -1086,7 +1166,7 @@ fn equality() { assert!(!v8_str(scope, "a").same_value(v8_str(scope, "b").into())); context.exit(); - }); + } drop(locker); isolate.exit(); drop(g); @@ -1100,7 +1180,9 @@ fn array_buffer_view() { let mut isolate = v8::Isolate::new(params); isolate.enter(); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |s| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let s = hs.enter(); let mut context = v8::Context::new(s); context.enter(); let source = v8::String::new(s, "new Uint8Array([23,23,23,23])").unwrap(); @@ -1121,7 +1203,7 @@ fn array_buffer_view() { let ab = maybe_ab.unwrap(); assert_eq!(ab.byte_length(), 4); context.exit(); - }); + } drop(locker); isolate.exit(); drop(g); @@ -1136,7 +1218,9 @@ fn snapshot_creator() { let mut snapshot_creator = v8::SnapshotCreator::default(); let isolate = snapshot_creator.get_isolate(); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); @@ -1148,7 +1232,7 @@ fn snapshot_creator() { snapshot_creator.set_default_context(context); context.exit(); - }); + } snapshot_creator.create_blob(v8::FunctionCodeHandling::Clear) }; @@ -1161,7 +1245,9 @@ fn snapshot_creator() { params.set_snapshot_blob(&mut startup_data); let isolate = v8::Isolate::new(params); let mut locker = v8::Locker::new(&isolate); - v8::HandleScope::enter(&mut locker, |scope| { + { + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); let mut context = v8::Context::new(scope); context.enter(); let source = v8::String::new(scope, "a === 3").unwrap(); @@ -1171,7 +1257,7 @@ fn snapshot_creator() { let true_val: Local = cast(v8::new_true(scope)); assert!(result.same_value(true_val)); context.exit(); - }); + } } // TODO(ry) startup_data is getting leaked and is not cleaned up properly!