diff --git a/build.rs b/build.rs index 83808b74..bc47afc4 100644 --- a/build.rs +++ b/build.rs @@ -61,6 +61,10 @@ fn build_v8() { vec!["is_debug=false".to_string()] }; + if !cargo_gn::is_debug() { + gn_args.push("v8_enable_handle_zapping=false".to_string()); + } + if let Some(clang_base_path) = find_compatible_system_clang() { println!("clang_base_path {}", clang_base_path.display()); gn_args.push(format!("clang_base_path={:?}", clang_base_path)); diff --git a/src/array_buffer.rs b/src/array_buffer.rs index ffdb7b27..1c106f17 100644 --- a/src/array_buffer.rs +++ b/src/array_buffer.rs @@ -14,10 +14,9 @@ use crate::support::SharedRef; use crate::support::UniquePtr; use crate::support::UniqueRef; use crate::ArrayBuffer; -use crate::InIsolate; +use crate::HandleScope; use crate::Isolate; use crate::Local; -use crate::ToLocal; extern "C" { fn v8__ArrayBuffer__Allocator__NewDefaultAllocator() -> *mut Allocator; @@ -236,25 +235,31 @@ impl ArrayBuffer { /// Allocated memory will be owned by a created ArrayBuffer and /// will be deallocated when it is garbage-collected, /// unless the object is externalized. - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s>, byte_length: usize, - ) -> Local<'sc, ArrayBuffer> { + ) -> Local<'s, ArrayBuffer> { unsafe { - scope.cast_local(|scope| { - v8__ArrayBuffer__New__with_byte_length(scope.isolate(), byte_length) + scope.cast_local(|sd| { + v8__ArrayBuffer__New__with_byte_length( + sd.get_isolate_ptr(), + byte_length, + ) }) } .unwrap() } - pub fn with_backing_store<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn with_backing_store<'s>( + scope: &mut HandleScope<'s>, backing_store: &SharedRef, - ) -> Local<'sc, ArrayBuffer> { + ) -> Local<'s, ArrayBuffer> { unsafe { - scope.cast_local(|scope| { - v8__ArrayBuffer__New__with_backing_store(scope.isolate(), backing_store) + scope.cast_local(|sd| { + v8__ArrayBuffer__New__with_backing_store( + sd.get_isolate_ptr(), + backing_store, + ) }) } .unwrap() @@ -281,12 +286,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( - scope: &mut impl InIsolate, + scope: &mut Isolate, byte_length: usize, ) -> UniqueRef { unsafe { UniqueRef::from_raw(v8__ArrayBuffer__NewBackingStore__with_byte_length( - scope.isolate(), + scope, byte_length, )) } diff --git a/src/array_buffer_view.rs b/src/array_buffer_view.rs index a051da89..73b56d0b 100644 --- a/src/array_buffer_view.rs +++ b/src/array_buffer_view.rs @@ -4,8 +4,8 @@ use std::ffi::c_void; use crate::support::int; use crate::ArrayBuffer; use crate::ArrayBufferView; +use crate::HandleScope; use crate::Local; -use crate::ToLocal; extern "C" { fn v8__ArrayBufferView__Buffer( @@ -22,10 +22,10 @@ extern "C" { impl ArrayBufferView { /// Returns underlying ArrayBuffer. - pub fn buffer<'sc>( + pub fn buffer<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { + scope: &mut HandleScope<'s>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__ArrayBufferView__Buffer(self)) } } diff --git a/src/binding.cc b/src/binding.cc index d5c680a7..5592ed9d 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -209,29 +209,6 @@ void v8__HandleScope__CONSTRUCT(uninit_t* buf, void v8__HandleScope__DESTRUCT(v8::HandleScope* self) { self->~HandleScope(); } -v8::Isolate* v8__HandleScope__GetIsolate(const v8::HandleScope& self) { - return self.GetIsolate(); -} - -void v8__EscapableHandleScope__CONSTRUCT( - uninit_t* buf, v8::Isolate* isolate) { - construct_in_place(buf, isolate); -} - -void v8__EscapableHandleScope__DESTRUCT(v8::EscapableHandleScope* self) { - self->~EscapableHandleScope(); -} - -const v8::Data* v8__EscapableHandleScope__Escape(v8::EscapableHandleScope* self, - const v8::Data& value) { - return local_to_ptr(self->Escape(ptr_to_local(&value))); -} - -v8::Isolate* v8__EscapableHandleScope__GetIsolate( - const v8::EscapableHandleScope& self) { - return self.GetIsolate(); -} - const v8::Data* v8__Local__New(v8::Isolate* isolate, const v8::Data& other) { return local_to_ptr(v8::Local::New(isolate, ptr_to_local(&other))); } @@ -889,6 +866,10 @@ const v8::Context* v8__Context__New(v8::Isolate* isolate, DeserializeInternalFields, nullptr))); } +bool v8__Context__EQ(const v8::Context& self, const v8::Context& other) { + return ptr_to_local(&self) == ptr_to_local(&other); +} + void v8__Context__Enter(const v8::Context& self) { ptr_to_local(&self)->Enter(); } diff --git a/src/context.rs b/src/context.rs index df21f552..52cd2b8c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,10 +1,10 @@ // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. use crate::isolate::Isolate; use crate::Context; +use crate::HandleScope; use crate::Local; use crate::Object; use crate::ObjectTemplate; -use crate::ToLocal; use crate::Value; use std::ptr::null; @@ -14,31 +14,30 @@ extern "C" { templ: *const ObjectTemplate, global_object: *const Value, ) -> *const Context; - fn v8__Context__Enter(this: *const Context); - fn v8__Context__Exit(this: *const Context); fn v8__Context__Global(this: *const Context) -> *const Object; } impl Context { /// Creates a new context. - pub fn new<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, Context> { + pub fn new<'s>(scope: &mut HandleScope<'s, ()>) -> Local<'s, Context> { // TODO: optional arguments; unsafe { scope - .cast_local(|scope| v8__Context__New(scope.isolate(), null(), null())) + .cast_local(|sd| v8__Context__New(sd.get_isolate_ptr(), null(), null())) } .unwrap() } /// Creates a new context using the object template as the template for /// the global object. - pub fn new_from_template<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new_from_template<'s>( + scope: &mut HandleScope<'s, ()>, templ: Local, - ) -> Local<'sc, Context> { + ) -> Local<'s, Context> { unsafe { - scope - .cast_local(|scope| v8__Context__New(scope.isolate(), &*templ, null())) + scope.cast_local(|sd| { + v8__Context__New(sd.get_isolate_ptr(), &*templ, null()) + }) } .unwrap() } @@ -53,26 +52,10 @@ 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>( + pub fn global<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Local<'sc, Object> { + scope: &mut HandleScope<'s, ()>, + ) -> Local<'s, Object> { unsafe { scope.cast_local(|_| v8__Context__Global(self)) }.unwrap() } - - /// Enter this context. After entering a context, all code compiled - /// and run is compiled and run in this context. If another context - /// is already entered, this old context is saved so it can be - /// restored when the new context is exited. - pub(crate) fn enter(&self) { - // TODO: enter/exit should be controlled by a scope. - unsafe { v8__Context__Enter(self) }; - } - - /// Exit this context. Exiting the current context restores the - /// context that was in place when entering the current context. - pub(crate) fn exit(&self) { - // TODO: enter/exit should be controlled by a scope. - unsafe { v8__Context__Exit(self) }; - } } diff --git a/src/data.rs b/src/data.rs index 1abd65f6..0ca5266e 100644 --- a/src/data.rs +++ b/src/data.rs @@ -25,8 +25,8 @@ macro_rules! impl_deref { macro_rules! impl_from { { $source:ident for $type:ident } => { - impl<'sc> From> for Local<'sc, $type> { - fn from(l: Local<'sc, $source>) -> Self { + impl<'s> From> for Local<'s, $type> { + fn from(l: Local<'s, $source>) -> Self { unsafe { transmute(l) } } } @@ -35,9 +35,9 @@ macro_rules! impl_from { macro_rules! impl_try_from { { $source:ident for $target:ident if $value:pat => $check:expr } => { - impl<'sc> TryFrom> for Local<'sc, $target> { + impl<'s> TryFrom> for Local<'s, $target> { type Error = TryFromTypeError; - fn try_from(l: Local<'sc, $source>) -> Result { + fn try_from(l: Local<'s, $source>) -> Result { match l { $value if $check => Ok(unsafe { transmute(l) }), _ => Err(TryFromTypeError::new(stringify!($target))) @@ -49,21 +49,21 @@ macro_rules! impl_try_from { macro_rules! impl_eq { { for $type:ident } => { - impl<'sc> Eq for Local<'sc, $type> {} + impl<'s> Eq for Local<'s, $type> {} }; } macro_rules! impl_partial_eq { { $rhs:ident for $type:ident use identity } => { - impl<'sc> PartialEq> for Local<'sc, $type> { - fn eq(&self, other: &Local<'sc, $rhs>) -> bool { + impl<'s> PartialEq> for Local<'s, $type> { + fn eq(&self, other: &Local<'s, $rhs>) -> bool { self.eq_identity((*other).into()) } } }; { $rhs:ident for $type:ident use strict_equals } => { - impl<'sc> PartialEq> for Local<'sc, $type> { - fn eq(&self, other: &Local<'sc, $rhs>) -> bool { + impl<'s> PartialEq> for Local<'s, $type> { + fn eq(&self, other: &Local<'s, $rhs>) -> bool { self.strict_equals((*other).into()) } } diff --git a/src/exception.rs b/src/exception.rs index 97088725..4054ceed 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -3,12 +3,12 @@ use crate::isolate::Isolate; use crate::support::int; use crate::Context; +use crate::HandleScope; use crate::Local; use crate::Message; use crate::StackFrame; use crate::StackTrace; use crate::String; -use crate::ToLocal; use crate::Value; extern "C" { @@ -73,14 +73,14 @@ impl StackTrace { } /// Returns a StackFrame at a particular index. - pub fn get_frame<'sc>( + pub fn get_frame<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, index: usize, - ) -> Option> { + ) -> Option> { unsafe { - scope.cast_local(|scope| { - v8__StackTrace__GetFrame(self, scope.isolate(), index as u32) + scope.cast_local(|sd| { + v8__StackTrace__GetFrame(self, sd.get_isolate_ptr(), index as u32) }) } } @@ -114,10 +114,10 @@ impl StackFrame { /// Returns the name of the resource that contains the script for the /// function for this StackFrame. - pub fn get_script_name<'sc>( + pub fn get_script_name<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { + scope: &mut HandleScope<'s>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__StackFrame__GetScriptName(self)) } } @@ -125,20 +125,20 @@ impl StackFrame { /// function for this StackFrame or sourceURL value if the script name /// is undefined and its source ends with //# sourceURL=... string or /// deprecated //@ sourceURL=... string. - pub fn get_script_name_or_source_url<'sc>( + pub fn get_script_name_or_source_url<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { + scope: &mut HandleScope<'s>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__StackFrame__GetScriptNameOrSourceURL(self)) } } /// Returns the name of the function associated with this stack frame. - pub fn get_function_name<'sc>( + pub fn get_function_name<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { + scope: &mut HandleScope<'s>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__StackFrame__GetFunctionName(self)) } } @@ -166,23 +166,23 @@ impl StackFrame { } impl Message { - pub fn get<'sc>(&self, scope: &mut impl ToLocal<'sc>) -> Local<'sc, String> { + pub fn get<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, String> { unsafe { scope.cast_local(|_| v8__Message__Get(self)) }.unwrap() } /// Exception stack trace. By default stack traces are not captured for /// uncaught exceptions. SetCaptureStackTraceForUncaughtExceptions allows /// to change this option. - pub fn get_stack_trace<'sc>( + pub fn get_stack_trace<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { + scope: &mut HandleScope<'s>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__Message__GetStackTrace(self)) } } pub fn get_source_line<'s>( &self, - scope: &mut impl ToLocal<'s>, + scope: &mut HandleScope<'s>, context: Local, ) -> Option> { unsafe { scope.cast_local(|_| v8__Message__GetSourceLine(self, &*context)) } @@ -192,7 +192,7 @@ impl Message { /// the error originates. pub fn get_script_resource_name<'s>( &self, - scope: &mut impl ToLocal<'s>, + scope: &mut HandleScope<'s>, ) -> Option> { unsafe { scope.cast_local(|_| v8__Message__GetScriptResourceName(self)) } } @@ -258,64 +258,64 @@ impl Message { pub struct Exception; impl Exception { - pub fn error<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn error<'s>( + scope: &mut HandleScope<'s>, message: Local, - ) -> Local<'sc, Value> { + ) -> Local<'s, Value> { Self::new_error_with(scope, message, v8__Exception__Error) } - pub fn range_error<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn range_error<'s>( + scope: &mut HandleScope<'s>, message: Local, - ) -> Local<'sc, Value> { + ) -> Local<'s, Value> { Self::new_error_with(scope, message, v8__Exception__RangeError) } - pub fn reference_error<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn reference_error<'s>( + scope: &mut HandleScope<'s>, message: Local, - ) -> Local<'sc, Value> { + ) -> Local<'s, Value> { Self::new_error_with(scope, message, v8__Exception__ReferenceError) } - pub fn syntax_error<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn syntax_error<'s>( + scope: &mut HandleScope<'s>, message: Local, - ) -> Local<'sc, Value> { + ) -> Local<'s, Value> { Self::new_error_with(scope, message, v8__Exception__SyntaxError) } - pub fn type_error<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn type_error<'s>( + scope: &mut HandleScope<'s>, message: Local, - ) -> Local<'sc, Value> { + ) -> Local<'s, Value> { Self::new_error_with(scope, message, v8__Exception__TypeError) } /// Internal helper to make the above error constructors less repetitive. - fn new_error_with<'sc>( - scope: &mut impl ToLocal<'sc>, + fn new_error_with<'s>( + scope: &mut HandleScope<'s>, message: Local, contructor: unsafe extern "C" fn(*const String) -> *const Value, - ) -> Local<'sc, Value> { - scope.isolate().enter(); + ) -> Local<'s, Value> { + scope.enter_isolate(); let error = unsafe { scope.cast_local(|_| (contructor)(&*message)) }.unwrap(); - scope.isolate().exit(); + scope.exit_isolate(); error } /// Creates an error message for the given exception. /// 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 impl ToLocal<'sc>, + pub fn create_message<'s>( + scope: &mut HandleScope<'s>, exception: Local, - ) -> Local<'sc, Message> { + ) -> Local<'s, Message> { unsafe { - scope.cast_local(|scope| { - v8__Exception__CreateMessage(scope.isolate(), &*exception) + scope.cast_local(|sd| { + v8__Exception__CreateMessage(sd.get_isolate_ptr(), &*exception) }) } .unwrap() @@ -323,10 +323,10 @@ impl Exception { /// 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 impl ToLocal<'sc>, + pub fn get_stack_trace<'s>( + scope: &mut HandleScope<'s>, exception: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Exception__GetStackTrace(&*exception)) } } } diff --git a/src/function.rs b/src/function.rs index 4161393b..63787d28 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,20 +1,18 @@ use std::convert::TryFrom; use std::marker::PhantomData; -use crate::scope::ScopeDefinition; use crate::support::MapFnFrom; use crate::support::MapFnTo; use crate::support::ToCFn; use crate::support::UnitType; use crate::support::{int, Opaque}; +use crate::CallbackScope; use crate::Context; use crate::Function; -use crate::FunctionCallbackScope; +use crate::HandleScope; use crate::Local; use crate::Name; use crate::Object; -use crate::PropertyCallbackScope; -use crate::ToLocal; use crate::Value; extern "C" { @@ -92,10 +90,7 @@ impl<'cb> ReturnValue<'cb> { /// Getter. Creates a new Local<> so it comes with a certain performance /// hit. If the ReturnValue was not yet set, this will return the undefined /// value. - pub fn get<'sc>( - &mut self, - scope: &mut impl ToLocal<'sc>, - ) -> Local<'sc, Value> { + pub fn get<'s>(&mut self, scope: &mut HandleScope<'s>) -> Local<'s, Value> { unsafe { scope.cast_local(|_| v8__ReturnValue__Get(self)) }.unwrap() } } @@ -113,11 +108,6 @@ pub struct FunctionCallbackInfo { length: int, } -unsafe impl<'s> ScopeDefinition<'s> for FunctionCallbackInfo { - type Args = (); - unsafe fn enter_scope(_: *mut Self, _: Self::Args) {} -} - /// The information passed to a property callback about the context /// of the property access. #[repr(C)] @@ -127,11 +117,6 @@ pub struct PropertyCallbackInfo { args: *mut Opaque, } -unsafe impl<'s> ScopeDefinition<'s> for PropertyCallbackInfo { - type Args = (); - unsafe fn enter_scope(_: *mut Self, _: Self::Args) {} -} - pub struct FunctionCallbackArguments<'s> { info: *const FunctionCallbackInfo, phantom: PhantomData<&'s ()>, @@ -239,13 +224,11 @@ pub type FunctionCallback = extern "C" fn(*const FunctionCallbackInfo); impl MapFnFrom for FunctionCallback where - F: UnitType - + Fn(FunctionCallbackScope, FunctionCallbackArguments, ReturnValue), + F: UnitType + Fn(&mut HandleScope, FunctionCallbackArguments, ReturnValue), { fn mapping() -> Self { let f = |info: *const FunctionCallbackInfo| { - let scope: FunctionCallbackScope = - &mut crate::scope::Entered::new_root(info as *mut FunctionCallbackInfo); + let scope = &mut unsafe { CallbackScope::new(&*info) }; let args = FunctionCallbackArguments::from_function_callback_info(info); let rv = ReturnValue::from_function_callback_info(info); (F::get())(scope, args, rv); @@ -262,17 +245,11 @@ pub type AccessorNameGetterCallback<'s> = impl MapFnFrom for AccessorNameGetterCallback<'_> where F: UnitType - + Fn( - PropertyCallbackScope, - Local, - PropertyCallbackArguments, - ReturnValue, - ), + + Fn(&mut HandleScope, Local, PropertyCallbackArguments, ReturnValue), { fn mapping() -> Self { let f = |key: Local, info: *const PropertyCallbackInfo| { - let scope: PropertyCallbackScope = - &mut crate::scope::Entered::new_root(info as *mut PropertyCallbackInfo); + let scope = &mut unsafe { CallbackScope::new(&*info) }; let args = PropertyCallbackArguments::from_property_callback_info(info); let rv = ReturnValue::from_property_callback_info(info); (F::get())(scope, key, args, rv); @@ -285,11 +262,11 @@ impl Function { // TODO: add remaining arguments from C++ /// Create a function in the current execution context /// for a given FunctionCallback. - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s>, context: Local, callback: impl MapFnTo, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Function__New(&*context, callback.map_fn_to())) } @@ -297,12 +274,12 @@ impl Function { /// Create a function in the current execution context /// for a given FunctionCallback and associated data. - pub fn new_with_data<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new_with_data<'s>( + scope: &mut HandleScope<'s>, context: Local, data: Local, callback: impl MapFnTo, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| { v8__Function__NewWithData(&*context, callback.map_fn_to(), &*data) @@ -310,13 +287,13 @@ impl Function { } } - pub fn call<'sc>( + pub fn call<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, recv: Local, args: &[Local], - ) -> Option> { + ) -> Option> { let args = Local::slice_into_raw(args); let argc = int::try_from(args.len()).unwrap(); let argv = args.as_ptr(); diff --git a/src/global.rs b/src/global.rs index f271f70c..c178b080 100644 --- a/src/global.rs +++ b/src/global.rs @@ -2,11 +2,10 @@ use std::mem::transmute; use std::ptr::NonNull; use crate::Data; -use crate::InIsolate; +use crate::HandleScope; use crate::Isolate; use crate::IsolateHandle; use crate::Local; -use crate::ToLocal; extern "C" { fn v8__Local__New(isolate: *mut Isolate, other: *const Data) -> *const Data; @@ -50,16 +49,12 @@ impl Global { /// Construct a new Global from an existing handle. When the existing handle /// is non-empty, a new storage cell is created pointing to the same object, /// and no flags are set. - pub fn new_from( - scope: &mut impl InIsolate, - other: impl AnyHandle, - ) -> Self { - let isolate = scope.isolate(); - let other_value = other.read(isolate); + pub fn new_from(scope: &mut Isolate, other: impl AnyHandle) -> Self { + let other_value = other.read(scope); Self { value: other_value - .map(|v| unsafe { transmute(v8__Global__New(isolate, transmute(v))) }), - isolate_handle: other_value.map(|_| isolate.thread_safe_handle()), + .map(|v| unsafe { transmute(v8__Global__New(scope, transmute(v))) }), + isolate_handle: other_value.map(|_| scope.thread_safe_handle()), } } @@ -70,25 +65,25 @@ impl Global { } /// Construct a Local from this global handle. - pub fn get<'sc>( + pub fn get<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - self.check_isolate(scope.isolate()); + scope: &mut HandleScope<'s, ()>, + ) -> Option> { + self.check_isolate(scope); self .value .map(|g| g.as_ptr() as *const Data) .and_then(|g| unsafe { - scope.cast_local(|scope| v8__Local__New(scope.isolate(), g) as *const T) + scope + .cast_local(|sd| v8__Local__New(sd.get_isolate_ptr(), g) as *const T) }) } /// 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, scope: &mut impl InIsolate, other: impl AnyHandle) { - let isolate = scope.isolate(); - self.check_isolate(isolate); - let other_value = other.read(isolate); + pub fn set(&mut self, scope: &mut Isolate, other: impl AnyHandle) { + self.check_isolate(scope); + let other_value = other.read(scope); match (&mut self.value, &other_value) { (None, None) => {} (target, None) => unsafe { @@ -99,17 +94,17 @@ impl Global { (target, source) => unsafe { v8__Global__Reset__2( &mut *(target as *mut Option> as *mut *const Data), - isolate, + scope, &*(source as *const Option> as *const *const Data), ) }, } - self.isolate_handle = other_value.map(|_| isolate.thread_safe_handle()); + self.isolate_handle = other_value.map(|_| scope.thread_safe_handle()); } /// If non-empty, destroy the underlying storage cell /// IsEmpty() will return true after this call. - pub fn reset(&mut self, scope: &mut impl InIsolate) { + pub fn reset(&mut self, scope: &mut Isolate) { self.set(scope, None); } @@ -164,19 +159,19 @@ pub trait AnyHandle { fn read(self, isolate: &mut Isolate) -> Option>; } -impl<'sc, T> AnyHandle for Local<'sc, T> { +impl<'s, T> AnyHandle for Local<'s, T> { fn read(self, _isolate: &mut Isolate) -> Option> { Some(self.as_non_null()) } } -impl<'sc, T> AnyHandle for Option> { +impl<'s, T> AnyHandle for Option> { fn read(self, _isolate: &mut Isolate) -> Option> { self.map(|local| local.as_non_null()) } } -impl<'sc, T> AnyHandle for &Global { +impl<'s, T> AnyHandle for &Global { fn read(self, isolate: &mut Isolate) -> Option> { self.check_isolate(isolate); self.value diff --git a/src/handle_scope.rs b/src/handle_scope.rs deleted file mode 100644 index b1ffff82..00000000 --- a/src/handle_scope.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. - -use crate::isolate::Isolate; -use crate::scope::Scope; -use crate::scope::ScopeDefinition; -use crate::scope_traits::ToLocalOrReturnsLocal; -use crate::Data; -use crate::InIsolate; -use crate::Local; - -extern "C" { - fn v8__HandleScope__CONSTRUCT(buf: *mut HandleScope, isolate: *mut Isolate); - fn v8__HandleScope__DESTRUCT(this: *mut HandleScope); - fn v8__EscapableHandleScope__CONSTRUCT( - buf: *mut EscapableHandleScope, - isolate: *mut Isolate, - ); - fn v8__EscapableHandleScope__DESTRUCT(this: *mut EscapableHandleScope); - fn v8__EscapableHandleScope__Escape( - this: *mut EscapableHandleScope, - value: *const Data, - ) -> *const Data; -} - -/// A stack-allocated class that governs a number of local handles. -/// After a handle scope has been created, all local handles will be -/// allocated within that handle scope until either the handle scope is -/// deleted or another handle scope is created. If there is already a -/// handle scope and a new one is created, all allocations will take -/// place in the new handle scope until it is deleted. After that, -/// new handles will again be allocated in the original handle scope. -/// -/// After the handle scope of a local handle has been deleted the -/// garbage collector will no longer track the object stored in the -/// handle and may deallocate it. The behavior of accessing a handle -/// for which the handle scope has been deleted is undefined. -#[repr(C)] -pub struct HandleScope([usize; 3]); - -impl<'s> HandleScope { - pub fn new

(parent: &'s mut P) -> Scope<'s, Self, P> - where - P: InIsolate, - { - let isolate: *mut Isolate = parent.isolate(); - Scope::new(isolate, parent) - } -} - -unsafe impl<'s> ScopeDefinition<'s> for HandleScope { - type Args = *mut Isolate; - unsafe fn enter_scope(buf: *mut Self, isolate: *mut Isolate) { - v8__HandleScope__CONSTRUCT(buf, isolate); - } -} - -impl Drop for HandleScope { - fn drop(&mut self) { - unsafe { v8__HandleScope__DESTRUCT(self) } - } -} - -/// A HandleScope which first allocates a handle in the current scope -/// which will be later filled with the escape value. -#[repr(C)] -pub struct EscapableHandleScope([usize; 4]); - -impl<'s> EscapableHandleScope { - pub fn new<'p: 's, P>(parent: &'s mut P) -> Scope<'s, Self, P> - where - P: ToLocalOrReturnsLocal<'p>, - { - let isolate: *mut Isolate = parent.isolate(); - Scope::new(isolate, parent) - } - - /// Pushes the value into the previous scope and returns a handle to it. - /// Cannot be called twice. - pub(crate) unsafe fn escape<'p, T>( - &mut self, - value: Local, - ) -> Local<'p, T> { - Local::from_raw(v8__EscapableHandleScope__Escape( - self, - value.as_ptr() as *const Data, - ) as *const T) - .unwrap() - } -} - -unsafe impl<'s> ScopeDefinition<'s> for EscapableHandleScope { - type Args = *mut Isolate; - unsafe fn enter_scope(buf: *mut Self, isolate: *mut Isolate) { - v8__EscapableHandleScope__CONSTRUCT(buf, isolate); - } -} - -impl Drop for EscapableHandleScope { - fn drop(&mut self) { - unsafe { v8__EscapableHandleScope__DESTRUCT(self) } - } -} diff --git a/src/inspector.rs b/src/inspector.rs index 960ab5f6..27cfceb4 100644 --- a/src/inspector.rs +++ b/src/inspector.rs @@ -13,7 +13,6 @@ //! https://github.com/nodejs/node/tree/v13.7.0/src/inspector //! https://github.com/denoland/deno/blob/v0.38.0/cli/inspector.rs -use crate::scope_traits::InIsolate; use crate::support::int; use crate::support::CxxVTable; use crate::support::FieldOffset; @@ -834,7 +833,7 @@ pub struct V8Inspector(Opaque); impl V8Inspector { pub fn create( - isolate: &mut impl InIsolate, + isolate: &mut Isolate, client: &mut T, ) -> UniqueRef where @@ -842,7 +841,7 @@ impl V8Inspector { { unsafe { UniqueRef::from_raw(v8_inspector__V8Inspector__create( - isolate.isolate(), + isolate, client.as_client_mut(), )) } diff --git a/src/isolate.rs b/src/isolate.rs index f598feff..c2d6041f 100644 --- a/src/isolate.rs +++ b/src/isolate.rs @@ -2,10 +2,10 @@ use crate::isolate_create_params::raw; use crate::isolate_create_params::CreateParams; use crate::promise::PromiseRejectMessage; +use crate::scope::data::ScopeData; use crate::support::Opaque; use crate::Context; use crate::Function; -use crate::InIsolate; use crate::Local; use crate::Message; use crate::Module; @@ -103,10 +103,6 @@ extern "C" { callback: InterruptCallback, data: *mut c_void, ); - fn v8__Isolate__ThrowException( - isolate: *mut Isolate, - exception: *const Value, - ) -> *const Value; fn v8__Isolate__TerminateExecution(isolate: *const Isolate); fn v8__Isolate__IsExecutionTerminating(isolate: *const Isolate) -> bool; fn v8__Isolate__CancelTerminateExecution(isolate: *const Isolate); @@ -133,6 +129,10 @@ extern "C" { pub struct Isolate(Opaque); impl Isolate { + const ANNEX_SLOT: u32 = 0; + const CURRENT_SCOPE_DATA_SLOT: u32 = 1; + const INTERNAL_SLOT_COUNT: u32 = 2; + /// Creates a new isolate. Does not change the currently entered /// isolate. /// @@ -146,6 +146,7 @@ impl Isolate { let (raw_create_params, create_param_allocations) = params.finalize(); let cxx_isolate = unsafe { v8__Isolate__New(&raw_create_params) }; let mut owned_isolate = OwnedIsolate::new(cxx_isolate); + ScopeData::new_root(&mut owned_isolate); owned_isolate.create_annex(create_param_allocations); owned_isolate } @@ -165,18 +166,23 @@ impl Isolate { ) { let annex_arc = Arc::new(IsolateAnnex::new(self, create_param_allocations)); let annex_ptr = Arc::into_raw(annex_arc); - unsafe { assert!(v8__Isolate__GetData(self, 0).is_null()) }; - unsafe { v8__Isolate__SetData(self, 0, annex_ptr as *mut c_void) }; + unsafe { + assert!(v8__Isolate__GetData(self, Self::ANNEX_SLOT).is_null()); + v8__Isolate__SetData(self, Self::ANNEX_SLOT, annex_ptr as *mut c_void); + }; } fn get_annex(&self) -> &IsolateAnnex { unsafe { - &*(v8__Isolate__GetData(self, 0) as *const _ as *const IsolateAnnex) + &*(v8__Isolate__GetData(self, Self::ANNEX_SLOT) as *const _ + as *const IsolateAnnex) } } fn get_annex_mut(&mut self) -> &mut IsolateAnnex { - unsafe { &mut *(v8__Isolate__GetData(self, 0) as *mut IsolateAnnex) } + unsafe { + &mut *(v8__Isolate__GetData(self, Self::ANNEX_SLOT) as *mut IsolateAnnex) + } } fn get_annex_arc(&self) -> Arc { @@ -186,22 +192,45 @@ impl Isolate { annex_arc } - /// Associate embedder-specific data with the isolate. |slot| has to be - /// between 0 and GetNumberOfDataSlots() - 1. + /// Associate embedder-specific data with the isolate. `slot` has to be + /// between 0 and `Isolate::get_number_of_data_slots()`. unsafe fn set_data(&mut self, slot: u32, ptr: *mut c_void) { - v8__Isolate__SetData(self, slot + 1, ptr) + v8__Isolate__SetData(self, slot + Self::INTERNAL_SLOT_COUNT, ptr) } /// Retrieve embedder-specific data from the isolate. - /// Returns NULL if SetData has never been called for the given |slot|. + /// Returns NULL if SetData has never been called for the given `slot`. fn get_data(&self, slot: u32) -> *mut c_void { - unsafe { v8__Isolate__GetData(self, slot + 1) } + unsafe { v8__Isolate__GetData(self, slot + Self::INTERNAL_SLOT_COUNT) } } /// Returns the maximum number of available embedder data slots. Valid slots - /// are in the range of 0 - GetNumberOfDataSlots() - 1. + /// are in the range of 0 - `Isolate::get_number_of_data_slots() - 1`. fn get_number_of_data_slots(&self) -> u32 { - unsafe { v8__Isolate__GetNumberOfDataSlots(self) - 1 } + unsafe { + v8__Isolate__GetNumberOfDataSlots(self) - Self::INTERNAL_SLOT_COUNT + } + } + + /// Returns a pointer to the `ScopeData` struct for the current scope. + pub(crate) fn get_current_scope_data(&self) -> Option> { + let scope_data_ptr = + unsafe { v8__Isolate__GetData(self, Self::CURRENT_SCOPE_DATA_SLOT) }; + NonNull::new(scope_data_ptr).map(NonNull::cast) + } + + /// Updates the slot that stores a `ScopeData` pointer for the current scope. + pub(crate) fn set_current_scope_data( + &mut self, + scope_data: Option>, + ) { + let scope_data_ptr = scope_data + .map(NonNull::cast) + .map(NonNull::as_ptr) + .unwrap_or_else(null_mut); + unsafe { + v8__Isolate__SetData(self, Self::CURRENT_SCOPE_DATA_SLOT, scope_data_ptr) + }; } /// Get mutable reference to embedder data. @@ -247,7 +276,7 @@ impl Isolate { /// Sets this isolate as the entered one for the current thread. /// Saves the previously entered one (if any), so that it can be /// restored when exiting. Re-entering an isolate is allowed. - pub(crate) fn enter(&mut self) { + pub(crate) fn enter_isolate(&mut self) { unsafe { v8__Isolate__Enter(self) } } @@ -256,7 +285,7 @@ impl Isolate { /// entered more than once. /// /// Requires: self == Isolate::GetCurrent(). - pub(crate) fn exit(&mut self) { + pub(crate) fn exit_isolate(&mut self) { unsafe { v8__Isolate__Exit(self) } } @@ -316,24 +345,6 @@ impl Isolate { } } - /// Schedules an exception to be thrown when returning to JavaScript. When an - /// exception has been scheduled it is illegal to invoke any JavaScript - /// operation; the caller must return immediately and only after the exception - /// has been handled does it become legal to invoke JavaScript operations. - /// - /// This function always returns the `undefined` value. - pub fn throw_exception( - &mut self, - exception: Local, - ) -> Local<'_, Value> { - let result = unsafe { - Local::from_raw(v8__Isolate__ThrowException(self, &*exception)) - } - .unwrap(); - debug_assert!(result.is_undefined()); - result - } - /// Runs the default MicrotaskQueue until it gets empty. /// Any exceptions thrown by microtask callbacks are swallowed. pub fn run_microtasks(&mut self) { @@ -348,11 +359,13 @@ impl Isolate { /// Disposes the isolate. The isolate must not be entered by any /// thread to be disposable. unsafe fn dispose(&mut self) { - let annex = self.get_annex_mut(); + // Drop the scope stack. + ScopeData::drop_root(self); // Set the `isolate` pointer inside the annex struct to null, so any // IsolateHandle that outlives the isolate will know that it can't call // methods on the isolate. + let annex = self.get_annex_mut(); { let _lock = annex.isolate_mutex.lock().unwrap(); annex.isolate = null_mut(); @@ -548,12 +561,6 @@ impl OwnedIsolate { } } -impl InIsolate for OwnedIsolate { - fn isolate(&mut self) -> &mut Isolate { - self.deref_mut() - } -} - impl Drop for OwnedIsolate { fn drop(&mut self) { unsafe { self.cxx_isolate.as_mut().dispose() } diff --git a/src/json.rs b/src/json.rs index 6ba9f2b9..d783a12d 100644 --- a/src/json.rs +++ b/src/json.rs @@ -1,9 +1,9 @@ // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. //! A JSON Parser and Stringifier. use crate::Context; +use crate::HandleScope; use crate::Local; use crate::String; -use crate::ToLocal; use crate::Value; extern "C" { @@ -19,20 +19,20 @@ extern "C" { /// Tries to parse the string `json_string` and returns it as value if /// successful. -pub fn parse<'sc>( - scope: &mut impl ToLocal<'sc>, +pub fn parse<'s>( + scope: &mut HandleScope<'s>, context: Local<'_, Context>, json_string: Local<'_, String>, -) -> Option> { +) -> Option> { unsafe { scope.cast_local(|_| v8__JSON__Parse(&*context, &*json_string)) } } /// Tries to stringify the JSON-serializable object `json_object` and returns /// it as string if successful. -pub fn stringify<'sc>( - scope: &mut impl ToLocal<'sc>, - context: Local<'sc, Context>, - json_object: Local<'sc, Value>, -) -> Option> { +pub fn stringify<'s>( + scope: &mut HandleScope<'s>, + context: Local<'s, Context>, + json_object: Local<'s, Value>, +) -> Option> { unsafe { scope.cast_local(|_| v8__JSON__Stringify(&*context, &*json_object)) } } diff --git a/src/lib.rs b/src/lib.rs index 00088a61..172b0c3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,14 +9,11 @@ //! v8::V8::initialize_platform(platform); //! v8::V8::initialize(); //! -//! let mut isolate = v8::Isolate::new(Default::default()); -//! -//! let mut handle_scope = v8::HandleScope::new(&mut isolate); -//! let scope = handle_scope.enter(); +//! let isolate = &mut v8::Isolate::new(Default::default()); //! +//! let scope = &mut v8::HandleScope::new(isolate); //! let context = v8::Context::new(scope); -//! let mut context_scope = v8::ContextScope::new(scope, context); -//! let scope = context_scope.enter(); +//! let scope = &mut v8::ContextScope::new(scope, context); //! //! let code = v8::String::new(scope, "'Hello' + ' World!'").unwrap(); //! println!("javascript code: {}", code.to_rust_string_lossy(scope)); @@ -26,44 +23,6 @@ //! let result = result.to_string(scope).unwrap(); //! println!("result: {}", result.to_rust_string_lossy(scope)); //! ``` -//! -//! # Design of Scopes -//! -//! Although the end is in sight, the design is still a bit in flux. -//! -//! The general idea is that the various scope classes mediate access to the v8 -//! Isolate and the various items on its heap (Local/Global handles, -//! return/escape slots, etc.). At any point in time there exists only one scope -//! object that is directly accessible, which guarantees that all interactions -//! with the Isolate are safe. -//! -//! A Scope as such is not a trait (there is currently an internal -//! ScopeDefinition trait but that's only there to make implementation easier). -//! -//! Rather, there are a number of traits that are implemented for the scopes -//! they're applicable to, you've probably seen most of them already. The -//! InIsolate which gives access to &mut Isolate is implemented for all scopes, -//! ToLocal (I might rename that) is implemented for all Scopes in which new -//! Local handles can be created and it sets the appropriate lifetime on them. -//! -//! Furthermore, many callbacks will receive receive an appropriate Scope object -//! as their first argument, which 'encodes' the the state the isolate is in -//! when the callback is called. E.g. a FunctionCallbackScope implements -//! InIsolate + and ToLocal (it acts as a HandleScope). -//! HostImportModuleDynamicallyScope would also implement InIsolate plus -//! EscapeLocal (it doesn't act like a HandleScope, but it lets you safely -//! escape one MaybeLocal which is returned to the caller). -//! -//! In a nutshell, that's it. -//! -//! Open TODOs are: -//! - Add these automatic scopes to more callbacks (in progress) and get rid of -//! the necessity to import the MapFnTo trait. -//! - Fully integrate TryCatch blocks into the scope system (currently a -//! TryCatch works like a scope internally but it's not integrated). -//! - Add methods to some on some of the scopes like get_context() for -//! ContextScope. -//! - Rename/reorganize/document. #![allow(clippy::missing_safety_doc)] #![allow(dead_code)] @@ -82,7 +41,6 @@ mod exception; mod external_references; mod function; mod global; -mod handle_scope; mod isolate; mod isolate_create_params; mod local; @@ -95,7 +53,7 @@ mod primitives; mod promise; mod property_attribute; mod proxy; -mod scope_traits; +mod scope; mod script; mod script_or_module; mod shared_array_buffer; @@ -109,7 +67,6 @@ mod value; pub mod inspector; pub mod json; -pub mod scope; pub mod script_compiler; // This module is intentionally named "V8" rather than "v8" to match the // C++ namespace "v8::V8". @@ -123,8 +80,6 @@ pub use external_references::ExternalReference; pub use external_references::ExternalReferences; pub use function::*; pub use global::Global; -pub use handle_scope::EscapableHandleScope; -pub use handle_scope::HandleScope; pub use isolate::HostImportModuleDynamicallyCallback; pub use isolate::HostInitializeImportMetaObjectCallback; pub use isolate::Isolate; @@ -148,10 +103,8 @@ pub use property_attribute::*; pub use proxy::*; pub use scope::CallbackScope; pub use scope::ContextScope; -pub use scope::FunctionCallbackScope; -pub use scope::PropertyCallbackScope; -pub use scope::Scope; -pub use scope_traits::*; +pub use scope::EscapableHandleScope; +pub use scope::HandleScope; pub use script::ScriptOrigin; pub use snapshot::FunctionCodeHandling; pub use snapshot::SnapshotCreator; diff --git a/src/local.rs b/src/local.rs index 11342811..114adead 100644 --- a/src/local.rs +++ b/src/local.rs @@ -38,51 +38,51 @@ use std::ptr::NonNull; /// never empty. In situations where empty handles are needed, use /// Option. #[repr(C)] -pub struct Local<'sc, T>(NonNull, PhantomData<&'sc ()>); +pub struct Local<'s, T>(NonNull, PhantomData<&'s ()>); -impl<'sc, T> Copy for Local<'sc, T> {} +impl<'s, T> Copy for Local<'s, T> {} -impl<'sc, T> Clone for Local<'sc, T> { +impl<'s, T> Clone for Local<'s, T> { fn clone(&self) -> Self { *self } } -impl<'sc, T> Local<'sc, T> { +impl<'s, T> Local<'s, T> { /// Create a local handle by downcasting from one of its super types. /// This function is unsafe because the cast is unchecked. - pub unsafe fn cast(other: Local<'sc, A>) -> Self + pub unsafe fn cast(other: Local<'s, A>) -> Self where - Local<'sc, A>: From, + Local<'s, A>: From, { transmute(other) } pub(crate) unsafe fn from_raw(ptr: *const T) -> Option { - Some(Self(NonNull::new(ptr as *mut _)?, PhantomData)) + NonNull::new(ptr as *mut _).map(|nn| Self::from_non_null(nn)) + } + + pub(crate) unsafe fn from_non_null(nn: NonNull) -> Self { + Self(nn, PhantomData) } pub(crate) fn as_non_null(self) -> NonNull { self.0 } - pub(crate) fn as_ptr(self) -> *const T { - self.0.as_ptr() - } - pub(crate) fn slice_into_raw(slice: &[Self]) -> &[*const T] { unsafe { &*(slice as *const [Self] as *const [*const T]) } } } -impl<'sc, T> Deref for Local<'sc, T> { +impl<'s, T> Deref for Local<'s, T> { type Target = T; fn deref(&self) -> &T { unsafe { self.0.as_ref() } } } -impl<'sc, T> DerefMut for Local<'sc, T> { +impl<'s, T> DerefMut for Local<'s, T> { fn deref_mut(&mut self) -> &mut T { unsafe { self.0.as_mut() } } diff --git a/src/module.rs b/src/module.rs index a8fe157b..ac86fe5e 100644 --- a/src/module.rs +++ b/src/module.rs @@ -9,10 +9,10 @@ use crate::support::MaybeBool; use crate::support::ToCFn; use crate::support::UnitType; use crate::Context; +use crate::HandleScope; use crate::Local; use crate::Module; use crate::String; -use crate::ToLocal; use crate::Value; /// Called during Module::instantiate_module. Provided with arguments: @@ -221,11 +221,11 @@ impl Module { /// kErrored and propagate the thrown exception (which is then also available /// via |GetException|). #[must_use] - pub fn evaluate<'sc>( + pub fn evaluate<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Module__Evaluate(&*self, &*context)) } } } diff --git a/src/number.rs b/src/number.rs index 88ec92b1..c42a3f5f 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,8 +1,8 @@ use crate::isolate::Isolate; +use crate::HandleScope; use crate::Integer; use crate::Local; use crate::Number; -use crate::ToLocal; extern "C" { fn v8__Number__New(isolate: *mut Isolate, value: f64) -> *const Number; @@ -16,12 +16,14 @@ extern "C" { } impl Number { - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s, ()>, value: f64, - ) -> Local<'sc, Number> { - unsafe { scope.cast_local(|scope| v8__Number__New(scope.isolate(), value)) } - .unwrap() + ) -> Local<'s, Number> { + unsafe { + scope.cast_local(|sd| v8__Number__New(sd.get_isolate_ptr(), value)) + } + .unwrap() } pub fn value(&self) -> f64 { @@ -30,23 +32,23 @@ impl Number { } impl Integer { - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s, ()>, value: i32, - ) -> Local<'sc, Integer> { + ) -> Local<'s, Integer> { unsafe { - scope.cast_local(|scope| v8__Integer__New(scope.isolate(), value)) + scope.cast_local(|sd| v8__Integer__New(sd.get_isolate_ptr(), value)) } .unwrap() } - pub fn new_from_unsigned<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new_from_unsigned<'s>( + scope: &mut HandleScope<'s, ()>, value: u32, - ) -> Local<'sc, Integer> { + ) -> Local<'s, Integer> { unsafe { - scope.cast_local(|scope| { - v8__Integer__NewFromUnsigned(scope.isolate(), value) + scope.cast_local(|sd| { + v8__Integer__NewFromUnsigned(sd.get_isolate_ptr(), value) }) } .unwrap() diff --git a/src/object.rs b/src/object.rs index b030c716..7404e968 100644 --- a/src/object.rs +++ b/src/object.rs @@ -5,12 +5,12 @@ use crate::support::MaybeBool; use crate::AccessorNameGetterCallback; use crate::Array; use crate::Context; +use crate::HandleScope; use crate::Local; use crate::Map; use crate::Name; use crate::Object; use crate::PropertyAttribute; -use crate::ToLocal; use crate::Value; extern "C" { @@ -93,8 +93,8 @@ extern "C" { impl Object { /// Creates an empty object. - pub fn new<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, Object> { - unsafe { scope.cast_local(|scope| v8__Object__New(scope.isolate())) } + pub fn new<'s>(scope: &mut HandleScope<'s>) -> Local<'s, Object> { + unsafe { scope.cast_local(|sd| v8__Object__New(sd.get_isolate_ptr())) } .unwrap() } @@ -103,19 +103,19 @@ impl Object { /// the newly created object won't have a prototype at all). This is similar /// to Object.create(). All properties will be created as enumerable, /// configurable and writable properties. - pub fn with_prototype_and_properties<'sc>( - scope: &mut impl ToLocal<'sc>, - prototype_or_null: Local<'sc, Value>, + pub fn with_prototype_and_properties<'s>( + scope: &mut HandleScope<'s>, + prototype_or_null: Local<'s, Value>, names: &[Local], values: &[Local], - ) -> Local<'sc, Object> { + ) -> Local<'s, Object> { assert_eq!(names.len(), values.len()); let names = Local::slice_into_raw(names); let values = Local::slice_into_raw(values); unsafe { - scope.cast_local(|scope| { + scope.cast_local(|sd| { v8__Object__New__with_prototype_and_properties( - scope.isolate(), + sd.get_isolate_ptr(), &*prototype_or_null, names.as_ptr(), values.as_ptr(), @@ -194,21 +194,21 @@ impl Object { .into() } - pub fn get<'a>( + pub fn get<'s>( &self, - scope: &mut impl ToLocal<'a>, + scope: &mut HandleScope<'s>, context: Local, key: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Object__Get(self, &*context, &*key)) } } - pub fn get_index<'a>( + pub fn get_index<'s>( &self, - scope: &mut impl ToLocal<'a>, + scope: &mut HandleScope<'s>, context: Local, index: u32, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Object__GetIndex(self, &*context, index)) } @@ -216,10 +216,10 @@ impl Object { /// Get the prototype object. This does not skip objects marked to be /// skipped by proto and it does not consult the security handler. - pub fn get_prototype<'a>( + pub fn get_prototype<'s>( &self, - scope: &mut impl ToLocal<'a>, - ) -> Option> { + scope: &mut HandleScope<'s>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__Object__GetPrototype(self)) } } @@ -246,21 +246,21 @@ impl Object { } /// Returns the context in which the object was created. - pub fn creation_context<'a>( + pub fn creation_context<'s>( &self, - scope: &mut impl ToLocal<'a>, - ) -> Local<'a, Context> { + scope: &mut HandleScope<'s>, + ) -> Local<'s, Context> { unsafe { scope.cast_local(|_| v8__Object__CreationContext(self)) }.unwrap() } /// This function has the same functionality as GetPropertyNames but the /// returned array doesn't contain the names of properties from prototype /// objects. - pub fn get_own_property_names<'sc>( + pub fn get_own_property_names<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Object__GetOwnPropertyNames(self, &*context)) } @@ -270,11 +270,11 @@ impl Object { /// object, including properties from prototype objects. The array returned by /// this method contains the same values as would be enumerated by a for-in /// statement over this object. - pub fn get_property_names<'sc>( + pub fn get_property_names<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Object__GetPropertyNames(self, &*context)) } @@ -284,28 +284,27 @@ impl Object { impl Array { /// Creates a JavaScript array with the given length. If the length /// is negative the returned array will have length 0. - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, - length: i32, - ) -> Local<'sc, Array> { - unsafe { scope.cast_local(|scope| v8__Array__New(scope.isolate(), length)) } - .unwrap() + pub fn new<'s>(scope: &mut HandleScope<'s>, length: i32) -> Local<'s, Array> { + unsafe { + scope.cast_local(|sd| v8__Array__New(sd.get_isolate_ptr(), length)) + } + .unwrap() } /// Creates a JavaScript array out of a Local array with a known /// length. - pub fn new_with_elements<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new_with_elements<'s>( + scope: &mut HandleScope<'s>, elements: &[Local], - ) -> Local<'sc, Array> { + ) -> Local<'s, Array> { if elements.is_empty() { return Self::new(scope, 0); } let elements = Local::slice_into_raw(elements); unsafe { - scope.cast_local(|scope| { + scope.cast_local(|sd| { v8__Array__New_with_elements( - scope.isolate(), + sd.get_isolate_ptr(), elements.as_ptr(), elements.len(), ) @@ -325,10 +324,7 @@ impl Map { } /// Returns an array of length size() * 2, where index N is the Nth key and /// index N + 1 is the Nth value. - pub fn as_array<'sc>( - &self, - scope: &mut impl ToLocal<'sc>, - ) -> Local<'sc, Array> { + pub fn as_array<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, Array> { unsafe { scope.cast_local(|_| v8__Map__As__Array(self)) }.unwrap() } } diff --git a/src/primitive_array.rs b/src/primitive_array.rs index f1a123c8..444a1740 100644 --- a/src/primitive_array.rs +++ b/src/primitive_array.rs @@ -1,10 +1,10 @@ // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. use crate::support::int; +use crate::HandleScope; use crate::Isolate; use crate::Local; use crate::Primitive; use crate::PrimitiveArray; -use crate::ToLocal; extern "C" { fn v8__PrimitiveArray__New( @@ -29,13 +29,13 @@ extern "C" { } impl PrimitiveArray { - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s>, length: usize, - ) -> Local<'sc, PrimitiveArray> { + ) -> Local<'s, PrimitiveArray> { unsafe { - scope.cast_local(|scope| { - v8__PrimitiveArray__New(scope.isolate(), length as int) + scope.cast_local(|sd| { + v8__PrimitiveArray__New(sd.get_isolate_ptr(), length as int) }) } .unwrap() @@ -45,25 +45,30 @@ impl PrimitiveArray { unsafe { v8__PrimitiveArray__Length(self) as usize } } - pub fn set<'sc>( + pub fn set<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, index: usize, item: Local<'_, Primitive>, ) { unsafe { - v8__PrimitiveArray__Set(self, scope.isolate(), index as int, &*item) + v8__PrimitiveArray__Set( + self, + scope.get_isolate_ptr(), + index as int, + &*item, + ) } } - pub fn get<'sc>( + pub fn get<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, index: usize, - ) -> Local<'sc, Primitive> { + ) -> Local<'s, Primitive> { unsafe { - scope.cast_local(|scope| { - v8__PrimitiveArray__Get(self, scope.isolate(), index as int) + scope.cast_local(|sd| { + v8__PrimitiveArray__Get(self, sd.get_isolate_ptr(), index as int) }) } .unwrap() diff --git a/src/primitives.rs b/src/primitives.rs index e0d0b12b..fe0e800c 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -1,8 +1,8 @@ use crate::isolate::Isolate; use crate::Boolean; +use crate::HandleScope; use crate::Local; use crate::Primitive; -use crate::ToLocal; extern "C" { fn v8__Null(isolate: *mut Isolate) -> *const Primitive; @@ -11,21 +11,21 @@ extern "C" { fn v8__Boolean__New(isolate: *mut Isolate, value: bool) -> *const Boolean; } -pub fn null<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, Primitive> { - unsafe { scope.cast_local(|scope| v8__Null(scope.isolate())) }.unwrap() +pub fn null<'s>(scope: &mut HandleScope<'s, ()>) -> Local<'s, Primitive> { + unsafe { scope.cast_local(|sd| v8__Null(sd.get_isolate_ptr())) }.unwrap() } -pub fn undefined<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, Primitive> { - unsafe { scope.cast_local(|scope| v8__Undefined(scope.isolate())) }.unwrap() +pub fn undefined<'s>(scope: &mut HandleScope<'s, ()>) -> Local<'s, Primitive> { + unsafe { scope.cast_local(|sd| v8__Undefined(sd.get_isolate_ptr())) }.unwrap() } impl Boolean { - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s, ()>, value: bool, - ) -> Local<'sc, Boolean> { + ) -> Local<'s, Boolean> { unsafe { - scope.cast_local(|scope| v8__Boolean__New(scope.isolate(), value)) + scope.cast_local(|sd| v8__Boolean__New(sd.get_isolate_ptr(), value)) } .unwrap() } diff --git a/src/promise.rs b/src/promise.rs index d29b822b..0c55cfd0 100644 --- a/src/promise.rs +++ b/src/promise.rs @@ -3,10 +3,10 @@ use std::marker::PhantomData; use crate::support::MaybeBool; use crate::Context; use crate::Function; +use crate::HandleScope; use crate::Local; use crate::Promise; use crate::PromiseResolver; -use crate::ToLocal; use crate::Value; extern "C" { @@ -79,22 +79,19 @@ impl Promise { /// Returns the content of the [[PromiseResult]] field. The Promise must not /// be pending. - pub fn result<'sc>( - &self, - scope: &mut impl ToLocal<'sc>, - ) -> Local<'sc, Value> { + pub fn result<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, Value> { unsafe { scope.cast_local(|_| v8__Promise__Result(&*self)) }.unwrap() } /// Register a rejection handler with a promise. /// /// See `Self::then2`. - pub fn catch<'sc>( + pub fn catch<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, handler: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Promise__Catch(&*self, &*context, &*handler)) } @@ -103,12 +100,12 @@ impl Promise { /// Register a resolution handler with a promise. /// /// See `Self::then2`. - pub fn then<'sc>( + pub fn then<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, handler: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Promise__Then(&*self, &*context, &*handler)) } @@ -118,13 +115,13 @@ impl Promise { /// The handler is given the respective resolution/rejection value as /// an argument. If the promise is already resolved/rejected, the handler is /// invoked at the end of turn. - pub fn then2<'sc>( + pub fn then2<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, on_fulfilled: Local, on_rejected: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| { v8__Promise__Then2(&*self, &*context, &*on_fulfilled, &*on_rejected) @@ -135,38 +132,38 @@ impl Promise { impl PromiseResolver { /// Create a new resolver, along with an associated promise in pending state. - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, - context: Local<'sc, Context>, - ) -> Option> { + pub fn new<'s>( + scope: &mut HandleScope<'s>, + context: Local<'s, Context>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__Promise__Resolver__New(&*context)) } } /// Extract the associated promise. - pub fn get_promise<'sc>( + pub fn get_promise<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Local<'sc, Promise> { + scope: &mut HandleScope<'s>, + ) -> Local<'s, Promise> { unsafe { scope.cast_local(|_| v8__Promise__Resolver__GetPromise(&*self)) } .unwrap() } /// Resolve the associated promise with a given value. /// Ignored if the promise is no longer pending. - pub fn resolve<'sc>( + pub fn resolve<'s>( &self, - context: Local<'sc, Context>, - value: Local<'sc, Value>, + context: Local<'s, Context>, + value: Local<'s, Value>, ) -> Option { unsafe { v8__Promise__Resolver__Resolve(&*self, &*context, &*value).into() } } /// Reject the associated promise with a given value. /// Ignored if the promise is no longer pending. - pub fn reject<'sc>( + pub fn reject<'s>( &self, - context: Local<'sc, Context>, - value: Local<'sc, Value>, + context: Local<'s, Context>, + value: Local<'s, Value>, ) -> Option { unsafe { v8__Promise__Resolver__Reject(&*self, &*context, &*value).into() } } diff --git a/src/proxy.rs b/src/proxy.rs index 0de80ffe..ee773c55 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,8 +1,8 @@ use crate::Context; +use crate::HandleScope; use crate::Local; use crate::Object; use crate::Proxy; -use crate::ToLocal; use crate::Value; extern "C" { @@ -18,28 +18,28 @@ extern "C" { } impl Proxy { - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s>, context: Local, target: Local, handler: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Proxy__New(&*context, &*target, &*handler)) } } - pub fn get_handler<'sc>( + pub fn get_handler<'s>( &mut self, - scope: &mut impl ToLocal<'sc>, - ) -> Local<'sc, Value> { + scope: &mut HandleScope<'s>, + ) -> Local<'s, Value> { unsafe { scope.cast_local(|_| v8__Proxy__GetHandler(&*self)) }.unwrap() } - pub fn get_target<'sc>( + pub fn get_target<'s>( &mut self, - scope: &mut impl ToLocal<'sc>, - ) -> Local<'sc, Value> { + scope: &mut HandleScope<'s>, + ) -> Local<'s, Value> { unsafe { scope.cast_local(|_| v8__Proxy__GetTarget(&*self)) }.unwrap() } diff --git a/src/scope.rs b/src/scope.rs index e3a64700..a2e426ad 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,179 +1,241 @@ // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. +//! This module's public API exports a number of 'scope' types. +//! +//! These types carry information about the state of the V8 Isolate, as well as +//! lifetimes for certain (return) values. More specialized scopes typically +//! deref to more generic scopes, and ultimately they all deref to `Isolate`. +//! +//! The scope types in the public API are all pointer wrappers, and they all +//! point at a heap-allocated struct `data::ScopeData`. `ScopeData` allocations +//! are never shared between scopes; each Handle/Context/CallbackScope gets +//! its own instance. +//! +//! Notes about the available scope types: +//! See also the tests at the end of this file. +//! +//! - `HandleScope<'s, ()>` +//! - 's = lifetime of local handles created in this scope, and of the scope +//! itself. +//! - This type is returned when a HandleScope is constructed from a direct +//! reference to an isolate (`&mut Isolate` or `&mut OwnedIsolate`). +//! - A `Context` is _not_ available. Only certain types JavaScript values can +//! be created: primitive values, templates, and instances of `Context`. +//! - Derefs to `Isolate`. +//! +//! - `HandleScope<'s>` +//! - 's = lifetime of local handles created in this scope, and of the scope +//! itself. +//! - A `Context` is available; any type of value can be created. +//! - Derefs to `HandleScope<'s, ()>` +//! +//! - `ContextScope<'s, P>` +//! - 's = lifetime of the scope itself. +//! - A `Context` is available; any type of value can be created. +//! - Derefs to `P`. +//! - When a constructed as the child of a `HandleScope<'a, ()>`, the returned +//! type is `ContextScope<'s, HandleScope<'p>>`. In other words, the parent +//! HandleScope gets an upgrade to indicate the availability of a `Context`. +//! - When a new scope is constructed inside this type of scope, the +//! `ContextScope` wrapper around `P` is erased first, which means that the +//! child scope is set up as if it had been created with `P` as its parent. +//! +//! - `EscapableHandleScope<'s, 'e>` +//! - 's = lifetime of local handles created in this scope, and of the scope +//! itself. +//! - 'e = lifetime of the HandleScope that will receive the local handle that +//! is created by `EscapableHandleScope::escape()`. +//! - A `Context` is available; any type of value can be created. +//! - Derefs 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. +//! - A `Context` is available; any type of value can be created. +//! - 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>`. + +use std::alloc::alloc; +use std::alloc::Layout; +use std::any::type_name; +use std::cell::Cell; use std::marker::PhantomData; -use std::mem::size_of; -use std::mem::take; use std::mem::MaybeUninit; +use std::num::NonZeroUsize; +use std::ops::Deref; +use std::ops::DerefMut; +use std::ptr; use std::ptr::NonNull; -use crate::scope_traits::internal::GetRawIsolate; +use crate::function::FunctionCallbackInfo; +use crate::function::PropertyCallbackInfo; use crate::Context; -use crate::FunctionCallbackInfo; -use crate::InIsolate; +use crate::Data; use crate::Isolate; use crate::Local; +use crate::Message; +use crate::Object; +use crate::OwnedIsolate; +use crate::Primitive; use crate::PromiseRejectMessage; -use crate::PropertyCallbackInfo; +use crate::Value; -// 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 ScopeDefinition<'s> -where - Self: Sized, -{ - type Args; - unsafe fn enter_scope(buf: *mut Self, args: Self::Args); +/// Stack-allocated class which sets the execution context for all operations +/// executed within a local scope. After entering a context, all code compiled +/// and run is compiled and run in this context. +pub struct ContextScope<'s, P> { + data: NonNull, + _phantom: PhantomData<&'s mut P>, } -/// A RAII scope wrapper object that will, when the `enter()` method is called, -/// initialize and activate the guarded object. -pub struct Scope<'s, S, P = ()> -where - S: ScopeDefinition<'s>, -{ - state: ScopeState<'s, S, P>, -} - -enum ScopeState<'s, S, P> -where - S: ScopeDefinition<'s>, -{ - Empty, - Allocated { - args: S::Args, - parent: &'s mut P, - }, - EnteredUninit { - data: MaybeUninit, - enter: MaybeUninit>, - }, - EnteredReady { - data: S, - enter: Entered<'s, S, P>, - }, -} - -fn parent_of_root() -> &'static mut () { - unsafe { &mut *NonNull::<()>::dangling().as_ptr() } -} - -impl<'s, S> Scope<'s, S, ()> -where - S: ScopeDefinition<'s>, -{ - /// Create a new root Scope object in unentered state. - pub(crate) fn new_root(args: S::Args) -> Self { - Self::new(args, parent_of_root()) - } -} - -impl<'s, S, P> Scope<'s, S, P> -where - S: ScopeDefinition<'s>, -{ - /// Create a new Scope object in unentered state. - pub(crate) fn new(args: S::Args, parent: &'s mut P) -> Self { - Self { - state: ScopeState::Allocated { args, parent }, - } - } - - /// 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<'s, S, P> { - assert_eq!(size_of::(), size_of::>()); - - use ScopeState::*; - let Self { state } = self; - - let (parent, args) = match take(state) { - Allocated { parent, args } => (parent, args), - _ => unreachable!(), - }; - - *state = EnteredUninit { - data: MaybeUninit::uninit(), - enter: MaybeUninit::uninit(), - }; - let data_ptr = match state { - EnteredUninit { data, .. } => data as *mut _ as *mut S, - _ => unreachable!(), - }; - - unsafe { S::enter_scope(data_ptr, args) }; - - *state = match take(state) { - EnteredUninit { data, .. } => EnteredReady { - data: unsafe { data.assume_init() }, - enter: Entered::new(unsafe { &mut *data_ptr }, parent), - }, - _ => unreachable!(), - }; - - match state { - EnteredReady { enter, .. } => enter, - _ => unreachable!(), +impl<'s, P: param::NewContextScope<'s>> ContextScope<'s, P> { + #[allow(clippy::new_ret_no_self)] + pub fn new(param: &'s mut P, context: Local) -> P::NewScope { + let scope_data = param.get_scope_data_mut(); + if scope_data.get_isolate_ptr() + != unsafe { raw::v8__Context__GetIsolate(&*context) } + { + panic!( + "{} and Context do not belong to the same Isolate", + type_name::

() + ) } + let new_scope_data = scope_data.new_context_scope_data(context); + new_scope_data.as_scope() } } -impl<'s, S, P> Default for ScopeState<'s, S, P> -where - S: ScopeDefinition<'s>, -{ - fn default() -> Self { - Self::Empty - } -} - -/// A wrapper around the an instantiated and entered scope object. -#[repr(C)] -pub struct Entered<'s, S, P = ()> { - data: *mut S, - parent: &'s mut P, -} - -impl<'s, S> Entered<'s, S, ()> { - pub(crate) fn new_root(data: *mut S) -> Self { - Self { - data, - parent: parent_of_root(), - } - } -} - -impl<'s, S, P> Entered<'s, S, P> { - pub(crate) fn new(data: *mut S, parent: &'s mut P) -> Self { - Self { data, parent } - } - - pub(crate) fn data(&self) -> &S { - unsafe { &*self.data } - } - - pub(crate) fn data_mut(&mut self) -> &mut S { - unsafe { &mut *self.data } - } - - pub(crate) fn parent(&self) -> &P { - &self.parent - } - - pub(crate) fn parent_mut(&mut self) -> &mut P { - &mut self.parent - } -} - -/// A CallbackScope can be used to obtain a mutable Isolate reference within -/// a callback that is called by V8 on the thread that already has a Locker -/// on the stack. +/// A stack-allocated class that governs a number of local handles. +/// After a handle scope has been created, all local handles will be +/// allocated within that handle scope until either the handle scope is +/// deleted or another handle scope is created. If there is already a +/// handle scope and a new one is created, all allocations will take +/// place in the new handle scope until it is deleted. After that, +/// new handles will again be allocated in the original handle scope. /// -/// Using a CallbackScope in any other situation is unsafe. -/// Also note that CallbackScope should not be used for function and property -/// accessor callbacks; use FunctionCallbackScope and PropertyCallbackScope -/// instead. +/// After the handle scope of a local handle has been deleted the +/// garbage collector will no longer track the object stored in the +/// handle and may deallocate it. The behavior of accessing a handle +/// for which the handle scope has been deleted is undefined. +pub struct HandleScope<'s, C = Context> { + data: NonNull, + _phantom: PhantomData<&'s mut C>, +} + +impl<'s> HandleScope<'s> { + #[allow(clippy::new_ret_no_self)] + pub fn new>(param: &'s mut P) -> P::NewScope { + param + .get_scope_data_mut() + .new_handle_scope_data() + .as_scope() + } + + /// Returns the context of the currently running JavaScript, or the context + /// on the top of the stack if no JavaScript is running. + pub fn get_current_context(&self) -> Local<'s, Context> { + let context_ptr = data::ScopeData::get(self).get_current_context(); + unsafe { Local::from_raw(context_ptr) }.unwrap() + } + + /// Returns either the last context entered through V8's C++ API, or the + /// context of the currently running microtask while processing microtasks. + /// If a context is entered while executing a microtask, that context is + /// returned. + pub fn get_entered_or_microtask_context(&self) -> Local<'s, Context> { + let data = data::ScopeData::get(self); + let isolate_ptr = data.get_isolate_ptr(); + let context_ptr = + unsafe { raw::v8__Isolate__GetEnteredOrMicrotaskContext(isolate_ptr) }; + unsafe { Local::from_raw(context_ptr) }.unwrap() + } +} + +impl<'s> HandleScope<'s, ()> { + /// Schedules an exception to be thrown when returning to JavaScript. When + /// an exception has been scheduled it is illegal to invoke any + /// JavaScript operation; the caller must return immediately and only + /// after the exception has been handled does it become legal to invoke + /// JavaScript operations. + /// + /// This function always returns the `undefined` value. + pub fn throw_exception( + &mut self, + exception: Local, + ) -> Local<'s, Value> { + unsafe { + self.cast_local(|sd| { + raw::v8__Isolate__ThrowException(sd.get_isolate_ptr(), &*exception) + }) + } + .unwrap() + } + + pub(crate) unsafe fn cast_local( + &mut self, + f: impl FnOnce(&mut data::ScopeData) -> *const T, + ) -> Option> { + Local::from_raw(f(data::ScopeData::get_mut(self))) + } + + pub(crate) fn get_isolate_ptr(&self) -> *mut Isolate { + data::ScopeData::get(self).get_isolate_ptr() + } +} + +/// A HandleScope which first allocates a handle in the current scope +/// which will be later filled with the escape value. +// TODO(piscisaureus): type parameter `C` is not very useful in practice; being +// a source of complexity and potential confusion, it is desirable to +// eventually remove it. Blocker at the time of writing is that there are some +// tests that enter an `EscapableHandleScope` without creating a `ContextScope` +// at all. These tests need to updated first. +pub struct EscapableHandleScope<'s, 'e: 's, C = Context> { + data: NonNull, + _phantom: + PhantomData<(&'s mut raw::HandleScope, &'e mut raw::EscapeSlot, &'s C)>, +} + +impl<'s, 'e: 's> EscapableHandleScope<'s, 'e> { + #[allow(clippy::new_ret_no_self)] + pub fn new>( + param: &'s mut P, + ) -> P::NewScope { + param + .get_scope_data_mut() + .new_escapable_handle_scope_data() + .as_scope() + } +} + +impl<'s, 'e: 's, C> EscapableHandleScope<'s, 'e, C> { + /// Pushes the value into the previous scope and returns a handle to it. + /// Cannot be called twice. + pub fn escape(&mut self, value: Local) -> Local<'e, T> + where + for<'l> Local<'l, T>: Into>, + { + let escape_slot = data::ScopeData::get_mut(self) + .get_escape_slot_mut() + .expect("internal error: EscapableHandleScope has no escape slot") + .take() + .expect("EscapableHandleScope::escape() called twice"); + escape_slot.escape(value) + } +} + +/// A `CallbackScope` can be used to bootstrap a `HandleScope` and +/// `ContextScope` inside a callback function that gets called by V8. +/// Bootstrapping a scope inside a callback is the only valid use case of this +/// type; using it in other places leads to undefined behavior, which is also +/// the reason `CallbackScope::new()` is marked as being an unsafe function. +/// +/// For some callback types, rusty_v8 internally creates a scope and passes it +/// as an argument to to embedder callback. Eventually we intend to wrap all +/// callbacks in this fashion, so the embedder would never needs to construct +/// a CallbackScope. /// /// A CallbackScope can be created from the following inputs: /// - `Local` @@ -181,112 +243,1038 @@ impl<'s, S, P> Entered<'s, S, P> { /// - `Local` /// - `Local` /// - `Local` +/// - `&FunctionCallbackInfo` +/// - `&PropertyCallbackInfo` /// - `&PromiseRejectMessage` -pub struct CallbackScope { - isolate: *mut Isolate, - phantom: PhantomData, +pub struct CallbackScope<'s> { + data: NonNull, + _phantom: PhantomData<&'s mut HandleScope<'s>>, } -pub struct Contained; -pub struct Escapable; +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()) + .as_scope() + } +} -impl<'s> CallbackScope { - pub fn new(input: I) -> Scope<'s, Self> - where - Scope<'s, Self>: From, +macro_rules! impl_as { + // Implements `AsRef` and AsMut` on a scope type. + (<$($params:tt),+> $src_type:ty as Isolate) => { + impl<$($params),*> AsRef for $src_type { + fn as_ref(&self) -> &Isolate { + data::ScopeData::get(self).get_isolate() + } + } + + impl<$($params),*> AsMut for $src_type { + fn as_mut(&mut self) -> &mut Isolate { + data::ScopeData::get_mut(self).get_isolate_mut() + } + } + }; + + // Implements `AsRef` and `AsMut` traits for the purpose of converting a + // a scope reference to a scope reference with a different but compatible type. + (<$($params:tt),+> $src_type:ty as $tgt_type:ty) => { + impl<$($params),*> AsRef<$tgt_type> for $src_type { + fn as_ref(&self) -> &$tgt_type { + self.cast_ref() + } + } + + impl<$($params),*> AsMut< $tgt_type> for $src_type { + fn as_mut(&mut self) -> &mut $tgt_type { + self.cast_mut() + } + } + }; +} + +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> CallbackScope<'s> 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, ()>); +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> CallbackScope<'s> 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>); +impl_as!(<'s> HandleScope<'s> as HandleScope<'s>); +impl_as!(<'s, 'e> EscapableHandleScope<'s, 'e> as HandleScope<'s>); +impl_as!(<'s> CallbackScope<'s> as HandleScope<'s>); + +impl_as!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e, ()>); +impl_as!(<'s, 'e, C> EscapableHandleScope<'s, 'e, C> as EscapableHandleScope<'s, 'e, ()>); + +impl_as!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e>); +impl_as!(<'s, 'e> EscapableHandleScope<'s, 'e> as EscapableHandleScope<'s, 'e>); + +macro_rules! impl_deref { + (<$($params:tt),+> $src_type:ty as $tgt_type:ty) => { + impl<$($params),*> Deref for $src_type { + type Target = $tgt_type; + fn deref(&self) -> &Self::Target { + self.as_ref() + } + } + + impl<$($params),*> DerefMut for $src_type { + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut() + } + } + }; +} + +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 HandleScope<'s, ()>); + +impl_deref!(<'s, 'e> EscapableHandleScope<'s, 'e, ()> as HandleScope<'s, ()>); +impl_deref!(<'s, 'e> EscapableHandleScope<'s, 'e> as HandleScope<'s>); + +impl_deref!(<'s> CallbackScope<'s> as HandleScope<'s>); + +macro_rules! impl_scope_drop { + (<$($params:tt),+> $type:ty) => { + unsafe impl<$($params),*> Scope for $type {} + + impl<$($params),*> Drop for $type { + fn drop(&mut self) { + data::ScopeData::get_mut(self).notify_scope_dropped(); + } + } + }; +} + +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> CallbackScope<'s> ); + +pub unsafe trait Scope: Sized {} + +trait ScopeCast: Sized { + fn cast_ref(&self) -> &S; + fn cast_mut(&mut self) -> &mut S; +} + +impl ScopeCast for T { + fn cast_ref(&self) -> &S { + assert_eq!(Layout::new::(), Layout::new::()); + unsafe { &*(self as *const _ as *const S) } + } + + fn cast_mut(&mut self) -> &mut S { + assert_eq!(Layout::new::(), Layout::new::()); + unsafe { &mut *(self as *mut _ as *mut S) } + } +} + +/// Scopes are typically constructed as the child of another scope. The scope +/// that is returned from `«Child»Scope::new(parent: &mut «Parent»Scope)` does +/// not necessarily have type `«Child»Scope`, but rather its type is a merger of +/// both the the parent and child scope types. +/// +/// For example: a `ContextScope` created inside `HandleScope<'a, ()>` does not +/// produce a `ContextScope`, but rather a `HandleScope<'a, Context>`, which +/// describes a scope that is both a `HandleScope` _and_ a `ContextScope`. +/// +/// The Traits in the (private) `param` module define which types can be passed +/// as a parameter to the `«Some»Scope::new()` constructor, and what the +/// actual, merged scope type will be that `new()` returns for a specific +/// parameter type. +mod param { + use super::*; + + pub trait NewContextScope<'s>: data::GetScopeData { + type NewScope: Scope; + } + + impl<'s, 'p: 's, P: Scope> NewContextScope<'s> for ContextScope<'p, P> { + type NewScope = ContextScope<'s, P>; + } + + impl<'s, 'p: 's, C> NewContextScope<'s> for HandleScope<'p, C> { + type NewScope = ContextScope<'s, HandleScope<'p>>; + } + + impl<'s, 'p: 's, 'e: 'p, C> NewContextScope<'s> + for EscapableHandleScope<'p, 'e, C> { - Scope::from(input) + type NewScope = ContextScope<'s, EscapableHandleScope<'p, 'e>>; } -} -impl<'s> CallbackScope { - pub fn new_escapable(input: I) -> Scope<'s, Self> - where - Scope<'s, Self>: From, + impl<'s, 'p: 's> NewContextScope<'s> for CallbackScope<'p> { + type NewScope = ContextScope<'s, HandleScope<'p>>; + } + + pub trait NewHandleScope<'s>: data::GetScopeData { + type NewScope: Scope; + } + + impl<'s> NewHandleScope<'s> for Isolate { + type NewScope = HandleScope<'s, ()>; + } + + impl<'s> NewHandleScope<'s> for OwnedIsolate { + type NewScope = HandleScope<'s, ()>; + } + + impl<'s, 'p: 's, P: NewHandleScope<'s>> NewHandleScope<'s> + for ContextScope<'p, P> { - Scope::from(input) + type NewScope =

>::NewScope; } -} -impl CallbackScope { - pub(crate) fn get_raw_isolate_(&self) -> *mut Isolate { - self.isolate + impl<'s, 'p: 's, C> NewHandleScope<'s> for HandleScope<'p, C> { + type NewScope = HandleScope<'s, C>; } -} -unsafe impl<'s, X> ScopeDefinition<'s> for CallbackScope { - type Args = *mut Isolate; - unsafe fn enter_scope(ptr: *mut Self, isolate: Self::Args) { - let data = Self { - isolate, - phantom: PhantomData, - }; - std::ptr::write(ptr, data); - } -} - -impl<'s, X> From<&'s mut Isolate> for Scope<'s, CallbackScope> { - fn from(isolate: &'s mut Isolate) -> Self { - Scope::new_root(isolate as *mut Isolate) - } -} - -impl<'s, X, T> From> for Scope<'s, CallbackScope> -where - Local<'s, T>: GetRawIsolate, -{ - fn from(local: Local<'s, T>) -> Self { - Scope::new_root(local.get_raw_isolate()) - } -} - -impl<'s, X> From<&'s PromiseRejectMessage<'s>> for Scope<'s, CallbackScope> { - fn from(msg: &'s PromiseRejectMessage<'s>) -> Self { - Self::from(msg.get_promise()) - } -} - -/// Stack-allocated class which sets the execution context for all operations -/// executed within a local scope. -pub struct ContextScope { - context: *mut Context, -} - -impl<'s> ContextScope { - pub fn new

( - parent: &'s mut P, - context: Local<'s, Context>, - ) -> Scope<'s, Self, P> - where - P: InIsolate, + impl<'s, 'p: 's, 'e: 'p, C> NewHandleScope<'s> + for EscapableHandleScope<'p, 'e, C> { - Scope::new(context, parent) + type NewScope = EscapableHandleScope<'s, 'e, C>; } - pub(crate) unsafe fn get_captured_context(&self) -> Local<'s, Context> { - Local::from_raw(self.context).unwrap() + impl<'s, 'p: 's> NewHandleScope<'s> for CallbackScope<'p> { + type NewScope = HandleScope<'s>; + } + + pub trait NewEscapableHandleScope<'s, 'e: 's>: data::GetScopeData { + type NewScope: Scope; + } + + impl<'s, 'p: 's, 'e: 'p, P: NewEscapableHandleScope<'s, 'e>> + NewEscapableHandleScope<'s, 'e> for ContextScope<'p, P> + { + type NewScope =

>::NewScope; + } + + impl<'s, 'p: 's, C> NewEscapableHandleScope<'s, 'p> for HandleScope<'p, C> { + type NewScope = EscapableHandleScope<'s, 'p, C>; + } + + impl<'s, 'p: 's, 'e: 'p, C> NewEscapableHandleScope<'s, 'p> + for EscapableHandleScope<'p, 'e, C> + { + type NewScope = EscapableHandleScope<'s, 'p, C>; + } + + impl<'s, 'p: 's> NewEscapableHandleScope<'s, 'p> for CallbackScope<'p> { + type NewScope = EscapableHandleScope<'s, 'p>; + } + + pub trait NewCallbackScope<'s>: Copy + Sized { + fn maybe_get_current_context(self) -> Option> { + None + } + fn get_isolate_mut(self) -> &'s mut Isolate; + } + + impl<'s> NewCallbackScope<'s> for Local<'s, Context> { + fn maybe_get_current_context(self) -> Option> { + Some(self) + } + + fn get_isolate_mut(self) -> &'s mut Isolate { + unsafe { &mut *raw::v8__Context__GetIsolate(&*self) } + } + } + + impl<'s> NewCallbackScope<'s> for Local<'s, Message> { + fn get_isolate_mut(self) -> &'s mut Isolate { + unsafe { &mut *raw::v8__Message__GetIsolate(&*self) } + } + } + + 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> NewCallbackScope<'s> for &'s PromiseRejectMessage<'s> { + fn get_isolate_mut(self) -> &'s mut Isolate { + let object: Local = self.get_promise().into(); + unsafe { &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) } + } + } + + impl<'s> NewCallbackScope<'s> for &'s PropertyCallbackInfo { + fn get_isolate_mut(self) -> &'s mut Isolate { + unsafe { &mut *raw::v8__PropertyCallbackInfo__GetIsolate(self) } + } } } -unsafe impl<'s> ScopeDefinition<'s> for ContextScope { - type Args = Local<'s, Context>; +/// All publicly exported `«Some»Scope` types are essentially wrapping a pointer +/// to a heap-allocated struct `ScopeData`. This module contains the definition +/// for `ScopeData` and its inner types, as well as related helper traits. +pub(crate) mod data { + use super::*; - unsafe fn enter_scope(ptr: *mut Self, mut context: Self::Args) { - context.enter(); - std::ptr::write( - ptr, + pub struct ScopeData { + // The first four fields are always valid - even when the `Box` + // struct is free (does not contain data related to an actual scope). + // The `previous` and `isolate` fields never change; the `next` field is + // set to `None` initially when the struct is created, but it may later be + // assigned a `Some(Box)` value, after which this field never + // changes again. + isolate: NonNull, + previous: Option>, + next: Option>, + // The 'status' field is also always valid (but does change). + status: Cell, + // The following fields are only valid when this ScopeData object is in use + // (eiter current or shadowed -- not free). + context: Cell>>, + escape_slot: Option>>, + scope_type_specific_data: ScopeTypeSpecificData, + } + + impl ScopeData { + /// Returns a mutable reference to the data associated with topmost scope + /// on the scope stack. This function does not automatically exit zombie + /// scopes, so it might return a zombie ScopeData reference. + pub(crate) fn get_current_mut(isolate: &mut Isolate) -> &mut Self { + let self_mut = isolate + .get_current_scope_data() + .map(NonNull::as_ptr) + .map(|p| unsafe { &mut *p }) + .unwrap(); + match self_mut.status.get() { + ScopeStatus::Current { .. } => self_mut, + _ => unreachable!(), + } + } + + /// Initializes the scope stack by creating a 'dummy' `ScopeData` at the + /// very bottom. This makes it possible to store the freelist of reusable + /// ScopeData objects even when no scope is entered. + pub(crate) fn new_root(isolate: &mut Isolate) { + let root = Box::leak(Self::boxed(isolate.into())); + root.status = ScopeStatus::Current { zombie: false }.into(); + debug_assert!(isolate.get_current_scope_data().is_none()); + isolate.set_current_scope_data(Some(root.into())); + } + + /// Activates and returns the 'root' `ScopeData` object that is created when + /// the isolate is initialized. In order to do so, any zombie scopes that + /// remain on the scope stack are cleaned up. + /// + /// # Panics + /// + /// This function panics if the root can't be activated because there are + /// still other scopes on the stack and they're not zombies. + pub(crate) fn get_root_mut(isolate: &mut Isolate) -> &mut Self { + let mut current_scope_data = Self::get_current_mut(isolate); + loop { + current_scope_data = match current_scope_data { + root if root.previous.is_none() => break root, + data => data.try_exit_scope(), + }; + } + } + + /// Drops the scope stack and releases all `Box` allocations. + /// This function should be called only when an Isolate is being disposed. + pub(crate) fn drop_root(isolate: &mut Isolate) { + let root = Self::get_root_mut(isolate); + unsafe { Box::from_raw(root) }; + isolate.set_current_scope_data(None); + } + + pub(super) fn new_context_scope_data<'s>( + &'s mut self, + context: Local<'s, Context>, + ) -> &'s mut Self { + self.new_scope_data_with(move |data| { + data.scope_type_specific_data.init_with(|| { + ScopeTypeSpecificData::ContextScope { + raw_context_scope: raw::ContextScope::new(context), + } + }); + data.context.set(Some(context.as_non_null())); + }) + } + + pub(super) fn new_handle_scope_data(&mut self) -> &mut Self { + self.new_scope_data_with(|data| { + let isolate = data.isolate; + data.scope_type_specific_data.init_with(|| { + ScopeTypeSpecificData::HandleScope { + raw_handle_scope: unsafe { raw::HandleScope::uninit() }, + } + }); + match &mut data.scope_type_specific_data { + ScopeTypeSpecificData::HandleScope { raw_handle_scope } => { + unsafe { raw_handle_scope.init(isolate) }; + } + _ => unreachable!(), + } + }) + } + + pub(super) fn new_escapable_handle_scope_data(&mut self) -> &mut Self { + self.new_scope_data_with(|data| { + // Note: the `raw_escape_slot` field must be initialized _before_ the + // `raw_handle_scope` field, otherwise the escaped local handle ends up + // inside the `EscapableHandleScope` that's being constructed here, + // rather than escaping from it. + let isolate = data.isolate; + data.scope_type_specific_data.init_with(|| { + ScopeTypeSpecificData::EscapableHandleScope { + raw_handle_scope: unsafe { raw::HandleScope::uninit() }, + raw_escape_slot: Some(raw::EscapeSlot::new(isolate)), + } + }); + match &mut data.scope_type_specific_data { + ScopeTypeSpecificData::EscapableHandleScope { + raw_handle_scope, + raw_escape_slot, + } => { + unsafe { raw_handle_scope.init(isolate) }; + data.escape_slot.replace(raw_escape_slot.into()); + } + _ => unreachable!(), + } + }) + } + + pub(super) fn new_callback_scope_data<'s>( + &'s mut self, + maybe_current_context: Option>, + ) -> &'s mut Self { + self.new_scope_data_with(|data| { + debug_assert!(data.scope_type_specific_data.is_none()); + data + .context + .set(maybe_current_context.map(|cx| cx.as_non_null())); + }) + } + + fn new_scope_data_with( + &mut self, + init_fn: impl FnOnce(&mut Self), + ) -> &mut Self { + // Mark this scope (the parent of the newly created scope) as 'shadowed'; + self.status.set(match self.status.get() { + ScopeStatus::Current { zombie } => ScopeStatus::Shadowed { zombie }, + _ => unreachable!(), + }); + // Copy fields that that will be inherited by the new scope. + let context = self.context.get().into(); + let escape_slot = self.escape_slot; + // Initialize the `struct ScopeData` for the new scope. + let new_scope_data = self.allocate_or_reuse_scope_data(); + // In debug builds, `zombie` is initially set to `true`, and the flag is + // later cleared in the `as_scope()` method, to verify that we're + // always creating exactly one scope from any `ScopeData` object. + // For performance reasons this check is not performed in release builds. + new_scope_data.status = Cell::new(ScopeStatus::Current { + zombie: cfg!(debug_assertions), + }); + // Store fields inherited from the parent scope. + new_scope_data.context = context; + new_scope_data.escape_slot = escape_slot; + (init_fn)(new_scope_data); + // Make the newly created scope the 'current' scope for this isolate. + let new_scope_nn = unsafe { NonNull::new_unchecked(new_scope_data) }; + new_scope_data + .get_isolate_mut() + .set_current_scope_data(Some(new_scope_nn)); + new_scope_data + } + + /// Either returns an free `Box` that is available for reuse, + /// or allocates a new one on the heap. + fn allocate_or_reuse_scope_data(&mut self) -> &mut Self { + let self_nn = NonNull::new(self); + match &mut self.next { + Some(next_box) => { + // Reuse a free `Box` allocation. + debug_assert_eq!(next_box.isolate, self.isolate); + debug_assert_eq!(next_box.previous, self_nn); + debug_assert_eq!(next_box.status.get(), ScopeStatus::Free); + debug_assert!(next_box.scope_type_specific_data.is_none()); + next_box.as_mut() + } + next_field @ None => { + // Allocate a new `Box`. + let mut next_box = Self::boxed(self.isolate); + next_box.previous = self_nn; + next_field.replace(next_box); + next_field.as_mut().unwrap() + } + } + } + + pub(super) fn as_scope(&mut self) -> S { + assert_eq!(Layout::new::<&mut Self>(), Layout::new::()); + // In debug builds, a new initialized `ScopeStatus` will have the `zombie` + // flag set, so we have to reset it. In release builds, new `ScopeStatus` + // objects come with the `zombie` flag cleared, so no update is necessary. + if cfg!(debug_assertions) { + assert_eq!(self.status.get(), ScopeStatus::Current { zombie: true }); + self.status.set(ScopeStatus::Current { zombie: false }); + } + let self_nn = NonNull::from(self); + unsafe { ptr::read(&self_nn as *const _ as *const S) } + } + + pub(super) fn get(scope: &S) -> &Self { + let self_mut = unsafe { + (*(scope as *const S as *mut S as *mut NonNull)).as_mut() + }; + self_mut.try_activate_scope(); + self_mut + } + + pub(super) fn get_mut(scope: &mut S) -> &mut Self { + let self_mut = + unsafe { (*(scope as *mut S as *mut NonNull)).as_mut() }; + self_mut.try_activate_scope(); + self_mut + } + + #[inline(always)] + fn try_activate_scope(mut self: &mut Self) -> &mut Self { + self = match self.status.get() { + ScopeStatus::Current { zombie: false } => self, + ScopeStatus::Shadowed { zombie: false } => { + self.next.as_mut().unwrap().try_exit_scope() + } + _ => unreachable!(), + }; + debug_assert_eq!( + self.get_isolate().get_current_scope_data(), + NonNull::new(self as *mut _) + ); + self + } + + fn try_exit_scope(mut self: &mut Self) -> &mut Self { + loop { + self = match self.status.get() { + ScopeStatus::Shadowed { .. } => { + self.next.as_mut().unwrap().try_exit_scope() + } + ScopeStatus::Current { zombie: true } => break self.exit_scope(), + ScopeStatus::Current { zombie: false } => { + panic!("active scope can't be dropped") + } + _ => unreachable!(), + } + } + } + + fn exit_scope(&mut self) -> &mut Self { + // Clear out the scope type specific data field. None of the other fields + // have a destructor, and there's no need to do any cleanup on them. + self.scope_type_specific_data = Default::default(); + // Change the ScopeData's status field from 'Current' to 'Free', which + // means that it is not associated with a scope and can be reused. + self.status.set(ScopeStatus::Free); + + // Point the Isolate's current scope data slot at our parent scope. + let previous_nn = self.previous.unwrap(); + self + .get_isolate_mut() + .set_current_scope_data(Some(previous_nn)); + // Update the parent scope's status field to reflect that it is now + // 'Current' again an no longer 'Shadowed'. + let previous_mut = unsafe { &mut *previous_nn.as_ptr() }; + previous_mut.status.set(match previous_mut.status.get() { + ScopeStatus::Shadowed { zombie } => ScopeStatus::Current { zombie }, + _ => unreachable!(), + }); + + previous_mut + } + + /// This function is called when any of the public scope objects (e.g + /// `HandleScope`, `ContextScope`, etc.) are dropped. + /// + /// The Rust borrow checker allows values of type `HandleScope<'a>` and + /// `EscapableHandleScope<'a, 'e>` to be dropped before their maximum + /// lifetime ('a) is up. This creates a potential problem because any local + /// handles that are created while these scopes are active are bound to + /// that 'a lifetime. This means that we run the risk of creating local + /// handles that outlive their creation scope. + /// + /// Therefore, we don't immediately exit the current scope at the very + /// moment the user drops their Escapable/HandleScope handle. + /// Instead, the current scope is marked as being a 'zombie': the scope + /// itself is gone, but its data still on the stack. The zombie's data will + /// be dropped when the user touches the parent scope; when that happens, it + /// is certain that there are no accessible `Local<'a, T>` handles left, + /// because the 'a lifetime ends there. + /// + /// Scope types that do no store local handles are exited immediately. + pub(super) fn notify_scope_dropped(&mut self) { + match &self.scope_type_specific_data { + ScopeTypeSpecificData::HandleScope { .. } + | ScopeTypeSpecificData::EscapableHandleScope { .. } => { + // Defer scope exit until the parent scope is touched. + self.status.set(match self.status.get() { + ScopeStatus::Current { zombie: false } => { + ScopeStatus::Current { zombie: true } + } + _ => unreachable!(), + }) + } + _ => { + // Regular, immediate exit. + self.exit_scope(); + } + } + } + + pub(crate) fn get_isolate(&self) -> &Isolate { + unsafe { self.isolate.as_ref() } + } + + pub(crate) fn get_isolate_mut(&mut self) -> &mut Isolate { + unsafe { self.isolate.as_mut() } + } + + pub(crate) fn get_isolate_ptr(&self) -> *mut Isolate { + self.isolate.as_ptr() + } + + pub(crate) fn get_current_context(&self) -> *const Context { + // To avoid creating a new Local every time `get_current_context() is + // called, the current context is usually cached in the `context` field. + // If the `context` field contains `None`, this might mean that this cache + // field never got populated, so we'll do that here when necessary. + let get_current_context_from_isolate = || unsafe { + raw::v8__Isolate__GetCurrentContext(self.get_isolate_ptr()) + }; + match self.context.get().map(|nn| nn.as_ptr() as *const _) { + Some(context) => { + debug_assert!(unsafe { + raw::v8__Context__EQ(context, get_current_context_from_isolate()) + }); + context + } + None => { + let context = get_current_context_from_isolate(); + self.context.set(NonNull::new(context as *mut _)); + context + } + } + } + + pub(super) fn get_escape_slot_mut( + &mut self, + ) -> Option<&mut Option> { + self + .escape_slot + .as_mut() + .map(|escape_slot_nn| unsafe { escape_slot_nn.as_mut() }) + } + + /// Returns a new `Box` with the `isolate` field set as specified + /// by the first parameter, and the other fields initialized to their + /// default values. This function exists solely because it turns out that + /// Rust doesn't optimize `Box::new(Self{ .. })` very well (a.k.a. not at + /// all) in this case, which is why `std::alloc::alloc()` is used directly. + fn boxed(isolate: NonNull) -> Box { + unsafe { + #[allow(clippy::cast_ptr_alignment)] + let self_ptr = alloc(Layout::new::()) as *mut Self; + ptr::write( + self_ptr, + Self { + isolate, + previous: Default::default(), + next: Default::default(), + status: Default::default(), + context: Default::default(), + escape_slot: Default::default(), + scope_type_specific_data: Default::default(), + }, + ); + Box::from_raw(self_ptr) + } + } + } + + #[derive(Debug, Clone, Copy, Eq, PartialEq)] + enum ScopeStatus { + Free, + Current { zombie: bool }, + Shadowed { zombie: bool }, + } + + impl Default for ScopeStatus { + fn default() -> Self { + Self::Free + } + } + + enum ScopeTypeSpecificData { + None, + ContextScope { + raw_context_scope: raw::ContextScope, + }, + HandleScope { + raw_handle_scope: raw::HandleScope, + }, + EscapableHandleScope { + raw_handle_scope: raw::HandleScope, + raw_escape_slot: Option, + }, + } + + impl Default for ScopeTypeSpecificData { + fn default() -> Self { + Self::None + } + } + + impl ScopeTypeSpecificData { + pub fn is_none(&self) -> bool { + match self { + Self::None => true, + _ => false, + } + } + + /// Replaces a `ScopeTypeSpecificData::None` value with the value returned + /// from the specified closure. This function exists because initializing + /// scopes is performance critical, and `ptr::write()` produces more + /// efficient code than using a regular assign statement, which will try to + /// drop the old value and move the new value into place, even after + /// asserting `self.is_none()`. + pub fn init_with(&mut self, init_fn: impl FnOnce() -> Self) { + assert!(self.is_none()); + 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 +/// are used in this file, as well as definitions for the types they operate on. +mod raw { + use super::*; + + #[derive(Clone, Copy)] + #[repr(transparent)] + pub(super) struct Address(NonZeroUsize); + + pub(super) struct ContextScope { + entered_context: *const Context, + } + + impl ContextScope { + pub fn new(context: Local) -> Self { + unsafe { v8__Context__Enter(&*context) }; Self { - context: &mut *context, - }, + entered_context: &*context, + } + } + } + + impl Drop for ContextScope { + fn drop(&mut self) { + debug_assert!(!self.entered_context.is_null()); + unsafe { v8__Context__Exit(self.entered_context) }; + } + } + + #[repr(C)] + pub(super) struct HandleScope([usize; 3]); + + impl HandleScope { + /// This function is marked unsafe because the caller must ensure that the + /// returned value isn't dropped before `init()` has been called. + pub unsafe fn uninit() -> Self { + // This is safe because there is no combination of bits that would produce + // an invalid `[usize; 3]`. + #[allow(clippy::uninit_assumed_init)] + Self(MaybeUninit::uninit().assume_init()) + } + + /// This function is marked unsafe because `init()` must be called exactly + /// once, no more and no less, after creating a `HandleScope` value with + /// `HandleScope::uninit()`. + pub unsafe fn init(&mut self, isolate: NonNull) { + let buf = NonNull::from(self).cast(); + v8__HandleScope__CONSTRUCT(buf.as_ptr(), isolate.as_ptr()); + } + } + + impl Drop for HandleScope { + fn drop(&mut self) { + unsafe { v8__HandleScope__DESTRUCT(self) }; + } + } + + #[repr(transparent)] + pub(super) struct EscapeSlot(NonNull); + + impl EscapeSlot { + pub fn new(isolate: NonNull) -> Self { + unsafe { + let undefined = raw::v8__Undefined(isolate.as_ptr()) as *const _; + let local = raw::v8__Local__New(isolate.as_ptr(), undefined); + let slot_address_ptr = local as *const Address as *mut _; + let slot_address_nn = NonNull::new_unchecked(slot_address_ptr); + Self(slot_address_nn) + } + } + + pub fn escape<'e, T>(self, value: Local<'_, T>) -> Local<'e, T> + where + for<'l> Local<'l, T>: Into>, + { + assert_eq!(Layout::new::(), Layout::new::>()); + unsafe { + let undefined = Local::::from_non_null(self.0.cast()); + debug_assert!(undefined.is_undefined()); + let value_address = *(&*value as *const T as *const Address); + ptr::write(self.0.as_ptr(), value_address); + Local::from_non_null(self.0.cast()) + } + } + } + + extern "C" { + pub(super) fn v8__Isolate__GetCurrentContext( + isolate: *mut Isolate, + ) -> *const Context; + pub(super) fn v8__Isolate__GetEnteredOrMicrotaskContext( + isolate: *mut Isolate, + ) -> *const Context; + pub(super) fn v8__Isolate__ThrowException( + isolate: *mut Isolate, + exception: *const Value, + ) -> *const Value; + + pub(super) fn v8__Context__EQ( + this: *const Context, + other: *const Context, + ) -> bool; + pub(super) fn v8__Context__Enter(this: *const Context); + pub(super) fn v8__Context__Exit(this: *const Context); + pub(super) fn v8__Context__GetIsolate(this: *const Context) + -> *mut Isolate; + + pub(super) fn v8__HandleScope__CONSTRUCT( + buf: *mut MaybeUninit, + isolate: *mut Isolate, ); + pub(super) fn v8__HandleScope__DESTRUCT(this: *mut HandleScope); + + pub(super) fn v8__Local__New( + isolate: *mut Isolate, + other: *const Data, + ) -> *const Data; + pub(super) fn v8__Undefined(isolate: *mut Isolate) -> *const Primitive; + + pub(super) fn v8__Message__GetIsolate(this: *const Message) + -> *mut Isolate; + pub(super) fn v8__Object__GetIsolate(this: *const Object) -> *mut Isolate; + pub(super) fn v8__FunctionCallbackInfo__GetIsolate( + this: *const FunctionCallbackInfo, + ) -> *mut Isolate; + pub(super) fn v8__PropertyCallbackInfo__GetIsolate( + this: *const PropertyCallbackInfo, + ) -> *mut Isolate; } } -impl Drop for ContextScope { - fn drop(&mut self) { - unsafe { self.get_captured_context() }.exit() +#[cfg(test)] +mod tests { + use super::*; + use crate::new_default_platform; + use crate::V8; + use std::any::type_name; + use std::sync::Once; + + trait SameType {} + impl SameType for (A, A) {} + + /// `AssertTypeOf` facilitates comparing types. The important difference with + /// assigning a value to a variable with an explicitly stated type is that the + /// latter allows coercions and dereferencing to change the type, whereas + /// `AssertTypeOf` requires the compared types to match exactly. + struct AssertTypeOf<'a, T>(pub &'a T); + impl<'a, T> AssertTypeOf<'a, T> { + pub fn is(self) + where + (A, T): SameType, + { + assert_eq!(type_name::(), type_name::()); + } + } + + fn initialize_v8() { + static INIT: Once = Once::new(); + INIT.call_once(|| { + V8::initialize_platform(new_default_platform().unwrap()); + V8::initialize(); + }); + } + + #[test] + fn deref_types() { + initialize_v8(); + let isolate = &mut Isolate::new(Default::default()); + AssertTypeOf(isolate).is::(); + let l1_hs = &mut HandleScope::new(isolate); + AssertTypeOf(l1_hs).is::>(); + let context = Context::new(l1_hs); + { + let l2_cxs = &mut ContextScope::new(l1_hs, context); + AssertTypeOf(l2_cxs).is::>(); + { + let d = l2_cxs.deref_mut(); + AssertTypeOf(d).is::(); + let d = d.deref_mut(); + AssertTypeOf(d).is::>(); + let d = d.deref_mut(); + AssertTypeOf(d).is::(); + } + { + let l3_ehs = &mut EscapableHandleScope::new(l2_cxs); + AssertTypeOf(l3_ehs).is::(); + let l4_cxs = &mut ContextScope::new(l3_ehs, context); + AssertTypeOf(l4_cxs).is::>(); + let d = l4_cxs.deref_mut(); + AssertTypeOf(d).is::(); + let d = d.deref_mut(); + AssertTypeOf(d).is::(); + let d = d.deref_mut(); + AssertTypeOf(d).is::>(); + let d = d.deref_mut(); + AssertTypeOf(d).is::(); + } + } + { + let l2_ehs = &mut EscapableHandleScope::new(l1_hs); + AssertTypeOf(l2_ehs).is::>(); + let d = l2_ehs.deref_mut(); + AssertTypeOf(d).is::>(); + let d = d.deref_mut(); + AssertTypeOf(d).is::(); + } + { + // `CallbackScope` is meant to be used inside V8 API callback functions + // only. It assumes that a `HandleScope` already exists on the stack, and + // that a context has been entered. Push a `ContextScope` onto the stack + // to also meet the second expectation. + let _ = ContextScope::new(l1_hs, context); + let l2_cbs = &mut unsafe { CallbackScope::new(context) }; + AssertTypeOf(l2_cbs).is::(); + let d = l2_cbs.deref_mut(); + AssertTypeOf(d).is::(); + let d = d.deref_mut(); + AssertTypeOf(d).is::>(); + let d = d.deref_mut(); + AssertTypeOf(d).is::(); + } + } + + #[test] + fn new_scope_types() { + initialize_v8(); + let isolate = &mut Isolate::new(Default::default()); + AssertTypeOf(isolate).is::(); + let l1_hs = &mut HandleScope::new(isolate); + AssertTypeOf(l1_hs).is::>(); + let context = Context::new(l1_hs); + AssertTypeOf(&HandleScope::new(l1_hs)).is::>(); + { + let l2_cxs = &mut ContextScope::new(l1_hs, context); + AssertTypeOf(l2_cxs).is::>(); + AssertTypeOf(&ContextScope::new(l2_cxs, context)) + .is::>(); + AssertTypeOf(&HandleScope::new(l2_cxs)).is::(); + AssertTypeOf(&EscapableHandleScope::new(l2_cxs)) + .is::(); + } + { + let l2_ehs = &mut EscapableHandleScope::new(l1_hs); + AssertTypeOf(l2_ehs).is::>(); + AssertTypeOf(&HandleScope::new(l2_ehs)).is::>(); + AssertTypeOf(&EscapableHandleScope::new(l2_ehs)) + .is::>(); + { + let l3_cxs = &mut ContextScope::new(l2_ehs, context); + AssertTypeOf(l3_cxs).is::>(); + AssertTypeOf(&ContextScope::new(l3_cxs, context)) + .is::>(); + AssertTypeOf(&HandleScope::new(l3_cxs)).is::(); + AssertTypeOf(&EscapableHandleScope::new(l3_cxs)) + .is::(); + } + } + { + let l2_cbs = &mut unsafe { CallbackScope::new(context) }; + AssertTypeOf(l2_cbs).is::(); + AssertTypeOf(&ContextScope::new(l2_cbs, context)) + .is::>(); + { + let l3_hs = &mut HandleScope::new(l2_cbs); + AssertTypeOf(l3_hs).is::(); + AssertTypeOf(&ContextScope::new(l3_hs, context)) + .is::>(); + AssertTypeOf(&HandleScope::new(l3_hs)).is::(); + AssertTypeOf(&EscapableHandleScope::new(l3_hs)) + .is::(); + } + { + let l3_ehs = &mut EscapableHandleScope::new(l2_cbs); + AssertTypeOf(l3_ehs).is::(); + AssertTypeOf(&ContextScope::new(l3_ehs, context)) + .is::>(); + AssertTypeOf(&HandleScope::new(l3_ehs)).is::(); + AssertTypeOf(&EscapableHandleScope::new(l3_ehs)) + .is::(); + } + } } } - -pub type FunctionCallbackScope<'s> = &'s mut Entered<'s, FunctionCallbackInfo>; -pub type PropertyCallbackScope<'s> = &'s mut Entered<'s, PropertyCallbackInfo>; diff --git a/src/scope_traits.rs b/src/scope_traits.rs deleted file mode 100644 index cfb51282..00000000 --- a/src/scope_traits.rs +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. - -use crate::scope::Entered; -use crate::scope::Escapable; -use crate::CallbackScope; -use crate::Context; -use crate::ContextScope; -use crate::EscapableHandleScope; -use crate::FunctionCallbackInfo; -use crate::HandleScope; -use crate::Isolate; -use crate::Local; -use crate::Message; -use crate::Object; -use crate::PropertyCallbackInfo; - -pub(crate) mod internal { - use super::*; - - extern "C" { - fn v8__Context__GetIsolate(this: *const Context) -> *mut Isolate; - fn v8__EscapableHandleScope__GetIsolate( - this: &EscapableHandleScope, - ) -> *mut Isolate; - fn v8__FunctionCallbackInfo__GetIsolate( - this: &FunctionCallbackInfo, - ) -> *mut Isolate; - fn v8__HandleScope__GetIsolate(this: &HandleScope) -> *mut Isolate; - fn v8__Message__GetIsolate(this: *const Message) -> *mut Isolate; - fn v8__Object__GetIsolate(this: *const Object) -> *mut Isolate; - fn v8__PropertyCallbackInfo__GetIsolate( - this: &PropertyCallbackInfo, - ) -> *mut Isolate; - } - - /// Internal trait for retrieving a raw Isolate pointer from various V8 - /// API objects. - pub trait GetRawIsolate { - fn get_raw_isolate(&self) -> *mut Isolate; - } - - impl<'s, T> GetRawIsolate for Local<'s, T> - where - Local<'s, Object>: From>, - { - fn get_raw_isolate(&self) -> *mut Isolate { - let local = Local::<'s, Object>::from(*self); - (&*local).get_raw_isolate() - } - } - - impl<'s> GetRawIsolate for Local<'s, Context> { - fn get_raw_isolate(&self) -> *mut Isolate { - (&**self).get_raw_isolate() - } - } - - impl<'s> GetRawIsolate for Local<'s, Message> { - fn get_raw_isolate(&self) -> *mut Isolate { - (&**self).get_raw_isolate() - } - } - - impl<'s, S> GetRawIsolate for Entered<'_, S> - where - S: GetRawIsolate, - { - fn get_raw_isolate(&self) -> *mut Isolate { - self.data().get_raw_isolate() - } - } - - impl GetRawIsolate for CallbackScope { - fn get_raw_isolate(&self) -> *mut Isolate { - self.get_raw_isolate_() - } - } - - impl<'s> GetRawIsolate for ContextScope { - fn get_raw_isolate(&self) -> *mut Isolate { - unsafe { self.get_captured_context() }.get_raw_isolate() - } - } - - impl GetRawIsolate for Context { - fn get_raw_isolate(&self) -> *mut Isolate { - unsafe { v8__Context__GetIsolate(self) } - } - } - - impl GetRawIsolate for EscapableHandleScope { - fn get_raw_isolate(&self) -> *mut Isolate { - unsafe { v8__EscapableHandleScope__GetIsolate(self) } - } - } - - impl GetRawIsolate for FunctionCallbackInfo { - fn get_raw_isolate(&self) -> *mut Isolate { - unsafe { v8__FunctionCallbackInfo__GetIsolate(self) } - } - } - - impl GetRawIsolate for HandleScope { - fn get_raw_isolate(&self) -> *mut Isolate { - unsafe { v8__HandleScope__GetIsolate(self) } - } - } - - impl GetRawIsolate for Message { - fn get_raw_isolate(&self) -> *mut Isolate { - unsafe { v8__Message__GetIsolate(self) } - } - } - - impl GetRawIsolate for Object { - fn get_raw_isolate(&self) -> *mut Isolate { - unsafe { v8__Object__GetIsolate(self) } - } - } - - impl GetRawIsolate for PropertyCallbackInfo { - fn get_raw_isolate(&self) -> *mut Isolate { - unsafe { v8__PropertyCallbackInfo__GetIsolate(self) } - } - } -} - -/// 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) or on shared references *e.g. &Isolate). - fn isolate(&mut self) -> &mut Isolate; -} - -impl<'s, S, P> InIsolate for Entered<'s, S, P> -where - S: internal::GetRawIsolate, -{ - fn isolate(&mut self) -> &mut Isolate { - unsafe { &mut *(self.data().get_raw_isolate()) } - } -} - -extern "C" { - fn v8__Isolate__GetCurrentContext(isolate: *mut Isolate) -> *const Context; - fn v8__Isolate__GetEnteredOrMicrotaskContext( - isolate: *mut Isolate, - ) -> *const Context; -} - -/// When scope implements this trait, this means that Local handles can be -/// created inside it. -pub trait ToLocal<'s>: InIsolate { - unsafe fn cast_local *const T>( - &mut self, - f: F, - ) -> Option> { - Local::from_raw(f(self)) - } - - fn get_current_context(&mut self) -> Option> { - unsafe { - self.cast_local(|scope| v8__Isolate__GetCurrentContext(scope.isolate())) - } - } - - fn get_entered_or_microtask_context(&mut self) -> Option> { - unsafe { - self.cast_local(|scope| { - v8__Isolate__GetEnteredOrMicrotaskContext(scope.isolate()) - }) - } - } -} - -impl<'s> ToLocal<'s> for Entered<'s, FunctionCallbackInfo> {} -impl<'s> ToLocal<'s> for Entered<'s, PropertyCallbackInfo> {} -impl<'s, P> ToLocal<'s> for Entered<'s, HandleScope, P> {} -impl<'s, P> ToLocal<'s> for Entered<'s, EscapableHandleScope, P> {} -impl<'s, 'p: 's, P> ToLocal<'p> for Entered<'s, ContextScope, P> where - P: ToLocal<'p> -{ -} - -pub trait ToLocalOrReturnsLocal<'s>: InIsolate {} -impl<'s, E> ToLocalOrReturnsLocal<'s> for E where E: ToLocal<'s> {} -impl<'s, 'p: 's> ToLocalOrReturnsLocal<'p> - for Entered<'s, CallbackScope> -{ -} - -pub trait EscapeLocal<'s, 'p: 's>: ToLocal<'s> { - fn escape(&mut self, local: Local) -> Local<'p, T>; -} - -impl<'s, 'p: 's, P> EscapeLocal<'s, 'p> for Entered<'s, EscapableHandleScope, P> -where - P: ToLocalOrReturnsLocal<'p>, -{ - fn escape(&mut self, local: Local) -> Local<'p, T> { - unsafe { self.data_mut().escape(local) } - } -} - -impl<'s, 'p: 's, P> EscapeLocal<'s, 'p> for Entered<'s, ContextScope, P> -where - P: EscapeLocal<'s, 'p>, -{ - fn escape(&mut self, local: Local) -> Local<'p, T> { - self.parent_mut().escape(local) - } -} - -impl<'s, 'p: 's, P> EscapeLocal<'s, 'p> for Entered<'s, HandleScope, P> -where - P: EscapeLocal<'s, 'p>, -{ - fn escape(&mut self, local: Local) -> Local<'p, T> { - self.parent_mut().escape(local) - } -} - -// TODO(piscisaureus): move the impls for Entered to a more sensible spot. - -impl<'s, S, P> Entered<'s, S, P> -where - Self: InIsolate, -{ - pub fn isolate(&mut self) -> &mut Isolate { - ::isolate(self) - } -} - -impl<'s, 'p: 's, S, P> Entered<'s, S, P> -where - Self: ToLocal<'p>, -{ - /// Returns the context of the currently running JavaScript, or the context - /// on the top of the stack if no JavaScript is running. - pub fn get_current_context(&mut self) -> Option> { - >::get_current_context(self) - } - - /// Returns either the last context entered through V8's C++ API, or the - /// context of the currently running microtask while processing microtasks. - /// If a context is entered while executing a microtask, that context is - /// returned. - pub fn get_entered_or_microtask_context( - &mut self, - ) -> Option> { - >::get_entered_or_microtask_context(self) - } -} - -impl<'s, 'p: 's, S, P> Entered<'s, S, P> -where - Self: EscapeLocal<'s, 'p>, -{ - /// Pushes the value into the previous scope and returns a handle to it. - /// Cannot be called twice. - pub fn escape(&mut self, local: Local) -> Local<'p, T> { - >::escape(self, local) - } -} diff --git a/src/script.rs b/src/script.rs index 3504ad61..8e71249d 100644 --- a/src/script.rs +++ b/src/script.rs @@ -4,16 +4,16 @@ use std::ptr::null; use crate::Boolean; use crate::Context; +use crate::HandleScope; use crate::Integer; use crate::Local; use crate::Script; use crate::String; -use crate::ToLocal; use crate::Value; /// The origin, within a file, of a script. #[repr(C)] -pub struct ScriptOrigin<'sc>([usize; 7], PhantomData<&'sc ()>); +pub struct ScriptOrigin<'s>([usize; 7], PhantomData<&'s ()>); extern "C" { fn v8__Script__Compile( @@ -42,18 +42,18 @@ extern "C" { impl Script { /// A shorthand for ScriptCompiler::Compile(). - pub fn compile<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn compile<'s>( + scope: &mut HandleScope<'s>, context: Local, source: Local, origin: Option<&ScriptOrigin>, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| { v8__Script__Compile( &*context, &*source, - origin.map(|r| r as *const _).unwrap_or(null()), + origin.map(|r| r as *const _).unwrap_or_else(null), ) }) } @@ -62,28 +62,28 @@ impl Script { /// Runs the script returning the resulting value. It will be run in the /// context in which it was created (ScriptCompiler::CompileBound or /// UnboundScript::BindToCurrentContext()). - pub fn run<'sc>( + pub fn run<'s>( &mut self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Script__Run(self, &*context)) } } } /// The origin, within a file, of a script. -impl<'sc> ScriptOrigin<'sc> { +impl<'s> ScriptOrigin<'s> { #[allow(clippy::too_many_arguments)] pub fn new( - resource_name: Local<'sc, Value>, - resource_line_offset: Local<'sc, Integer>, - resource_column_offset: Local<'sc, Integer>, - resource_is_shared_cross_origin: Local<'sc, Boolean>, - script_id: Local<'sc, Integer>, - source_map_url: Local<'sc, Value>, - resource_is_opaque: Local<'sc, Boolean>, - is_wasm: Local<'sc, Boolean>, - is_module: Local<'sc, Boolean>, + resource_name: Local<'s, Value>, + resource_line_offset: Local<'s, Integer>, + resource_column_offset: Local<'s, Integer>, + resource_is_shared_cross_origin: Local<'s, Boolean>, + script_id: Local<'s, Integer>, + source_map_url: Local<'s, Value>, + resource_is_opaque: Local<'s, Boolean>, + is_wasm: Local<'s, Boolean>, + is_module: Local<'s, Boolean>, ) -> Self { unsafe { let mut buf = std::mem::MaybeUninit::::uninit(); diff --git a/src/script_compiler.rs b/src/script_compiler.rs index 6d1b5194..e2b9569c 100644 --- a/src/script_compiler.rs +++ b/src/script_compiler.rs @@ -1,12 +1,12 @@ // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. use std::mem::MaybeUninit; +use crate::HandleScope; use crate::Isolate; use crate::Local; use crate::Module; use crate::ScriptOrigin; use crate::String; -use crate::ToLocal; extern "C" { fn v8__ScriptCompiler__Source__CONSTRUCT( @@ -77,10 +77,10 @@ pub enum NoCacheReason { /// /// Corresponds to the ParseModule abstract operation in the ECMAScript /// specification. -pub fn compile_module<'sc>( - scope: &mut impl ToLocal<'sc>, +pub fn compile_module<'s>( + scope: &mut HandleScope<'s>, source: Source, -) -> Option> { +) -> Option> { compile_module2( scope, source, @@ -90,16 +90,16 @@ pub fn compile_module<'sc>( } /// Same as compile_module with more options. -pub fn compile_module2<'sc>( - scope: &mut impl ToLocal<'sc>, +pub fn compile_module2<'s>( + scope: &mut HandleScope<'s>, mut source: Source, options: CompileOptions, no_cache_reason: NoCacheReason, -) -> Option> { +) -> Option> { unsafe { - scope.cast_local(|scope| { + scope.cast_local(|sd| { v8__ScriptCompiler__CompileModule( - scope.isolate(), + sd.get_isolate_ptr(), &mut source, options, no_cache_reason, diff --git a/src/shared_array_buffer.rs b/src/shared_array_buffer.rs index 5b2b75b5..7dd3bffd 100644 --- a/src/shared_array_buffer.rs +++ b/src/shared_array_buffer.rs @@ -8,11 +8,10 @@ use crate::support::SharedRef; use crate::support::UniqueRef; use crate::BackingStore; use crate::BackingStoreDeleterCallback; -use crate::InIsolate; +use crate::HandleScope; use crate::Isolate; use crate::Local; use crate::SharedArrayBuffer; -use crate::ToLocal; extern "C" { fn v8__SharedArrayBuffer__New__with_byte_length( @@ -45,28 +44,28 @@ impl SharedArrayBuffer { /// Allocated memory will be owned by a created SharedArrayBuffer and /// will be deallocated when it is garbage-collected, /// unless the object is externalized. - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s>, byte_length: usize, - ) -> Option> { + ) -> Option> { unsafe { - scope.cast_local(|scope| { + scope.cast_local(|sd| { v8__SharedArrayBuffer__New__with_byte_length( - scope.isolate(), + sd.get_isolate_ptr(), byte_length, ) }) } } - pub fn with_backing_store<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn with_backing_store<'s>( + scope: &mut HandleScope<'s>, backing_store: &SharedRef, - ) -> Local<'sc, SharedArrayBuffer> { + ) -> Local<'s, SharedArrayBuffer> { unsafe { - scope.cast_local(|scope| { + scope.cast_local(|sd| { v8__SharedArrayBuffer__New__with_backing_store( - scope.isolate(), + sd.get_isolate_ptr(), backing_store, ) }) @@ -95,13 +94,13 @@ impl SharedArrayBuffer { /// 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( - scope: &mut impl InIsolate, + scope: &mut Isolate, byte_length: usize, ) -> UniqueRef { unsafe { UniqueRef::from_raw( v8__SharedArrayBuffer__NewBackingStore__with_byte_length( - scope.isolate(), + scope, byte_length, ), ) diff --git a/src/snapshot.rs b/src/snapshot.rs index 57f5b411..b8613d8b 100644 --- a/src/snapshot.rs +++ b/src/snapshot.rs @@ -1,4 +1,5 @@ use crate::external_references::ExternalReferences; +use crate::scope::data::ScopeData; use crate::support::char; use crate::support::int; use crate::support::intptr_t; @@ -105,7 +106,7 @@ impl SnapshotCreator { /// Set the default context to be included in the snapshot blob. /// The snapshot will not contain the global proxy, and we expect one or a /// global object template to create one, to be provided upon deserialization. - pub fn set_default_context<'sc>(&mut self, context: Local<'sc, Context>) { + pub fn set_default_context<'s>(&mut self, context: Local<'s, Context>) { unsafe { v8__SnapshotCreator__SetDefaultContext(self, &*context) }; } @@ -115,6 +116,10 @@ impl SnapshotCreator { &mut self, function_code_handling: FunctionCodeHandling, ) -> Option { + { + let isolate = unsafe { &mut *v8__SnapshotCreator__GetIsolate(self) }; + ScopeData::get_root_mut(isolate); + } let blob = unsafe { v8__SnapshotCreator__CreateBlob(self, function_code_handling) }; if blob.data.is_null() { @@ -134,6 +139,7 @@ impl SnapshotCreator { pub unsafe fn get_owned_isolate(&mut self) -> OwnedIsolate { let isolate_ptr = v8__SnapshotCreator__GetIsolate(self); let mut owned_isolate = OwnedIsolate::new(isolate_ptr); + ScopeData::new_root(&mut owned_isolate); owned_isolate.create_annex(Box::new(())); owned_isolate } diff --git a/src/string.rs b/src/string.rs index 230762a4..0e48e6ec 100644 --- a/src/string.rs +++ b/src/string.rs @@ -5,11 +5,10 @@ use std::slice; use crate::support::char; use crate::support::int; -use crate::InIsolate; +use crate::HandleScope; use crate::Isolate; use crate::Local; use crate::String; -use crate::ToLocal; extern "C" { fn v8__String__Empty(isolate: *mut Isolate) -> *const String; @@ -63,26 +62,26 @@ bitflags! { } impl String { - pub fn empty<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, String> { + pub fn empty<'s>(scope: &mut HandleScope<'s, ()>) -> Local<'s, String> { // FIXME(bnoordhuis) v8__String__Empty() is infallible so there // is no need to box up the result, only to unwrap it again. - unsafe { scope.cast_local(|scope| v8__String__Empty(scope.isolate())) } + unsafe { scope.cast_local(|sd| v8__String__Empty(sd.get_isolate_ptr())) } .unwrap() } - pub fn new_from_utf8<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new_from_utf8<'s>( + scope: &mut HandleScope<'s, ()>, buffer: &[u8], new_type: NewStringType, - ) -> Option> { + ) -> Option> { if buffer.is_empty() { return Some(Self::empty(scope)); } let buffer_len = buffer.len().try_into().ok()?; unsafe { - scope.cast_local(|scope| { + scope.cast_local(|sd| { v8__String__NewFromUtf8( - scope.isolate(), + sd.get_isolate_ptr(), buffer.as_ptr() as *const char, new_type, buffer_len, @@ -98,13 +97,13 @@ impl String { /// Returns the number of bytes in the UTF-8 encoded representation of this /// string. - pub fn utf8_length(&self, scope: &mut impl InIsolate) -> usize { - unsafe { v8__String__Utf8Length(self, scope.isolate()) as usize } + pub fn utf8_length(&self, scope: &mut Isolate) -> usize { + unsafe { v8__String__Utf8Length(self, scope) as usize } } pub fn write_utf8( &self, - scope: &mut impl InIsolate, + scope: &mut Isolate, buffer: &mut [u8], nchars_ref: Option<&mut usize>, options: WriteOptions, @@ -113,7 +112,7 @@ impl String { let bytes = unsafe { v8__String__WriteUtf8( self, - scope.isolate(), + scope, buffer.as_mut_ptr() as *mut char, buffer.len().try_into().unwrap_or(int::max_value()), &mut nchars_ref_int, @@ -127,17 +126,17 @@ impl String { } // Convenience function not present in the original V8 API. - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s, ()>, value: &str, - ) -> Option> { + ) -> Option> { Self::new_from_utf8(scope, value.as_ref(), NewStringType::Normal) } // Convenience function not present in the original V8 API. pub fn to_rust_string_lossy( &self, - scope: &mut impl InIsolate, + scope: &mut Isolate, ) -> std::string::String { let capacity = self.utf8_length(scope); let mut string = std::string::String::with_capacity(capacity); diff --git a/src/template.rs b/src/template.rs index bccc8a5f..42e28c4a 100644 --- a/src/template.rs +++ b/src/template.rs @@ -8,11 +8,11 @@ use crate::support::MapFnTo; use crate::Context; use crate::Function; use crate::FunctionCallback; +use crate::HandleScope; use crate::Local; use crate::Object; use crate::PropertyAttribute; use crate::String; -use crate::ToLocal; use crate::NONE; extern "C" { @@ -66,24 +66,24 @@ impl Template { impl FunctionTemplate { /// Creates a function template. - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s, ()>, callback: impl MapFnTo, - ) -> Local<'sc, FunctionTemplate> { + ) -> Local<'s, FunctionTemplate> { unsafe { - scope.cast_local(|scope| { - v8__FunctionTemplate__New(scope.isolate(), callback.map_fn_to()) + scope.cast_local(|sd| { + v8__FunctionTemplate__New(sd.get_isolate_ptr(), callback.map_fn_to()) }) } .unwrap() } /// Returns the unique function instance in the current execution context. - pub fn get_function<'sc>( + pub fn get_function<'s>( &mut self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s, ()>, context: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__FunctionTemplate__GetFunction(&*self, &*context)) } @@ -99,33 +99,33 @@ impl FunctionTemplate { impl ObjectTemplate { /// Creates an object template. - pub fn new<'sc>(scope: &mut impl ToLocal<'sc>) -> Local<'sc, ObjectTemplate> { + pub fn new<'s>(scope: &mut HandleScope<'s, ()>) -> Local<'s, ObjectTemplate> { unsafe { - scope.cast_local(|scope| { - v8__ObjectTemplate__New(scope.isolate(), std::ptr::null()) + scope.cast_local(|sd| { + v8__ObjectTemplate__New(sd.get_isolate_ptr(), std::ptr::null()) }) } .unwrap() } /// Creates an object template from a function template. - pub fn new_from_template<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new_from_template<'s>( + scope: &mut HandleScope<'s, ()>, templ: Local, - ) -> Local<'sc, ObjectTemplate> { + ) -> Local<'s, ObjectTemplate> { unsafe { scope - .cast_local(|scope| v8__ObjectTemplate__New(scope.isolate(), &*templ)) + .cast_local(|sd| v8__ObjectTemplate__New(sd.get_isolate_ptr(), &*templ)) } .unwrap() } /// Creates a new instance of this object template. - pub fn new_instance<'a>( + pub fn new_instance<'s>( &self, - scope: &mut impl ToLocal<'a>, + scope: &mut HandleScope<'s>, context: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__ObjectTemplate__NewInstance(self, &*context)) } diff --git a/src/try_catch.rs b/src/try_catch.rs index f33345be..5aaec222 100644 --- a/src/try_catch.rs +++ b/src/try_catch.rs @@ -5,11 +5,10 @@ use std::mem::take; use std::mem::MaybeUninit; use crate::Context; -use crate::InIsolate; +use crate::HandleScope; use crate::Isolate; use crate::Local; use crate::Message; -use crate::ToLocal; use crate::Value; extern "C" { @@ -72,10 +71,8 @@ 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 InIsolate) -> TryCatchScope<'tc> { - TryCatchScope(TryCatchState::New { - isolate: scope.isolate(), - }) + pub fn new(scope: &mut Isolate) -> TryCatchScope<'tc> { + TryCatchScope(TryCatchState::New { isolate: scope }) } /// Returns true if an exception has been caught by this try/catch block. @@ -115,10 +112,10 @@ impl<'tc> TryCatch<'tc> { /// no longer and no shorter than the active HandleScope at the time this /// method is called. An issue has been opened about this in the V8 bug /// tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. - pub fn exception<'sc>( + pub fn exception<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { + scope: &mut HandleScope<'s>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__TryCatch__Exception(&self.0)) } } @@ -127,20 +124,20 @@ impl<'tc> TryCatch<'tc> { /// /// Note: the remark about the lifetime for the `exception()` return value /// applies here too. - pub fn message<'sc>( + pub fn message<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { + scope: &mut HandleScope<'s>, + ) -> Option> { unsafe { scope.cast_local(|_| v8__TryCatch__Message(&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>( + pub fn stack_trace<'s>( &self, - scope: &mut impl ToLocal<'sc>, + scope: &mut HandleScope<'s>, context: Local, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__TryCatch__StackTrace(&self.0, &*context)) } diff --git a/src/uint8_array.rs b/src/uint8_array.rs index ceebe97f..d633e33b 100644 --- a/src/uint8_array.rs +++ b/src/uint8_array.rs @@ -1,7 +1,7 @@ // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. use crate::ArrayBuffer; +use crate::HandleScope; use crate::Local; -use crate::ToLocal; use crate::Uint8Array; extern "C" { @@ -13,12 +13,12 @@ extern "C" { } impl Uint8Array { - pub fn new<'sc>( - scope: &mut impl ToLocal<'sc>, + pub fn new<'s>( + scope: &mut HandleScope<'s>, buf: Local, byte_offset: usize, length: usize, - ) -> Option> { + ) -> Option> { unsafe { scope.cast_local(|_| v8__Uint8Array__New(&*buf, byte_offset, length)) } diff --git a/src/value.rs b/src/value.rs index 4736a901..321be98b 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,13 +1,13 @@ use crate::support::Maybe; use crate::BigInt; use crate::Context; +use crate::HandleScope; use crate::Int32; use crate::Integer; use crate::Local; use crate::Number; use crate::Object; use crate::String; -use crate::ToLocal; use crate::Uint32; use crate::Value; @@ -411,124 +411,124 @@ impl Value { unsafe { v8__Value__IsModuleNamespaceObject(self) } } - pub fn strict_equals<'sc>(&self, that: Local<'sc, Value>) -> bool { + pub fn strict_equals<'s>(&self, that: Local<'s, Value>) -> bool { unsafe { v8__Value__StrictEquals(self, &*that) } } - pub fn same_value<'sc>(&self, that: Local<'sc, Value>) -> bool { + pub fn same_value<'s>(&self, that: Local<'s, Value>) -> bool { unsafe { v8__Value__SameValue(self, &*that) } } - pub fn to_big_int<'sc>( + pub fn to_big_int<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - scope.get_current_context().and_then(|context| unsafe { - scope.cast_local(|_| v8__Value__ToBigInt(self, &*context)) - }) + scope: &mut HandleScope<'s>, + ) -> Option> { + unsafe { + scope + .cast_local(|sd| v8__Value__ToBigInt(self, &*sd.get_current_context())) + } } - pub fn to_number<'sc>( + pub fn to_number<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - scope.get_current_context().and_then(|context| unsafe { - scope.cast_local(|_| v8__Value__ToNumber(self, &*context)) - }) + scope: &mut HandleScope<'s>, + ) -> Option> { + unsafe { + scope + .cast_local(|sd| v8__Value__ToNumber(self, &*sd.get_current_context())) + } } - pub fn to_string<'sc>( + pub fn to_string<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - scope.get_current_context().and_then(|context| unsafe { - scope.cast_local(|_| v8__Value__ToString(self, &*context)) - }) + scope: &mut HandleScope<'s>, + ) -> Option> { + unsafe { + scope + .cast_local(|sd| v8__Value__ToString(self, &*sd.get_current_context())) + } } - pub fn to_detail_string<'sc>( + pub fn to_detail_string<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - scope.get_current_context().and_then(|context| unsafe { - scope.cast_local(|_| v8__Value__ToDetailString(self, &*context)) - }) + scope: &mut HandleScope<'s>, + ) -> Option> { + unsafe { + scope.cast_local(|sd| { + v8__Value__ToDetailString(self, &*sd.get_current_context()) + }) + } } - pub fn to_object<'sc>( + pub fn to_object<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - scope.get_current_context().and_then(|context| unsafe { - scope.cast_local(|_| v8__Value__ToObject(self, &*context)) - }) + scope: &mut HandleScope<'s>, + ) -> Option> { + unsafe { + scope + .cast_local(|sd| v8__Value__ToObject(self, &*sd.get_current_context())) + } } - pub fn to_integer<'sc>( + pub fn to_integer<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - scope.get_current_context().and_then(|context| unsafe { - scope.cast_local(|_| v8__Value__ToInteger(self, &*context)) - }) + scope: &mut HandleScope<'s>, + ) -> Option> { + unsafe { + scope + .cast_local(|sd| v8__Value__ToInteger(self, &*sd.get_current_context())) + } } - pub fn to_uint32<'sc>( + pub fn to_uint32<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - scope.get_current_context().and_then(|context| unsafe { - scope.cast_local(|_| v8__Value__ToUint32(self, &*context)) - }) + scope: &mut HandleScope<'s>, + ) -> Option> { + unsafe { + scope + .cast_local(|sd| v8__Value__ToUint32(self, &*sd.get_current_context())) + } } - pub fn to_int32<'sc>( + pub fn to_int32<'s>( &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option> { - scope.get_current_context().and_then(|context| unsafe { - scope.cast_local(|_| v8__Value__ToInt32(self, &*context)) - }) + scope: &mut HandleScope<'s>, + ) -> Option> { + unsafe { + scope + .cast_local(|sd| v8__Value__ToInt32(self, &*sd.get_current_context())) + } } - pub fn number_value<'sc>( - &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option { - scope.get_current_context().and_then(|context| unsafe { - let mut out = Maybe::::default(); - v8__Value__NumberValue(self, &*context, &mut out); - out.into() - }) + pub fn number_value<'s>(&self, scope: &mut HandleScope<'s>) -> Option { + let mut out = Maybe::::default(); + unsafe { + v8__Value__NumberValue(self, &*scope.get_current_context(), &mut out) + }; + out.into() } - pub fn integer_value<'sc>( - &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option { - scope.get_current_context().and_then(|context| unsafe { - let mut out = Maybe::::default(); - v8__Value__IntegerValue(self, &*context, &mut out); - out.into() - }) + pub fn integer_value<'s>(&self, scope: &mut HandleScope<'s>) -> Option { + let mut out = Maybe::::default(); + unsafe { + v8__Value__IntegerValue(self, &*scope.get_current_context(), &mut out) + }; + out.into() } - pub fn uint32_value<'sc>( - &self, - scope: &mut impl ToLocal<'sc>, - ) -> Option { - scope.get_current_context().and_then(|context| unsafe { - let mut out = Maybe::::default(); - v8__Value__Uint32Value(self, &*context, &mut out); - out.into() - }) + pub fn uint32_value<'s>(&self, scope: &mut HandleScope<'s>) -> Option { + let mut out = Maybe::::default(); + unsafe { + v8__Value__Uint32Value(self, &*scope.get_current_context(), &mut out) + }; + out.into() } - pub fn int32_value<'sc>(&self, scope: &mut impl ToLocal<'sc>) -> Option { - scope.get_current_context().and_then(|context| unsafe { - let mut out = Maybe::::default(); - v8__Value__Int32Value(self, &*context, &mut out); - out.into() - }) + pub fn int32_value<'s>(&self, scope: &mut HandleScope<'s>) -> Option { + let mut out = Maybe::::default(); + unsafe { + v8__Value__Int32Value(self, &*scope.get_current_context(), &mut out) + }; + out.into() } } diff --git a/tests/compile_fail/boxed_local.rs b/tests/compile_fail/boxed_local.rs index f5cd7d53..c8cd762e 100644 --- a/tests/compile_fail/boxed_local.rs +++ b/tests/compile_fail/boxed_local.rs @@ -3,11 +3,12 @@ use rusty_v8 as v8; pub fn main() { let mut isolate = v8::Isolate::new(mock()); + let mut scope1 = v8::HandleScope::new(&mut isolate); let _boxed_local = { - let mut hs = v8::HandleScope::new(&mut isolate); - let hs = hs.enter(); - Box::new(v8::Integer::new(hs, 123)) + let mut scope2 = v8::HandleScope::new(&mut scope1); + let mut scope3 = v8::HandleScope::new(&mut scope2); + Box::new(v8::Integer::new(&mut scope3, 123)) }; } diff --git a/tests/compile_fail/boxed_local.stderr b/tests/compile_fail/boxed_local.stderr index 1669b5c2..69087649 100644 --- a/tests/compile_fail/boxed_local.stderr +++ b/tests/compile_fail/boxed_local.stderr @@ -1,11 +1,11 @@ -error[E0597]: `hs` does not live long enough - --> $DIR/boxed_local.rs:9:14 +error[E0597]: `scope2` does not live long enough + --> $DIR/boxed_local.rs:10:43 | -7 | let _boxed_local = { +8 | let _boxed_local = { | ------------ borrow later stored here -8 | let mut hs = v8::HandleScope::new(&mut isolate); -9 | let hs = hs.enter(); - | ^^ borrowed value does not live long enough -10 | Box::new(v8::Integer::new(hs, 123)) -11 | }; - | - `hs` dropped here while still borrowed +9 | let mut scope2 = v8::HandleScope::new(&mut scope1); +10 | let mut scope3 = v8::HandleScope::new(&mut scope2); + | ^^^^^^^^^^^ borrowed value does not live long enough +11 | Box::new(v8::Integer::new(&mut scope3, 123)) +12 | }; + | - `scope2` dropped here while still borrowed diff --git a/tests/compile_fail/handle_scope_escape_lifetime.rs b/tests/compile_fail/handle_scope_escape_lifetime.rs index dce086a7..94232906 100644 --- a/tests/compile_fail/handle_scope_escape_lifetime.rs +++ b/tests/compile_fail/handle_scope_escape_lifetime.rs @@ -3,18 +3,14 @@ use rusty_v8 as v8; pub fn main() { let mut isolate = v8::Isolate::new(mock()); - let mut hs1 = v8::HandleScope::new(&mut isolate); - let hs1 = hs1.enter(); + let mut scope1 = v8::HandleScope::new(&mut isolate); let _local = { - let mut hs2 = v8::HandleScope::new(hs1); - let hs2 = hs2.enter(); - - let mut hs3 = v8::EscapableHandleScope::new(hs2); - let hs3 = hs3.enter(); - - let value: v8::Local = v8::Integer::new(hs3, 42).into(); - hs3.escape(value) + let mut scope2 = v8::HandleScope::new(&mut scope1); + let mut scope3 = v8::HandleScope::new(&mut scope2); + let mut scope4 = v8::EscapableHandleScope::new(&mut scope3); + let value = v8::Integer::new(&mut scope4, 42); + scope4.escape(value) }; } diff --git a/tests/compile_fail/handle_scope_escape_lifetime.stderr b/tests/compile_fail/handle_scope_escape_lifetime.stderr index bec4376d..c477e676 100644 --- a/tests/compile_fail/handle_scope_escape_lifetime.stderr +++ b/tests/compile_fail/handle_scope_escape_lifetime.stderr @@ -1,11 +1,11 @@ -error[E0597]: `hs2` does not live long enough - --> $DIR/handle_scope_escape_lifetime.rs:11:15 +error[E0597]: `scope2` does not live long enough + --> $DIR/handle_scope_escape_lifetime.rs:10:43 | -9 | let _local = { +8 | let _local = { | ------ borrow later stored here -10 | let mut hs2 = v8::HandleScope::new(hs1); -11 | let hs2 = hs2.enter(); - | ^^^ borrowed value does not live long enough +9 | let mut scope2 = v8::HandleScope::new(&mut scope1); +10 | let mut scope3 = v8::HandleScope::new(&mut scope2); + | ^^^^^^^^^^^ borrowed value does not live long enough ... -18 | }; - | - `hs2` dropped here while still borrowed +14 | }; + | - `scope2` dropped here while still borrowed diff --git a/tests/compile_fail/handle_scope_escape_to_nowhere.rs b/tests/compile_fail/handle_scope_escape_to_nowhere.rs index 3f277765..7354ea3b 100644 --- a/tests/compile_fail/handle_scope_escape_to_nowhere.rs +++ b/tests/compile_fail/handle_scope_escape_to_nowhere.rs @@ -2,9 +2,8 @@ use rusty_v8 as v8; pub fn main() { - let context: v8::Local = mock(); - let mut cs = v8::CallbackScope::new(context); - let _hs = v8::EscapableHandleScope::new(cs.enter()); + let mut isolate = v8::Isolate::new(mock()); + let mut _scope = v8::EscapableHandleScope::new(&mut isolate); } fn mock() -> T { diff --git a/tests/compile_fail/handle_scope_escape_to_nowhere.stderr b/tests/compile_fail/handle_scope_escape_to_nowhere.stderr index ebd535ac..f1cee797 100644 --- a/tests/compile_fail/handle_scope_escape_to_nowhere.stderr +++ b/tests/compile_fail/handle_scope_escape_to_nowhere.stderr @@ -1,18 +1,13 @@ -error[E0277]: the trait bound `rusty_v8::scope::Entered<'_, rusty_v8::scope::CallbackScope>: rusty_v8::scope_traits::ToLocal<'_>` is not satisfied - --> $DIR/handle_scope_escape_to_nowhere.rs:7:43 - | -7 | let _hs = v8::EscapableHandleScope::new(cs.enter()); - | ^^^^^^^^^^ the trait `rusty_v8::scope_traits::ToLocal<'_>` is not implemented for `rusty_v8::scope::Entered<'_, rusty_v8::scope::CallbackScope>` - | - ::: $WORKSPACE/src/handle_scope.rs:71:8 - | -71 | P: ToLocalOrReturnsLocal<'p>, - | ------------------------- required by this bound in `rusty_v8::handle_scope::EscapableHandleScope::new` - | - = help: the following implementations were found: - as rusty_v8::scope_traits::ToLocal<'s>> - as rusty_v8::scope_traits::ToLocal<'s>> - as rusty_v8::scope_traits::ToLocal<'s>> - as rusty_v8::scope_traits::ToLocal<'s>> - as rusty_v8::scope_traits::ToLocal<'p>> - = note: required because of the requirements on the impl of `rusty_v8::scope_traits::ToLocalOrReturnsLocal<'_>` for `rusty_v8::scope::Entered<'_, rusty_v8::scope::CallbackScope>` +error[E0277]: the trait bound `rusty_v8::isolate::OwnedIsolate: rusty_v8::scope::param::NewEscapableHandleScope<'_, '_>` is not satisfied + --> $DIR/handle_scope_escape_to_nowhere.rs:6:50 + | +6 | let mut _scope = v8::EscapableHandleScope::new(&mut isolate); + | ^^^^^^^^^^^^ the trait `rusty_v8::scope::param::NewEscapableHandleScope<'_, '_>` is not implemented for `rusty_v8::isolate::OwnedIsolate` + | + = note: required by `rusty_v8::scope::EscapableHandleScope::<'s, 'e>::new` + +error[E0277]: the trait bound `rusty_v8::isolate::OwnedIsolate: rusty_v8::scope::param::NewEscapableHandleScope<'_, '_>` is not satisfied + --> $DIR/handle_scope_escape_to_nowhere.rs:6:20 + | +6 | let mut _scope = v8::EscapableHandleScope::new(&mut isolate); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `rusty_v8::scope::param::NewEscapableHandleScope<'_, '_>` is not implemented for `rusty_v8::isolate::OwnedIsolate` diff --git a/tests/compile_fail/handle_scope_lifetime_1.rs b/tests/compile_fail/handle_scope_lifetime_1.rs index 4e2d247d..2ea6925d 100644 --- a/tests/compile_fail/handle_scope_lifetime_1.rs +++ b/tests/compile_fail/handle_scope_lifetime_1.rs @@ -3,14 +3,9 @@ use rusty_v8 as v8; pub fn main() { let mut isolate = v8::Isolate::new(mock()); - let mut hs1 = v8::HandleScope::new(&mut isolate); - let hs1 = hs1.enter(); - - let mut hs2 = v8::EscapableHandleScope::new(hs1); - let hs2 = hs2.enter(); - - let _hs3 = v8::EscapableHandleScope::new(hs1); - let _local = v8::Integer::new(hs2, 123); + let mut scope1 = v8::HandleScope::new(&mut isolate); + let mut _scope2 = v8::EscapableHandleScope::new(&mut scope1); + let _local = v8::Integer::new(&mut scope1, 123); } fn mock() -> T { diff --git a/tests/compile_fail/handle_scope_lifetime_1.stderr b/tests/compile_fail/handle_scope_lifetime_1.stderr index e599e8df..3f3c2b28 100644 --- a/tests/compile_fail/handle_scope_lifetime_1.stderr +++ b/tests/compile_fail/handle_scope_lifetime_1.stderr @@ -1,10 +1,9 @@ -error[E0499]: cannot borrow `*hs1` as mutable more than once at a time - --> $DIR/handle_scope_lifetime_1.rs:12:44 - | -9 | let mut hs2 = v8::EscapableHandleScope::new(hs1); - | --- first mutable borrow occurs here -... -12 | let _hs3 = v8::EscapableHandleScope::new(hs1); - | ^^^ second mutable borrow occurs here -13 | let _local = v8::Integer::new(hs2, 123); - | --- first borrow later used here +error[E0499]: cannot borrow `scope1` as mutable more than once at a time + --> $DIR/handle_scope_lifetime_1.rs:8:33 + | +7 | let mut _scope2 = v8::EscapableHandleScope::new(&mut scope1); + | ----------- first mutable borrow occurs here +8 | let _local = v8::Integer::new(&mut scope1, 123); + | ^^^^^^^^^^^ second mutable borrow occurs here +9 | } + | - first borrow might be used here, when `_scope2` is dropped and runs the `Drop` code for type `rusty_v8::scope::EscapableHandleScope` diff --git a/tests/compile_fail/handle_scope_lifetime_2.rs b/tests/compile_fail/handle_scope_lifetime_2.rs index 3664b8da..ffcbf33e 100644 --- a/tests/compile_fail/handle_scope_lifetime_2.rs +++ b/tests/compile_fail/handle_scope_lifetime_2.rs @@ -3,16 +3,10 @@ use rusty_v8 as v8; pub fn main() { let mut isolate = v8::Isolate::new(mock()); - let mut hs1 = v8::HandleScope::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 _local2 = v8::Integer::new(hs1, 123); - let _local3 = v8::Integer::new(hs2, 123); + let mut scope1 = v8::HandleScope::new(&mut isolate); + let mut scope2 = v8::EscapableHandleScope::new(&mut scope1); + let _local1 = v8::Integer::new(&mut scope1, 123); + let _local2 = v8::Integer::new(&mut scope2, 123); } fn mock() -> T { diff --git a/tests/compile_fail/handle_scope_lifetime_2.stderr b/tests/compile_fail/handle_scope_lifetime_2.stderr index d4a4f30e..8f7e5fd0 100644 --- a/tests/compile_fail/handle_scope_lifetime_2.stderr +++ b/tests/compile_fail/handle_scope_lifetime_2.stderr @@ -1,10 +1,9 @@ -error[E0499]: cannot borrow `*hs1` as mutable more than once at a time - --> $DIR/handle_scope_lifetime_2.rs:14:34 - | -11 | let mut hs2 = v8::EscapableHandleScope::new(hs1); - | --- first mutable borrow occurs here -... -14 | let _local2 = v8::Integer::new(hs1, 123); - | ^^^ second mutable borrow occurs here -15 | let _local3 = v8::Integer::new(hs2, 123); - | --- first borrow later used here +error[E0499]: cannot borrow `scope1` as mutable more than once at a time + --> $DIR/handle_scope_lifetime_2.rs:8:34 + | +7 | let mut scope2 = v8::EscapableHandleScope::new(&mut scope1); + | ----------- first mutable borrow occurs here +8 | let _local1 = v8::Integer::new(&mut scope1, 123); + | ^^^^^^^^^^^ second mutable borrow occurs here +9 | let _local2 = v8::Integer::new(&mut scope2, 123); + | ----------- first borrow later used here diff --git a/tests/compile_fail/handle_scope_lifetime_3.rs b/tests/compile_fail/handle_scope_lifetime_3.rs index fe2ea86f..374ff4d3 100644 --- a/tests/compile_fail/handle_scope_lifetime_3.rs +++ b/tests/compile_fail/handle_scope_lifetime_3.rs @@ -3,13 +3,11 @@ use rusty_v8 as v8; pub fn main() { let mut isolate = v8::Isolate::new(mock()); - let mut hs1 = v8::HandleScope::new(&mut isolate); - let hs1 = hs1.enter(); + let mut scope1 = v8::HandleScope::new(&mut isolate); let _local = { - let mut hs2 = v8::EscapableHandleScope::new(hs1); - let hs2 = hs2.enter(); - v8::Integer::new(hs2, 456) + let mut _scope2 = v8::EscapableHandleScope::new(&mut scope1); + v8::Integer::new(&mut scope1, 123) }; } diff --git a/tests/compile_fail/handle_scope_lifetime_3.stderr b/tests/compile_fail/handle_scope_lifetime_3.stderr index 11137adc..cfd140ab 100644 --- a/tests/compile_fail/handle_scope_lifetime_3.stderr +++ b/tests/compile_fail/handle_scope_lifetime_3.stderr @@ -1,11 +1,9 @@ -error[E0597]: `hs2` does not live long enough - --> $DIR/handle_scope_lifetime_3.rs:11:15 +error[E0499]: cannot borrow `scope1` as mutable more than once at a time + --> $DIR/handle_scope_lifetime_3.rs:10:22 | -9 | let _local = { - | ------ borrow later stored here -10 | let mut hs2 = v8::EscapableHandleScope::new(hs1); -11 | let hs2 = hs2.enter(); - | ^^^ borrowed value does not live long enough -12 | v8::Integer::new(hs2, 456) -13 | }; - | - `hs2` dropped here while still borrowed +9 | let mut _scope2 = v8::EscapableHandleScope::new(&mut scope1); + | ----------- first mutable borrow occurs here +10 | v8::Integer::new(&mut scope1, 123) + | ^^^^^^^^^^^ second mutable borrow occurs here +11 | }; + | - first borrow might be used here, when `_scope2` is dropped and runs the `Drop` code for type `rusty_v8::scope::EscapableHandleScope` diff --git a/tests/compile_fail/handle_scope_lifetime_4.rs b/tests/compile_fail/handle_scope_lifetime_4.rs index a333a8d7..b0af5555 100644 --- a/tests/compile_fail/handle_scope_lifetime_4.rs +++ b/tests/compile_fail/handle_scope_lifetime_4.rs @@ -3,12 +3,11 @@ use rusty_v8 as v8; pub fn main() { let mut isolate = v8::Isolate::new(mock()); - let mut hs1 = v8::HandleScope::new(&mut isolate); - let hs1 = hs1.enter(); + let mut scope1 = v8::HandleScope::new(&mut isolate); - let _hs2 = { - let mut hs2 = v8::EscapableHandleScope::new(hs1); - hs2.enter() + let mut _scope3 = { + let mut scope2 = v8::HandleScope::new(&mut scope1); + v8::EscapableHandleScope::new(&mut scope2) }; } diff --git a/tests/compile_fail/handle_scope_lifetime_4.stderr b/tests/compile_fail/handle_scope_lifetime_4.stderr index 2e8d5c01..9eed7aea 100644 --- a/tests/compile_fail/handle_scope_lifetime_4.stderr +++ b/tests/compile_fail/handle_scope_lifetime_4.stderr @@ -1,10 +1,10 @@ -error[E0597]: `hs2` does not live long enough - --> $DIR/handle_scope_lifetime_4.rs:11:5 +error[E0597]: `scope2` does not live long enough + --> $DIR/handle_scope_lifetime_4.rs:10:35 | -11 | hs2.enter() - | ^^^-------- - | | - | borrowed value does not live long enough - | borrow later used here -12 | }; - | - `hs2` dropped here while still borrowed +8 | let mut _scope3 = { + | ----------- borrow later stored here +9 | let mut scope2 = v8::HandleScope::new(&mut scope1); +10 | v8::EscapableHandleScope::new(&mut scope2) + | ^^^^^^^^^^^ borrowed value does not live long enough +11 | }; + | - `scope2` dropped here while still borrowed diff --git a/tests/compile_fail/object_without_context_scope.rs b/tests/compile_fail/object_without_context_scope.rs new file mode 100644 index 00000000..43315f29 --- /dev/null +++ b/tests/compile_fail/object_without_context_scope.rs @@ -0,0 +1,12 @@ +// Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. +use rusty_v8 as v8; + +pub fn main() { + let mut isolate = v8::Isolate::new(mock()); + let mut scope = v8::HandleScope::new(&mut isolate); + let _object = v8::Object::new(&mut scope); +} + +fn mock() -> T { + unimplemented!() +} diff --git a/tests/compile_fail/object_without_context_scope.stderr b/tests/compile_fail/object_without_context_scope.stderr new file mode 100644 index 00000000..4e3a014e --- /dev/null +++ b/tests/compile_fail/object_without_context_scope.stderr @@ -0,0 +1,8 @@ +error[E0308]: mismatched types + --> $DIR/object_without_context_scope.rs:7:33 + | +7 | let _object = v8::Object::new(&mut scope); + | ^^^^^^^^^^ expected struct `rusty_v8::data::Context`, found `()` + | + = note: expected mutable reference `&mut rusty_v8::scope::HandleScope<'_>` + found mutable reference `&mut rusty_v8::scope::HandleScope<'_, ()>` diff --git a/tests/compile_fail/try_catch_exception_lifetime.rs b/tests/compile_fail/try_catch_exception_lifetime.rs index a77835c8..bc5b8c49 100644 --- a/tests/compile_fail/try_catch_exception_lifetime.rs +++ b/tests/compile_fail/try_catch_exception_lifetime.rs @@ -3,13 +3,17 @@ use rusty_v8 as v8; pub fn main() { let mut isolate = v8::Isolate::new(mock()); - let mut try_catch = v8::TryCatch::new(&mut isolate); + let mut scope1 = v8::HandleScope::new(&mut isolate); + let context = v8::Context::new(&mut scope1); + let mut scope2 = v8::ContextScope::new(&mut scope1, context); + + let mut try_catch = v8::TryCatch::new(&mut scope2); let try_catch = try_catch.enter(); let _exception = { - let mut hs = v8::HandleScope::new(&mut isolate); - let hs = hs.enter(); - try_catch.exception(hs).unwrap() + let mut scope3 = v8::HandleScope::new(&mut scope2); + let mut scope4 = v8::HandleScope::new(&mut scope3); + try_catch.exception(&mut scope4).unwrap() }; } diff --git a/tests/compile_fail/try_catch_exception_lifetime.stderr b/tests/compile_fail/try_catch_exception_lifetime.stderr index 655e562b..d583eeae 100644 --- a/tests/compile_fail/try_catch_exception_lifetime.stderr +++ b/tests/compile_fail/try_catch_exception_lifetime.stderr @@ -1,11 +1,11 @@ -error[E0597]: `hs` does not live long enough - --> $DIR/try_catch_exception_lifetime.rs:11:14 +error[E0597]: `scope3` does not live long enough + --> $DIR/try_catch_exception_lifetime.rs:15:43 | -9 | let _exception = { +13 | let _exception = { | ---------- borrow later stored here -10 | let mut hs = v8::HandleScope::new(&mut isolate); -11 | let hs = hs.enter(); - | ^^ borrowed value does not live long enough -12 | try_catch.exception(hs).unwrap() -13 | }; - | - `hs` dropped here while still borrowed +14 | let mut scope3 = v8::HandleScope::new(&mut scope2); +15 | let mut scope4 = v8::HandleScope::new(&mut scope3); + | ^^^^^^^^^^^ borrowed value does not live long enough +16 | try_catch.exception(&mut scope4).unwrap() +17 | }; + | - `scope3` dropped here while still borrowed diff --git a/tests/compile_fail/try_catch_message_lifetime.rs b/tests/compile_fail/try_catch_message_lifetime.rs index e64b6d08..a59227e1 100644 --- a/tests/compile_fail/try_catch_message_lifetime.rs +++ b/tests/compile_fail/try_catch_message_lifetime.rs @@ -3,13 +3,17 @@ use rusty_v8 as v8; pub fn main() { let mut isolate = v8::Isolate::new(mock()); - let mut try_catch = v8::TryCatch::new(&mut isolate); + let mut scope1 = v8::HandleScope::new(&mut isolate); + let context = v8::Context::new(&mut scope1); + let mut scope2 = v8::ContextScope::new(&mut scope1, context); + + let mut try_catch = v8::TryCatch::new(&mut scope2); let try_catch = try_catch.enter(); - let _exception = { - let mut hs = v8::HandleScope::new(&mut isolate); - let hs = hs.enter(); - try_catch.message(hs).unwrap() + let _message = { + let mut scope3 = v8::HandleScope::new(&mut scope2); + let mut scope4 = v8::HandleScope::new(&mut scope3); + try_catch.message(&mut scope4).unwrap() }; } diff --git a/tests/compile_fail/try_catch_message_lifetime.stderr b/tests/compile_fail/try_catch_message_lifetime.stderr index d9aee186..411142ad 100644 --- a/tests/compile_fail/try_catch_message_lifetime.stderr +++ b/tests/compile_fail/try_catch_message_lifetime.stderr @@ -1,11 +1,11 @@ -error[E0597]: `hs` does not live long enough - --> $DIR/try_catch_message_lifetime.rs:11:14 +error[E0597]: `scope3` does not live long enough + --> $DIR/try_catch_message_lifetime.rs:15:43 | -9 | let _exception = { - | ---------- borrow later stored here -10 | let mut hs = v8::HandleScope::new(&mut isolate); -11 | let hs = hs.enter(); - | ^^ borrowed value does not live long enough -12 | try_catch.message(hs).unwrap() -13 | }; - | - `hs` dropped here while still borrowed +13 | let _message = { + | -------- borrow later stored here +14 | let mut scope3 = v8::HandleScope::new(&mut scope2); +15 | let mut scope4 = v8::HandleScope::new(&mut scope3); + | ^^^^^^^^^^^ borrowed value does not live long enough +16 | try_catch.message(&mut scope4).unwrap() +17 | }; + | - `scope3` dropped here while still borrowed diff --git a/tests/slots.rs b/tests/slots.rs index c8df20b4..61f4c5d5 100644 --- a/tests/slots.rs +++ b/tests/slots.rs @@ -39,11 +39,9 @@ impl CoreIsolate { // Returns false if there was an error. fn execute(&mut self, code: &str) -> bool { - let mut hs = v8::HandleScope::new(&mut self.0); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(&mut self.0); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = v8::String::new(scope, code).unwrap(); let mut script = v8::Script::compile(scope, context, source, None).unwrap(); let r = script.run(scope, context); diff --git a/tests/test_api.rs b/tests/test_api.rs index 3b012a90..b7b15c20 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -38,13 +38,11 @@ fn setup() -> SetupGuard { #[test] fn handle_scope_nested() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope1 = hs.enter(); + let scope1 = &mut v8::HandleScope::new(isolate); { - let mut hs = v8::HandleScope::new(scope1); - let _scope2 = hs.enter(); + let _scope2 = &mut v8::HandleScope::new(scope1); } } } @@ -53,15 +51,13 @@ fn handle_scope_nested() { #[allow(clippy::float_cmp)] fn handle_scope_numbers() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope1 = hs.enter(); + let scope1 = &mut v8::HandleScope::new(isolate); let l1 = v8::Integer::new(scope1, -123); let l2 = v8::Integer::new_from_unsigned(scope1, 456); { - let mut hs = v8::HandleScope::new(scope1); - let scope2 = hs.enter(); + let scope2 = &mut v8::HandleScope::new(scope1); let l3 = v8::Number::new(scope2, 78.9); assert_eq!(l1.value(), -123); assert_eq!(l2.value(), 456); @@ -72,21 +68,17 @@ fn handle_scope_numbers() { } } -// TODO: the type checker is kumbaya with this but in reality the -// `Local` created at the end of the test is created in HandleScope -// `hs2` and not in `hs1` as specified. When this local is accessed, which is -// after `hs2` is destroyed, a crash happens. #[test] -#[ignore] -fn handle_scope_early_drop() { +fn handle_scope_non_lexical_lifetime() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); - let mut hs1 = v8::HandleScope::new(&mut isolate); - let hs1 = hs1.enter(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope1 = &mut v8::HandleScope::new(isolate); + + // Despite `local` living slightly longer than `scope2`, this test should + // not crash. let local = { - let mut hs2 = v8::HandleScope::new(hs1); - let _hs2 = hs2.enter(); - v8::Integer::new(hs1, 123) + let scope2 = &mut v8::HandleScope::new(scope1); + v8::Integer::new(scope2, 123) }; assert_eq!(local.value(), 123); } @@ -94,7 +86,7 @@ fn handle_scope_early_drop() { #[test] fn global_handles() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); let mut g1 = v8::Global::::new(); let mut g2 = v8::Global::::new(); let mut g3 = v8::Global::::new(); @@ -102,8 +94,7 @@ fn global_handles() { let g5 = v8::Global::::new(); let mut g6 = v8::Global::::new(); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let l1 = v8::String::new(scope, "bla").unwrap(); let l2 = v8::Integer::new(scope, 123); g1.set(scope, l1); @@ -114,8 +105,7 @@ fn global_handles() { g6.set(scope, l6); } { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); assert!(!g1.is_empty()); assert_eq!(g1.get(scope).unwrap().to_rust_string_lossy(scope), "bla"); assert!(!g2.is_empty()); @@ -129,13 +119,13 @@ fn global_handles() { g6.reset(scope); assert_eq!(num.value(), 100); } - g1.reset(&mut isolate); + g1.reset(isolate); assert!(g1.is_empty()); - g2.reset(&mut isolate); + g2.reset(isolate); assert!(g2.is_empty()); - g3.reset(&mut isolate); + g3.reset(isolate); assert!(g3.is_empty()); - _g4.reset(&mut isolate); + _g4.reset(isolate); assert!(_g4.is_empty()); assert!(g5.is_empty()); } @@ -147,10 +137,8 @@ fn global_handle_drop() { // Global 'g1' will be dropped _after_ the Isolate has been disposed. let mut g1 = v8::Global::::new(); - let mut isolate = v8::Isolate::new(Default::default()); - - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); let l1 = v8::String::new(scope, "foo").unwrap(); g1.set(scope, l1); @@ -163,10 +151,9 @@ fn global_handle_drop() { #[test] fn test_string() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let reference = "Hello 🦕 world!"; let local = v8::String::new(scope, reference).unwrap(); assert_eq!(15, local.length()); @@ -174,16 +161,14 @@ fn test_string() { assert_eq!(reference, local.to_rust_string_lossy(scope)); } { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let local = v8::String::empty(scope); assert_eq!(0, local.length()); assert_eq!(0, local.utf8_length(scope)); assert_eq!("", local.to_rust_string_lossy(scope)); } { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let local = v8::String::new_from_utf8(scope, b"", v8::NewStringType::Normal).unwrap(); assert_eq!(0, local.length()); @@ -196,121 +181,126 @@ fn test_string() { #[allow(clippy::float_cmp)] fn escapable_handle_scope() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope1 = hs.enter(); + let handle_scope = &mut v8::HandleScope::new(isolate); + // After dropping EscapableHandleScope, we should be able to // read escaped values. let number = { - let mut hs = v8::EscapableHandleScope::new(scope1); - let escapable_scope = hs.enter(); + let escapable_scope = &mut v8::EscapableHandleScope::new(handle_scope); let number = v8::Number::new(escapable_scope, 78.9); escapable_scope.escape(number) }; assert_eq!(number.value(), 78.9); let string = { - let mut hs = v8::EscapableHandleScope::new(scope1); - let escapable_scope = hs.enter(); + let escapable_scope = &mut v8::EscapableHandleScope::new(handle_scope); let string = v8::String::new(escapable_scope, "Hello 🦕 world!").unwrap(); escapable_scope.escape(string) }; - assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1)); + assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(handle_scope)); let string = { - let mut hs = v8::EscapableHandleScope::new(scope1); - let escapable_scope = hs.enter(); + let escapable_scope = &mut v8::EscapableHandleScope::new(handle_scope); let nested_str_val = { - let mut hs = v8::EscapableHandleScope::new(escapable_scope); - let nested_escapable_scope = hs.enter(); + let nested_escapable_scope = + &mut v8::EscapableHandleScope::new(escapable_scope); let string = v8::String::new(nested_escapable_scope, "Hello 🦕 world!").unwrap(); nested_escapable_scope.escape(string) }; escapable_scope.escape(nested_str_val) }; - assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1)); + assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(handle_scope)); } } -#[ignore] #[test] -#[should_panic( - expected = "Only one handle can escape from an EscapableHandleScope" -)] +#[should_panic(expected = "EscapableHandleScope::escape() called twice")] fn escapable_handle_scope_can_escape_only_once() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); - let mut hs = v8::HandleScope::new(&mut isolate); - let hs = hs.enter(); + let scope1 = &mut v8::HandleScope::new(isolate); + let scope2 = &mut v8::EscapableHandleScope::new(scope1); - let mut ehs = v8::EscapableHandleScope::new(hs); - let ehs = ehs.enter(); - - let local1 = v8::Integer::new(ehs, -123); - let escaped1 = ehs.escape(local1); + let local1 = v8::Integer::new(scope2, -123); + let escaped1 = scope2.escape(local1); assert!(escaped1 == local1); - let local2 = v8::Integer::new(ehs, 456); - let escaped2 = ehs.escape(local2); + let local2 = v8::Integer::new(scope2, 456); + let escaped2 = scope2.escape(local2); assert!(escaped2 == local2); } #[test] fn context_scope() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); + let context1 = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context1); - assert!(scope.get_current_context().is_none()); - assert!(scope.get_entered_or_microtask_context().is_none()); + assert!(scope.get_current_context() == context1); + assert!(scope.get_entered_or_microtask_context() == context1); { - let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let context2 = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context2); - assert!(scope.get_current_context().unwrap() == context); - assert!(scope.get_entered_or_microtask_context().unwrap() == context); + assert!(scope.get_current_context() == context2); + assert!(scope.get_entered_or_microtask_context() == context2); } - assert!(scope.get_current_context().is_none()); - assert!(scope.get_entered_or_microtask_context().is_none()); + assert!(scope.get_current_context() == context1); + assert!(scope.get_entered_or_microtask_context() == context1); +} + +#[test] +#[should_panic( + expected = "HandleScope<()> and Context do not belong to the same Isolate" +)] +fn context_scope_param_and_context_must_share_isolate() { + let _setup_guard = setup(); + let isolate1 = &mut v8::Isolate::new(Default::default()); + let isolate2 = &mut v8::Isolate::new(Default::default()); + let scope1 = &mut v8::HandleScope::new(isolate1); + let scope2 = &mut v8::HandleScope::new(isolate2); + let context1 = v8::Context::new(scope1); + let context2 = v8::Context::new(scope2); + let _context_scope_12 = &mut v8::ContextScope::new(scope1, context2); + let _context_scope_21 = &mut v8::ContextScope::new(scope2, context1); } #[test] fn microtasks() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); isolate.run_microtasks(); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); let function = v8::Function::new( scope, context, - |_: v8::FunctionCallbackScope, + |_: &mut v8::HandleScope, _: v8::FunctionCallbackArguments, _: v8::ReturnValue| { CALL_COUNT.fetch_add(1, Ordering::SeqCst); }, ) .unwrap(); - scope.isolate().enqueue_microtask(function); + scope.enqueue_microtask(function); assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 0); - scope.isolate().run_microtasks(); + scope.run_microtasks(); assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1); } } @@ -324,10 +314,11 @@ fn get_isolate_from_handle() { } fn check_handle_helper( - isolate_ptr: NonNull, + isolate: &mut v8::Isolate, expect_some: Option, local: v8::Local, ) { + let isolate_ptr = NonNull::from(isolate); let maybe_ptr = unsafe { v8__internal__GetIsolateFromHeapObject(&*local) }; let maybe_ptr = NonNull::new(maybe_ptr); if let Some(ptr) = maybe_ptr { @@ -338,74 +329,72 @@ fn get_isolate_from_handle() { } }; - fn check_handle<'s, S, F, D>(scope: &mut S, expect_some: Option, f: F) - where - S: v8::ToLocal<'s>, - F: Fn(&mut S) -> D, + fn check_handle<'s, F, D>( + scope: &mut v8::HandleScope<'s>, + expect_some: Option, + f: F, + ) where + F: Fn(&mut v8::HandleScope<'s>) -> D, D: Into>, { - let isolate_ptr = NonNull::from(scope.isolate()); let local = f(scope).into(); // Check that we can get the isolate from a Local. - check_handle_helper(isolate_ptr, expect_some, local); + check_handle_helper(scope, expect_some, local); // Check that we can still get it after converting it to a Global and back. let global = v8::Global::new_from(scope, local); let local = global.get(scope).unwrap(); - check_handle_helper(isolate_ptr, expect_some, local); + check_handle_helper(scope, expect_some, local); }; - fn check_eval<'s, S>(scope: &mut S, expect_some: Option, code: &str) - where - S: v8::ToLocal<'s>, - { - let context = scope.get_current_context().unwrap(); + fn check_eval<'s>( + scope: &mut v8::HandleScope<'s>, + expect_some: Option, + code: &str, + ) { + let context = scope.get_current_context(); check_handle(scope, expect_some, |scope| { eval(scope, context, code).unwrap() }); } let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let s = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); - check_handle(s, None, |s| v8::null(s)); - check_handle(s, None, |s| v8::undefined(s)); - check_handle(s, None, |s| v8::Boolean::new(s, true)); - check_handle(s, None, |s| v8::Boolean::new(s, false)); - check_handle(s, None, |s| v8::String::new(s, "").unwrap()); - check_eval(s, None, "''"); - check_handle(s, Some(true), |s| v8::String::new(s, "Words").unwrap()); - check_eval(s, Some(true), "'Hello'"); - check_eval(s, Some(true), "Symbol()"); - check_handle(s, Some(true), |s| v8::Object::new(s)); - check_eval(s, Some(true), "this"); - check_handle(s, Some(true), |s| s.get_current_context().unwrap()); - check_eval(s, Some(true), "({ foo: 'bar' })"); - check_eval(s, Some(true), "() => {}"); - check_handle(s, Some(true), |s| v8::Number::new(s, 4.2f64)); - check_handle(s, Some(true), |s| v8::Number::new(s, -0f64)); - check_handle(s, Some(false), |s| v8::Integer::new(s, 0)); - check_eval(s, Some(true), "3.3"); - check_eval(s, Some(false), "3.3 / 3.3"); + check_handle(scope, None, |s| v8::null(s)); + check_handle(scope, None, |s| v8::undefined(s)); + check_handle(scope, None, |s| v8::Boolean::new(s, true)); + check_handle(scope, None, |s| v8::Boolean::new(s, false)); + check_handle(scope, None, |s| v8::String::new(s, "").unwrap()); + check_eval(scope, None, "''"); + check_handle(scope, Some(true), |s| v8::String::new(s, "Words").unwrap()); + check_eval(scope, Some(true), "'Hello'"); + check_eval(scope, Some(true), "Symbol()"); + check_handle(scope, Some(true), |s| v8::Object::new(s)); + check_eval(scope, Some(true), "this"); + check_handle(scope, Some(true), |s| s.get_current_context()); + check_eval(scope, Some(true), "({ foo: 'bar' })"); + check_eval(scope, Some(true), "() => {}"); + check_handle(scope, Some(true), |s| v8::Number::new(s, 4.2f64)); + check_handle(scope, Some(true), |s| v8::Number::new(s, -0f64)); + check_handle(scope, Some(false), |s| v8::Integer::new(s, 0)); + check_eval(scope, Some(true), "3.3"); + check_eval(scope, Some(false), "3.3 / 3.3"); } #[test] fn array_buffer() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let ab = v8::ArrayBuffer::new(scope, 42); assert_eq!(42, ab.byte_length()); @@ -444,13 +433,11 @@ fn backing_store_segfault() { let params = v8::Isolate::create_params() .array_buffer_allocator(array_buffer_allocator.clone()); assert_eq!(2, v8::SharedRef::use_count(&array_buffer_allocator)); - let mut isolate = v8::Isolate::new(params); + let isolate = &mut v8::Isolate::new(params); assert_eq!(2, v8::SharedRef::use_count(&array_buffer_allocator)); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let ab = v8::ArrayBuffer::new(scope, 10); let shared_bs = ab.get_backing_store(); assert_eq!(3, v8::SharedRef::use_count(&array_buffer_allocator)); @@ -485,14 +472,12 @@ fn shared_array_buffer_allocator() { #[test] fn array_buffer_with_shared_backing_store() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let ab1 = v8::ArrayBuffer::new(scope, 42); assert_eq!(42, ab1.byte_length()); @@ -542,21 +527,13 @@ fn array_buffer_with_shared_backing_store() { } } -fn v8_str<'sc>( - scope: &mut impl v8::ToLocal<'sc>, - s: &str, -) -> v8::Local<'sc, v8::String> { - v8::String::new(scope, s).unwrap() -} - -fn eval<'sc>( - scope: &mut impl v8::ToLocal<'sc>, +fn eval<'s>( + scope: &mut v8::HandleScope<'s>, context: v8::Local, code: &str, -) -> Option> { - let mut hs = v8::EscapableHandleScope::new(scope); - let scope = hs.enter(); - let source = v8_str(scope, code); +) -> Option> { + let scope = &mut v8::EscapableHandleScope::new(scope); + let source = v8::String::new(scope, code).unwrap(); let mut script = v8::Script::compile(scope, context, source, None).unwrap(); let r = script.run(scope, context); r.map(|v| scope.escape(v)) @@ -565,13 +542,11 @@ fn eval<'sc>( #[test] fn try_catch() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); { // Error thrown - should be caught. let mut try_catch = v8::TryCatch::new(scope); @@ -623,19 +598,17 @@ fn try_catch() { #[test] fn try_catch_caught_lifetime() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let (caught_exc, caught_msg) = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); // Throw exception. let msg = v8::String::new(scope, "DANG!").unwrap(); let exc = v8::Exception::type_error(scope, msg); - scope.isolate().throw_exception(exc); + scope.throw_exception(exc); // Catch exception. let caught_exc = try_catch.exception(scope).unwrap(); let caught_msg = try_catch.message(scope).unwrap(); @@ -658,23 +631,21 @@ fn try_catch_caught_lifetime() { #[test] fn throw_exception() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); { let mut try_catch = v8::TryCatch::new(scope); let tc = try_catch.enter(); - let exception = v8_str(scope, "boom"); - scope.isolate().throw_exception(exception.into()); + let exception = v8::String::new(scope, "boom").unwrap(); + scope.throw_exception(exception.into()); assert!(tc.has_caught()); assert!(tc .exception(scope) .unwrap() - .strict_equals(v8_str(scope, "boom").into())); + .strict_equals(v8::String::new(scope, "boom").unwrap().into())); }; } } @@ -713,7 +684,7 @@ fn thread_safe_handle_drop_after_isolate() { #[test] fn terminate_execution() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); let (tx, rx) = std::sync::mpsc::channel::(); let handle = isolate.thread_safe_handle(); let t = std::thread::spawn(move || { @@ -726,13 +697,11 @@ fn terminate_execution() { tx.send(false).ok(); }); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); // Rn an infinite loop, which should be terminated. - let source = v8_str(scope, "for(;;) {}"); + let source = v8::String::new(scope, "for(;;) {}").unwrap(); let r = v8::Script::compile(scope, context, source, None); let mut script = r.unwrap(); let result = script.run(scope, context); @@ -749,14 +718,12 @@ fn terminate_execution() { #[test] fn request_interrupt_small_scripts() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); let handle = isolate.thread_safe_handle(); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); extern "C" fn callback( @@ -775,7 +742,7 @@ fn request_interrupt_small_scripts() { #[test] fn add_message_listener() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); isolate.set_capture_stack_trace_for_uncaught_exceptions(true, 32); static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -784,10 +751,9 @@ fn add_message_listener() { message: v8::Local, _exception: v8::Local, ) { - let mut sc = v8::CallbackScope::new(message); - let mut sc = v8::HandleScope::new(sc.enter()); - let scope = sc.enter(); - let context = scope.get_current_context().unwrap(); + let scope = &mut unsafe { v8::CallbackScope::new(message) }; + let scope = &mut v8::HandleScope::new(scope); + let context = scope.get_current_context(); let message_str = message.get(scope); assert_eq!(message_str.to_rust_string_lossy(scope), "Uncaught foo"); assert_eq!(Some(1), message.get_line_number(context)); @@ -819,11 +785,9 @@ fn add_message_listener() { isolate.add_message_listener(check_message_0); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = v8::String::new(scope, "throw 'foo'").unwrap(); let mut script = v8::Script::compile(scope, context, source, None).unwrap(); assert!(script.run(scope, context).is_none()); @@ -842,7 +806,7 @@ fn unexpected_module_resolve_callback<'a>( #[test] fn set_host_initialize_import_meta_object_callback() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -852,9 +816,8 @@ fn set_host_initialize_import_meta_object_callback() { meta: v8::Local, ) { CALL_COUNT.fetch_add(1, Ordering::SeqCst); - let mut cbs = v8::CallbackScope::new(context); - let mut hs = v8::HandleScope::new(cbs.enter()); - let scope = hs.enter(); + let scope = &mut unsafe { v8::CallbackScope::new(context) }; + let scope = &mut v8::HandleScope::new(scope); let key = v8::String::new(scope, "foo").unwrap(); let value = v8::String::new(scope, "bar").unwrap(); meta.create_data_property(context, key.into(), value.into()); @@ -862,11 +825,9 @@ fn set_host_initialize_import_meta_object_callback() { isolate.set_host_initialize_import_meta_object_callback(callback); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = mock_source(scope, "google.com", "import.meta;"); let mut module = v8::script_compiler::compile_module(scope, source).unwrap(); @@ -887,13 +848,11 @@ fn set_host_initialize_import_meta_object_callback() { #[test] fn script_compile_and_run() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = v8::String::new(scope, "'Hello ' + 13 + 'th planet'").unwrap(); let mut script = v8::Script::compile(scope, context, source, None).unwrap(); source.to_rust_string_lossy(scope); @@ -906,14 +865,12 @@ fn script_compile_and_run() { #[test] fn script_origin() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let resource_name = v8::String::new(scope, "foo.js").unwrap(); let resource_line_offset = v8::Integer::new(scope, 4); @@ -995,10 +952,9 @@ fn inspector_string_buffer() { #[test] fn test_primitives() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let null = v8::null(scope); assert!(!null.is_undefined()); assert!(null.is_null()); @@ -1026,12 +982,10 @@ fn test_primitives() { #[test] fn exception() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let msg_in = v8::String::new(scope, "This is a test error").unwrap(); let _exception = v8::Exception::error(scope, msg_in); @@ -1051,18 +1005,16 @@ fn exception() { #[test] fn create_message_argument_lifetimes() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); { let create_message = v8::Function::new( scope, context, - |scope: v8::FunctionCallbackScope, + |scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut rv: v8::ReturnValue| { let message = v8::Exception::create_message(scope, args.get(0)); @@ -1086,14 +1038,12 @@ fn create_message_argument_lifetimes() { #[test] fn json() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); - let json_string = v8_str(scope, "{\"a\": 1, \"b\": 2}"); + let scope = &mut v8::ContextScope::new(scope, context); + let json_string = v8::String::new(scope, "{\"a\": 1, \"b\": 2}").unwrap(); let maybe_value = v8::json::parse(scope, context, json_string); assert!(maybe_value.is_some()); let value = maybe_value.unwrap(); @@ -1108,21 +1058,19 @@ fn json() { #[test] fn object_template() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let object_templ = v8::ObjectTemplate::new(scope); let function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback); - let name = v8_str(scope, "f"); + let name = v8::String::new(scope, "f").unwrap(); let attr = v8::READ_ONLY + v8::DONT_ENUM + v8::DONT_DELETE; object_templ.set_with_attr(name.into(), function_templ.into(), attr); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let object = object_templ.new_instance(scope, context).unwrap(); assert!(!object.is_null_or_undefined()); - let name = v8_str(scope, "g"); + let name = v8::String::new(scope, "g").unwrap(); context.global(scope).define_own_property( context, name.into(), @@ -1136,7 +1084,7 @@ fn object_template() { } "#; let actual = eval(scope, context, source).unwrap(); - let expected = v8_str(scope, "true,false,true"); + let expected = v8::String::new(scope, "true,false,true").unwrap(); assert!(expected.strict_equals(actual)); let actual = eval(scope, context, "g.f()").unwrap(); let expected = v8::Integer::new(scope, 42); @@ -1148,7 +1096,7 @@ fn object_template() { } "#; let actual = eval(scope, context, source).unwrap(); - let expected = v8_str(scope, "false,false,false"); + let expected = v8::String::new(scope, "false,false,false").unwrap(); assert!(expected.strict_equals(actual)); } } @@ -1156,22 +1104,20 @@ fn object_template() { #[test] fn object_template_from_function_template() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let mut function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback); - let expected_class_name = v8_str(scope, "fortytwo"); + let expected_class_name = v8::String::new(scope, "fortytwo").unwrap(); function_templ.set_class_name(expected_class_name); let object_templ = v8::ObjectTemplate::new_from_template(scope, function_templ); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let object = object_templ.new_instance(scope, context).unwrap(); assert!(!object.is_null_or_undefined()); - let name = v8_str(scope, "g"); + let name = v8::String::new(scope, "g").unwrap(); context .global(scope) .set(context, name.into(), object.into()); @@ -1183,13 +1129,11 @@ fn object_template_from_function_template() { #[test] fn object() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let null: v8::Local = v8::null(scope).into(); let n1: v8::Local = v8::String::new(scope, "a").unwrap().into(); let n2: v8::Local = v8::String::new(scope, "b").unwrap().into(); @@ -1216,13 +1160,11 @@ fn object() { #[test] fn array() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let s1 = v8::String::new(scope, "a").unwrap(); let s2 = v8::String::new(scope, "b").unwrap(); let array = v8::Array::new(scope, 2); @@ -1258,32 +1200,30 @@ fn array() { #[test] fn create_data_property() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); eval(scope, context, "var a = {};"); - let key = v8_str(scope, "a"); + let key = v8::String::new(scope, "a").unwrap(); let obj = context .global(scope) .get(scope, context, key.into()) .unwrap(); assert!(obj.is_object()); let obj = obj.to_object(scope).unwrap(); - let key = v8_str(scope, "foo"); - let value = v8_str(scope, "bar"); + let key = v8::String::new(scope, "foo").unwrap(); + let value = v8::String::new(scope, "bar").unwrap(); assert!(obj .create_data_property(context, key.into(), value.into()) .unwrap()); let actual = obj.get(scope, context, key.into()).unwrap(); assert!(value.strict_equals(actual)); - let key2 = v8_str(scope, "foo2"); + let key2 = v8::String::new(scope, "foo2").unwrap(); assert!(obj.set(context, key2.into(), value.into()).unwrap()); let actual = obj.get(scope, context, key2.into()).unwrap(); assert!(value.strict_equals(actual)); @@ -1293,21 +1233,19 @@ fn create_data_property() { #[test] fn object_set_accessor() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); { static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); - let getter = |scope: v8::PropertyCallbackScope, + let getter = |scope: &mut v8::HandleScope, key: v8::Local, args: v8::PropertyCallbackArguments, mut rv: v8::ReturnValue| { - let context = scope.get_current_context().unwrap(); + let context = scope.get_current_context(); let this = args.this(); let expected_key = v8::String::new(scope, "getter_key").unwrap(); @@ -1349,13 +1287,11 @@ fn object_set_accessor() { #[test] fn promise_resolved() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let maybe_resolver = v8::PromiseResolver::new(scope, context); assert!(maybe_resolver.is_some()); let resolver = maybe_resolver.unwrap(); @@ -1381,13 +1317,11 @@ fn promise_resolved() { #[test] fn promise_rejected() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let maybe_resolver = v8::PromiseResolver::new(scope, context); assert!(maybe_resolver.is_some()); let resolver = maybe_resolver.unwrap(); @@ -1413,13 +1347,11 @@ fn promise_rejected() { #[test] fn proxy() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let target = v8::Object::new(scope); let handler = v8::Object::new(scope); let maybe_proxy = v8::Proxy::new(scope, context, target, handler); @@ -1434,7 +1366,7 @@ fn proxy() { } fn fn_callback( - scope: v8::FunctionCallbackScope, + scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut rv: v8::ReturnValue, ) { @@ -1445,7 +1377,7 @@ fn fn_callback( } fn fn_callback2( - scope: v8::FunctionCallbackScope, + scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut rv: v8::ReturnValue, ) { @@ -1466,7 +1398,7 @@ fn fn_callback2( } fn fortytwo_callback( - scope: v8::FunctionCallbackScope, + scope: &mut v8::HandleScope<'_>, _: v8::FunctionCallbackArguments, mut rv: v8::ReturnValue, ) { @@ -1474,7 +1406,7 @@ fn fortytwo_callback( } fn data_is_true_callback( - _scope: v8::FunctionCallbackScope, + _scope: &mut v8::HandleScope<'_>, args: v8::FunctionCallbackArguments, _rv: v8::ReturnValue, ) { @@ -1487,14 +1419,12 @@ fn data_is_true_callback( #[test] fn function() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let global = context.global(scope); let recv: v8::Local = global.into(); // create function using template @@ -1535,16 +1465,14 @@ fn function() { } extern "C" fn promise_reject_callback(msg: v8::PromiseRejectMessage) { - let mut scope = v8::CallbackScope::new(&msg); - let scope = scope.enter(); + let scope = &mut unsafe { v8::CallbackScope::new(&msg) }; let event = msg.get_event(); assert_eq!(event, v8::PromiseRejectEvent::PromiseRejectWithNoHandler); let promise = msg.get_promise(); assert_eq!(promise.state(), v8::PromiseState::Rejected); let value = msg.get_value(); { - let mut hs = v8::HandleScope::new(scope); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(scope); let value_str = value.to_string(scope).unwrap(); let rust_str = value_str.to_rust_string_lossy(scope); assert_eq!(rust_str, "promise rejected".to_string()); @@ -1554,30 +1482,28 @@ extern "C" fn promise_reject_callback(msg: v8::PromiseRejectMessage) { #[test] fn set_promise_reject_callback() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); isolate.set_promise_reject_callback(promise_reject_callback); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let resolver = v8::PromiseResolver::new(scope, context).unwrap(); let value = v8::String::new(scope, "promise rejected").unwrap(); resolver.reject(context, value.into()); } } -fn mock_script_origin<'sc>( - scope: &mut impl v8::ToLocal<'sc>, +fn mock_script_origin<'s>( + scope: &mut v8::HandleScope<'s>, resource_name_: &str, -) -> v8::ScriptOrigin<'sc> { - let resource_name = v8_str(scope, resource_name_); +) -> v8::ScriptOrigin<'s> { + let resource_name = v8::String::new(scope, resource_name_).unwrap(); 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::Boolean::new(scope, true); let script_id = v8::Integer::new(scope, 123); - let source_map_url = v8_str(scope, "source_map_url"); + let source_map_url = v8::String::new(scope, "source_map_url").unwrap(); let resource_is_opaque = v8::Boolean::new(scope, true); let is_wasm = v8::Boolean::new(scope, false); let is_module = v8::Boolean::new(scope, true); @@ -1594,12 +1520,12 @@ fn mock_script_origin<'sc>( ) } -fn mock_source<'sc>( - scope: &mut impl v8::ToLocal<'sc>, +fn mock_source<'s>( + scope: &mut v8::HandleScope<'s>, resource_name: &str, source: &str, ) -> v8::script_compiler::Source { - let source_str = v8_str(scope, source); + let source_str = v8::String::new(scope, source).unwrap(); let script_origin = mock_script_origin(scope, resource_name); v8::script_compiler::Source::new(source_str, &script_origin) } @@ -1607,19 +1533,19 @@ fn mock_source<'sc>( #[test] fn script_compiler_source() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); isolate.set_promise_reject_callback(promise_reject_callback); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = "1+2"; let script_origin = mock_script_origin(scope, "foo.js"); - let source = - v8::script_compiler::Source::new(v8_str(scope, source), &script_origin); + let source = v8::script_compiler::Source::new( + v8::String::new(scope, source).unwrap(), + &script_origin, + ); let result = v8::script_compiler::compile_module(scope, source); assert!(result.is_some()); @@ -1629,19 +1555,18 @@ fn script_compiler_source() { #[test] fn module_instantiation_failures1() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); - let source_text = v8_str( + let source_text = v8::String::new( scope, "import './foo.js';\n\ export {} from './bar.js';", - ); + ) + .unwrap(); let origin = mock_script_origin(scope, "foo.js"); let source = v8::script_compiler::Source::new(source_text, &origin); @@ -1675,11 +1600,10 @@ fn module_instantiation_failures1() { _specifier: v8::Local<'a, v8::String>, _referrer: v8::Local<'a, v8::Module>, ) -> Option> { - 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()); + let scope = &mut unsafe { v8::CallbackScope::new(context) }; + let scope = &mut v8::HandleScope::new(scope); + let e = v8::String::new(scope, "boom").unwrap(); + scope.throw_exception(e.into()); None } let result = module.instantiate_module(context, resolve_callback); @@ -1688,7 +1612,7 @@ fn module_instantiation_failures1() { assert!(tc .exception(scope) .unwrap() - .strict_equals(v8_str(scope, "boom").into())); + .strict_equals(v8::String::new(scope, "boom").unwrap().into())); assert_eq!(v8::ModuleStatus::Uninstantiated, module.get_status()); } } @@ -1699,31 +1623,28 @@ fn compile_specifier_as_module_resolve_callback<'a>( specifier: v8::Local<'a, v8::String>, _referrer: v8::Local<'a, v8::Module>, ) -> Option> { - let mut cbs = v8::CallbackScope::new_escapable(context); - let mut hs = v8::EscapableHandleScope::new(cbs.enter()); - let scope = hs.enter(); + let scope = &mut unsafe { v8::CallbackScope::new(context) }; let origin = mock_script_origin(scope, "module.js"); let source = v8::script_compiler::Source::new(specifier, &origin); let module = v8::script_compiler::compile_module(scope, source).unwrap(); - Some(scope.escape(module)) + Some(module) } #[test] fn module_evaluation() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); - let source_text = v8_str( + let source_text = v8::String::new( scope, "import 'Object.expando = 5';\n\ import 'Object.expando *= 2';", - ); + ) + .unwrap(); let origin = mock_script_origin(scope, "foo.js"); let source = v8::script_compiler::Source::new(source_text, &origin); @@ -1752,13 +1673,11 @@ fn module_evaluation() { #[test] fn primitive_array() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let length = 3; let array = v8::PrimitiveArray::new(scope, length); @@ -1769,7 +1688,7 @@ fn primitive_array() { assert!(item.is_undefined()); } - let string = v8_str(scope, "test"); + let string = v8::String::new(scope, "test").unwrap(); array.set(scope, 1, string.into()); assert!(array.get(scope, 0).is_undefined()); assert!(array.get(scope, 1).is_string()); @@ -1785,32 +1704,36 @@ fn primitive_array() { #[test] fn equality() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); - assert!(v8_str(scope, "a").strict_equals(v8_str(scope, "a").into())); - assert!(!v8_str(scope, "a").strict_equals(v8_str(scope, "b").into())); + assert!(v8::String::new(scope, "a") + .unwrap() + .strict_equals(v8::String::new(scope, "a").unwrap().into())); + assert!(!v8::String::new(scope, "a") + .unwrap() + .strict_equals(v8::String::new(scope, "b").unwrap().into())); - assert!(v8_str(scope, "a").same_value(v8_str(scope, "a").into())); - assert!(!v8_str(scope, "a").same_value(v8_str(scope, "b").into())); + assert!(v8::String::new(scope, "a") + .unwrap() + .same_value(v8::String::new(scope, "a").unwrap().into())); + assert!(!v8::String::new(scope, "a") + .unwrap() + .same_value(v8::String::new(scope, "b").unwrap().into())); } } #[test] fn array_buffer_view() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = v8::String::new(scope, "new Uint8Array([23,23,23,23])").unwrap(); let mut script = v8::Script::compile(scope, context, source, None).unwrap(); @@ -1837,29 +1760,25 @@ fn snapshot_creator() { // the value 3. let startup_data = { let mut snapshot_creator = v8::SnapshotCreator::new(None); + // TODO(ry) this shouldn't be necessary. workaround unfinished business in + // the scope type system. + let mut isolate = unsafe { snapshot_creator.get_owned_isolate() }; { - // TODO(ry) this shouldn't be necessary. workaround unfinished business in - // the scope type system. - let mut isolate = unsafe { snapshot_creator.get_owned_isolate() }; - // Check that the SnapshotCreator isolate has been set up correctly. let _ = isolate.thread_safe_handle(); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); - + let scope = &mut v8::HandleScope::new(&mut isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); + let source = v8::String::new(scope, "a = 1 + 2").unwrap(); let mut script = v8::Script::compile(scope, context, source, None).unwrap(); script.run(scope, context).unwrap(); snapshot_creator.set_default_context(context); - std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary. } - + std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary. snapshot_creator .create_blob(v8::FunctionCodeHandling::Clear) .unwrap() @@ -1869,13 +1788,11 @@ fn snapshot_creator() { // value. { let params = v8::Isolate::create_params().snapshot_blob(startup_data); - let mut isolate = v8::Isolate::new(params); + let isolate = &mut v8::Isolate::new(params); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = v8::String::new(scope, "a === 3").unwrap(); let mut script = v8::Script::compile(scope, context, source, None).unwrap(); @@ -1901,16 +1818,13 @@ fn external_references() { let startup_data = { let mut snapshot_creator = v8::SnapshotCreator::new(Some(&EXTERNAL_REFERENCES)); + // TODO(ry) this shouldn't be necessary. workaround unfinished business in + // the scope type system. + let mut isolate = unsafe { snapshot_creator.get_owned_isolate() }; { - // TODO(ry) this shouldn't be necessary. workaround unfinished business in - // the scope type system. - let mut isolate = unsafe { snapshot_creator.get_owned_isolate() }; - - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(&mut isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); // create function using template let mut fn_template = v8::FunctionTemplate::new(scope, fn_callback); @@ -1919,13 +1833,15 @@ fn external_references() { .expect("Unable to create function"); let global = context.global(scope); - global.set(context, v8_str(scope, "F").into(), function.into()); + global.set( + context, + v8::String::new(scope, "F").unwrap().into(), + function.into(), + ); snapshot_creator.set_default_context(context); - - std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary. } - + std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary. snapshot_creator .create_blob(v8::FunctionCodeHandling::Clear) .unwrap() @@ -1937,13 +1853,11 @@ fn external_references() { let params = v8::Isolate::create_params() .snapshot_blob(startup_data) .external_references(&**EXTERNAL_REFERENCES); - let mut isolate = v8::Isolate::new(params); + let isolate = &mut v8::Isolate::new(params); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let result = eval(scope, context, "if(F() != 'wrong answer') throw 'boom1'"); @@ -1975,13 +1889,11 @@ fn create_params_snapshot_blob() { #[test] fn uint8_array() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = v8::String::new(scope, "new Uint8Array([23,23,23,23])").unwrap(); let mut script = v8::Script::compile(scope, context, source, None).unwrap(); @@ -2005,7 +1917,7 @@ fn uint8_array() { #[test] fn dynamic_import() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -2014,29 +1926,28 @@ fn dynamic_import() { _referrer: v8::Local, specifier: v8::Local, ) -> *mut v8::Promise { - let mut cbs = v8::CallbackScope::new(context); - let mut hs = v8::HandleScope::new(cbs.enter()); - let scope = hs.enter(); - assert!(specifier.strict_equals(v8_str(scope, "bar.js").into())); - let e = v8_str(scope, "boom"); - scope.isolate().throw_exception(e.into()); + let scope = &mut unsafe { v8::CallbackScope::new(context) }; + let scope = &mut v8::HandleScope::new(scope); + assert!( + specifier.strict_equals(v8::String::new(scope, "bar.js").unwrap().into()) + ); + let e = v8::String::new(scope, "boom").unwrap(); + scope.throw_exception(e.into()); CALL_COUNT.fetch_add(1, Ordering::SeqCst); std::ptr::null_mut() } isolate.set_host_import_module_dynamically_callback(dynamic_import_cb); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let result = eval( scope, context, "(async function () {\n\ - let x = await import('bar.js');\n\ + let x = await import('bar.js');\n\ })();", ); assert!(result.is_some()); @@ -2047,13 +1958,11 @@ fn dynamic_import() { #[test] fn shared_array_buffer() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let sab = v8::SharedArrayBuffer::new(scope, 16).unwrap(); let shared_bs_1 = sab.get_backing_store(); @@ -2062,7 +1971,11 @@ fn shared_array_buffer() { let global = context.global(scope); let r = global - .create_data_property(context, v8_str(scope, "shared").into(), sab.into()) + .create_data_property( + context, + v8::String::new(scope, "shared").unwrap().into(), + sab.into(), + ) .unwrap(); assert!(r); let source = v8::String::new( @@ -2103,13 +2016,11 @@ fn shared_array_buffer() { #[allow(clippy::eq_op)] fn value_checker() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let value = eval(scope, context, "undefined").unwrap(); assert!(value.is_undefined()); @@ -2436,13 +2347,11 @@ fn value_checker() { #[test] fn try_from_local() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); { let value: v8::Local = v8::undefined(scope).into(); @@ -2665,17 +2574,15 @@ impl v8::inspector::ChannelImpl for ChannelCounter { #[test] fn inspector_dispatch_protocol_message() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); use v8::inspector::*; let mut default_client = ClientCounter::new(); - let mut inspector = V8Inspector::create(&mut isolate, &mut default_client); + let mut inspector = V8Inspector::create(isolate, &mut default_client); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let _scope = cs.enter(); + let mut _scope = v8::ContextScope::new(scope, context); let name = b""; let name_view = StringView::from(&name[..]); @@ -2698,17 +2605,15 @@ fn inspector_dispatch_protocol_message() { #[test] fn inspector_schedule_pause_on_next_statement() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); use v8::inspector::*; let mut client = ClientCounter::new(); - let mut inspector = V8Inspector::create(&mut isolate, &mut client); + let mut inspector = V8Inspector::create(isolate, &mut client); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let mut channel = ChannelCounter::new(); let state = b"{}"; @@ -2760,7 +2665,7 @@ fn inspector_schedule_pause_on_next_statement() { #[test] fn inspector_console_api_message() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); use v8::inspector::*; @@ -2802,13 +2707,11 @@ fn inspector_console_api_message() { } let mut client = Client::new(); - let mut inspector = V8Inspector::create(&mut isolate, &mut client); + let mut inspector = V8Inspector::create(isolate, &mut client); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let name = b""; let name_view = StringView::from(&name[..]); @@ -2826,17 +2729,15 @@ fn inspector_console_api_message() { #[test] fn context_from_object_template() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let object_templ = v8::ObjectTemplate::new(scope); let function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback); - let name = v8_str(scope, "f"); + let name = v8::String::new(scope, "f").unwrap(); object_templ.set(name.into(), function_templ.into()); let context = v8::Context::new_from_template(scope, object_templ); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let actual = eval(scope, context, "f()").unwrap(); let expected = v8::Integer::new(scope, 42); assert!(expected.strict_equals(actual)); @@ -2846,13 +2747,11 @@ fn context_from_object_template() { #[test] fn take_heap_snapshot() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let source = r#" { class Eyecatcher {} @@ -2862,7 +2761,7 @@ fn take_heap_snapshot() { "#; let _ = eval(scope, context, source).unwrap(); let mut vec = Vec::::new(); - isolate.take_heap_snapshot(|chunk| { + scope.take_heap_snapshot(|chunk| { vec.extend_from_slice(chunk); true }); @@ -2874,13 +2773,11 @@ fn take_heap_snapshot() { #[test] fn test_prototype_api() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let obj = v8::Object::new(scope); let proto_obj = v8::Object::new(scope); @@ -2902,11 +2799,9 @@ fn test_prototype_api() { assert_eq!(sub_gotten.to_rust_string_lossy(scope), "test_proto_value"); } { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let obj = v8::Object::new(scope); obj.set_prototype(context, v8::null(scope).into()); @@ -2914,11 +2809,9 @@ fn test_prototype_api() { assert!(obj.get_prototype(scope).unwrap().is_null()); } { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let val = eval(scope, context, "({ __proto__: null })").unwrap(); let obj = val.to_object(scope).unwrap(); @@ -2930,13 +2823,11 @@ fn test_prototype_api() { #[test] fn test_map_api() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let value = eval(scope, context, "new Map([['r','s'],['v',8]])").unwrap(); assert!(value.is_map()); @@ -2969,14 +2860,12 @@ fn test_map_api() { #[test] fn test_object_get_property_names() { let _setup_guard = setup(); - let mut isolate = v8::Isolate::new(Default::default()); + let isolate = &mut v8::Isolate::new(Default::default()); - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let js_test_str: v8::Local = v8::String::new(scope, "test").unwrap().into(); @@ -3037,22 +2926,20 @@ fn module_snapshot() { let startup_data = { let mut snapshot_creator = v8::SnapshotCreator::new(None); + // TODO(ry) this shouldn't be necessary. workaround unfinished business in + // the scope type system. + let mut isolate = unsafe { snapshot_creator.get_owned_isolate() }; { - // TODO(ry) this shouldn't be necessary. workaround unfinished business in - // the scope type system. - let mut isolate = unsafe { snapshot_creator.get_owned_isolate() }; - - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(&mut isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); - let source_text = v8_str( + let source_text = v8::String::new( scope, "import 'globalThis.b = 42';\n\ globalThis.a = 3", - ); + ) + .unwrap(); let origin = mock_script_origin(scope, "foo.js"); let source = v8::script_compiler::Source::new(source_text, &origin); @@ -3072,9 +2959,8 @@ fn module_snapshot() { assert_eq!(v8::ModuleStatus::Evaluated, module.get_status()); snapshot_creator.set_default_context(context); - std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary. } - + std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary. snapshot_creator .create_blob(v8::FunctionCodeHandling::Keep) .unwrap() @@ -3082,13 +2968,11 @@ fn module_snapshot() { assert!(startup_data.len() > 0); { let params = v8::Isolate::create_params().snapshot_blob(startup_data); - let mut isolate = v8::Isolate::new(params); + let isolate = &mut v8::Isolate::new(params); { - let mut hs = v8::HandleScope::new(&mut isolate); - let scope = hs.enter(); + let scope = &mut v8::HandleScope::new(isolate); let context = v8::Context::new(scope); - let mut cs = v8::ContextScope::new(scope, context); - let scope = cs.enter(); + let scope = &mut v8::ContextScope::new(scope, context); let true_val = v8::Boolean::new(scope, true).into(); diff --git a/tests/test_ui.rs b/tests/test_ui.rs index 3db64f77..ba2320f7 100644 --- a/tests/test_ui.rs +++ b/tests/test_ui.rs @@ -7,19 +7,5 @@ fn ui() { env::set_var("DENO_TRYBUILD", "1"); let t = trybuild::TestCases::new(); - t.compile_fail("tests/compile_fail/boxed_local.rs"); - t.compile_fail("tests/compile_fail/handle_scope_escape_lifetime.rs"); - t.compile_fail("tests/compile_fail/handle_scope_lifetime_1.rs"); - t.compile_fail("tests/compile_fail/handle_scope_lifetime_2.rs"); - t.compile_fail("tests/compile_fail/handle_scope_lifetime_3.rs"); - t.compile_fail("tests/compile_fail/handle_scope_lifetime_4.rs"); - t.compile_fail("tests/compile_fail/try_catch_exception_lifetime.rs"); - t.compile_fail("tests/compile_fail/try_catch_message_lifetime.rs"); - - // For unclear reasons rustc on Windows in Github Actions omits some - // diagnostic information, causing this test to fail. It might have something - // to do with this Rust issue: https://github.com/rust-lang/rust/issues/53081. - if cfg!(not(windows)) || env::var("GITHUB_ACTION").is_err() { - t.compile_fail("tests/compile_fail/handle_scope_escape_to_nowhere.rs"); - } + t.compile_fail("tests/compile_fail/*.rs"); }