diff --git a/src/binding.cc b/src/binding.cc index 5592ed9d..c304d7e0 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -220,17 +220,9 @@ const v8::Data* v8__Global__New(v8::Isolate* isolate, const v8::Data& other) { return make_pod(std::move(global)); } -void v8__Global__Reset__0(const v8::Data*& self) { - auto global = ptr_to_global(self); +void v8__Global__Reset(const v8::Data* data) { + auto global = ptr_to_global(data); global.Reset(); - self = make_pod(std::move(global)); -} - -void v8__Global__Reset__2(const v8::Data*& self, v8::Isolate* isolate, - const v8::Data* const& other) { - auto global = ptr_to_global(self); - global.Reset(isolate, ptr_to_local(other)); - self = make_pod(std::move(global)); } void v8__ScriptCompiler__Source__CONSTRUCT( diff --git a/src/data.rs b/src/data.rs index 31133acc..990a2854 100644 --- a/src/data.rs +++ b/src/data.rs @@ -53,33 +53,38 @@ macro_rules! impl_eq { }; } +extern "C" { + fn v8__Data__EQ(this: *const Data, other: *const Data) -> bool; + fn v8__Value__StrictEquals(this: *const Value, other: *const Value) -> bool; +} + macro_rules! impl_partial_eq { { $rhs:ident for $type:ident use identity } => { - impl<'s> PartialEq> for Local<'s, $type> { - fn eq(&self, other: &Local<'s, $rhs>) -> bool { - self.eq_identity((*other).into()) + impl<'s> PartialEq<$rhs> for $type { + fn eq(&self, other: &$rhs) -> bool { + unsafe { + v8__Data__EQ( + self as *const _ as *const Data, + other as *const _ as *const Data, + ) + } } } }; { $rhs:ident for $type:ident use strict_equals } => { - impl<'s> PartialEq> for Local<'s, $type> { - fn eq(&self, other: &Local<'s, $rhs>) -> bool { - self.strict_equals((*other).into()) + impl<'s> PartialEq<$rhs> for $type { + fn eq(&self, other: &$rhs) -> bool { + unsafe { + v8__Value__StrictEquals( + self as *const _ as *const Value, + other as *const _ as *const Value, + ) + } } } }; } -extern "C" { - fn v8__Data__EQ(this: *const Data, other: *const Data) -> bool; -} - -impl Data { - fn eq_identity(&self, other: Local) -> bool { - unsafe { v8__Data__EQ(self, &*other) } - } -} - #[derive(Clone, Copy, Debug)] pub struct TryFromTypeError { expected_type: &'static str, diff --git a/src/global.rs b/src/global.rs deleted file mode 100644 index c178b080..00000000 --- a/src/global.rs +++ /dev/null @@ -1,179 +0,0 @@ -use std::mem::transmute; -use std::ptr::NonNull; - -use crate::Data; -use crate::HandleScope; -use crate::Isolate; -use crate::IsolateHandle; -use crate::Local; - -extern "C" { - fn v8__Local__New(isolate: *mut Isolate, other: *const Data) -> *const Data; - - fn v8__Global__New(isolate: *mut Isolate, other: *const Data) -> *const Data; - - fn v8__Global__Reset__0(this: *mut *const Data); - - fn v8__Global__Reset__2( - this: *mut *const Data, - isolate: *mut Isolate, - other: *const *const Data, - ); -} - -/// An object reference that is independent of any handle scope. Where -/// a Local handle only lives as long as the HandleScope in which it was -/// allocated, a global handle remains valid until it is explicitly -/// disposed using reset(). -/// -/// A global handle contains a reference to a storage cell within -/// the V8 engine which holds an object value and which is updated by -/// the garbage collector whenever the object is moved. A new storage -/// cell can be created using the constructor or Global::set and -/// existing handles can be disposed using Global::reset. -#[repr(C)] -pub struct Global { - value: Option>, - isolate_handle: Option, -} - -impl Global { - /// Construct a Global with no storage cell. - pub fn new() -> Self { - Self { - value: None, - isolate_handle: None, - } - } - - /// 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 Isolate, other: impl AnyHandle) -> Self { - let other_value = other.read(scope); - Self { - value: other_value - .map(|v| unsafe { transmute(v8__Global__New(scope, transmute(v))) }), - isolate_handle: other_value.map(|_| scope.thread_safe_handle()), - } - } - - /// Returns true if this Global is empty, i.e., has not been - /// assigned an object. - pub fn is_empty(&self) -> bool { - self.value.is_none() - } - - /// Construct a Local from this global handle. - pub fn get<'s>( - &self, - 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(|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 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 { - v8__Global__Reset__0( - &mut *(target as *mut Option> as *mut *const Data), - ) - }, - (target, source) => unsafe { - v8__Global__Reset__2( - &mut *(target as *mut Option> as *mut *const Data), - scope, - &*(source as *const Option> as *const *const Data), - ) - }, - } - 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 Isolate) { - self.set(scope, None); - } - - fn check_isolate(&self, isolate: &mut Isolate) { - match self.value { - None => assert!(self.isolate_handle.is_none()), - Some(_) => assert_eq!( - unsafe { self.isolate_handle.as_ref().unwrap().get_isolate_ptr() }, - isolate as *mut _ - ), - } - } -} - -impl Default for Global { - fn default() -> Self { - Self::new() - } -} - -impl Drop for Global { - fn drop(&mut self) { - match &mut self.value { - None => { - // This global handle is empty. - assert!(self.isolate_handle.is_none()) - } - Some(_) - if unsafe { - self - .isolate_handle - .as_ref() - .unwrap() - .get_isolate_ptr() - .is_null() - } => - { - // This global handle is associated with an Isolate that has already - // been disposed. - } - addr @ Some(_) => unsafe { - // Destroy the storage cell that contains the contents of this Global. - v8__Global__Reset__0( - &mut *(addr as *mut Option> as *mut *const Data), - ) - }, - } - } -} - -pub trait AnyHandle { - fn read(self, isolate: &mut Isolate) -> Option>; -} - -impl<'s, T> AnyHandle for Local<'s, T> { - fn read(self, _isolate: &mut Isolate) -> Option> { - Some(self.as_non_null()) - } -} - -impl<'s, T> AnyHandle for Option> { - fn read(self, _isolate: &mut Isolate) -> Option> { - self.map(|local| local.as_non_null()) - } -} - -impl<'s, T> AnyHandle for &Global { - fn read(self, isolate: &mut Isolate) -> Option> { - self.check_isolate(isolate); - self.value - } -} diff --git a/src/handle.rs b/src/handle.rs new file mode 100644 index 00000000..d8e4bbd0 --- /dev/null +++ b/src/handle.rs @@ -0,0 +1,373 @@ +use std::marker::PhantomData; +use std::mem::transmute; +use std::ops::Deref; +use std::ptr::NonNull; + +use crate::Data; +use crate::HandleScope; +use crate::Isolate; +use crate::IsolateHandle; + +extern "C" { + fn v8__Local__New(isolate: *mut Isolate, other: *const Data) -> *const Data; + fn v8__Global__New(isolate: *mut Isolate, data: *const Data) -> *const Data; + fn v8__Global__Reset(data: *const Data); +} + +/// An object reference managed by the v8 garbage collector. +/// +/// All objects returned from v8 have to be tracked by the garbage +/// collector so that it knows that the objects are still alive. Also, +/// because the garbage collector may move objects, it is unsafe to +/// point directly to an object. Instead, all objects are stored in +/// handles which are known by the garbage collector and updated +/// whenever an object moves. Handles should always be passed by value +/// (except in cases like out-parameters) and they should never be +/// allocated on the heap. +/// +/// There are two types of handles: local and persistent handles. +/// +/// Local handles are light-weight and transient and typically used in +/// local operations. They are managed by HandleScopes. That means that a +/// HandleScope must exist on the stack when they are created and that they are +/// only valid inside of the `HandleScope` active during their creation. +/// For passing a local handle to an outer `HandleScope`, an +/// `EscapableHandleScope` and its `Escape()` method must be used. +/// +/// Persistent handles can be used when storing objects across several +/// independent operations and have to be explicitly deallocated when they're no +/// longer used. +/// +/// It is safe to extract the object stored in the handle by +/// dereferencing the handle (for instance, to extract the *Object from +/// a Local); the value will still be governed by a handle +/// behind the scenes and the same rules apply to these values as to +/// their handles. +/// +/// Note: Local handles in Rusty V8 differ from the V8 C++ API in that they are +/// never empty. In situations where empty handles are needed, use +/// Option. +#[repr(C)] +pub struct Local<'s, T>(NonNull, PhantomData<&'s ()>); + +impl<'s, T> Local<'s, T> { + /// Construct a new Local from an existing Handle. + pub fn new( + scope: &mut HandleScope<'s, ()>, + handle: impl Handle, + ) -> Self { + let HandleInfo { data, host } = handle.get_handle_info(); + host.assert_match_isolate(scope); + unsafe { + scope.cast_local(|sd| { + v8__Local__New(sd.get_isolate_ptr(), data.cast().as_ptr()) as *const T + }) + } + .unwrap() + } + + /// 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<'s, A>) -> Self + where + Local<'s, A>: From, + { + transmute(other) + } + + pub(crate) unsafe fn from_raw(ptr: *const T) -> Option { + 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 slice_into_raw(slice: &[Self]) -> &[*const T] { + unsafe { &*(slice as *const [Self] as *const [*const T]) } + } +} + +impl<'s, T> Copy for Local<'s, T> {} + +impl<'s, T> Clone for Local<'s, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'s, T> Deref for Local<'s, T> { + type Target = T; + fn deref(&self) -> &T { + unsafe { self.0.as_ref() } + } +} + +/// An object reference that is independent of any handle scope. Where +/// a Local handle only lives as long as the HandleScope in which it was +/// allocated, a global handle remains valid until it is explicitly +/// disposed using reset(). +/// +/// A global handle contains a reference to a storage cell within +/// the V8 engine which holds an object value and which is updated by +/// the garbage collector whenever the object is moved. +pub struct Global { + data: NonNull, + isolate_handle: IsolateHandle, +} + +impl Global { + /// Construct a new Global from an existing Handle. + pub fn new(isolate: &mut Isolate, handle: impl Handle) -> Self { + let HandleInfo { data, host } = handle.get_handle_info(); + host.assert_match_isolate(isolate); + unsafe { Self::new_raw(isolate, data) } + } + + /// Implementation helper function that contains the code that can be shared + /// between `Global::new()` and `Global::clone()`. + unsafe fn new_raw(isolate: *mut Isolate, data: NonNull) -> Self { + let data = data.cast().as_ptr(); + let data = v8__Global__New(isolate, data) as *const T; + let data = NonNull::new_unchecked(data as *mut _); + let isolate_handle = (*isolate).thread_safe_handle(); + Self { + data, + isolate_handle, + } + } + + pub fn get<'a>(&'a self, scope: &mut Isolate) -> &'a T { + Handle::get(self, scope) + } +} + +impl Clone for Global { + fn clone(&self) -> Self { + let HandleInfo { data, host } = self.get_handle_info(); + unsafe { Self::new_raw(host.get_isolate().as_mut(), data) } + } +} + +impl Drop for Global { + fn drop(&mut self) { + unsafe { + if self.isolate_handle.get_isolate_ptr().is_null() { + // This `Global` handle is associated with an `Isolate` that has already + // been disposed. + } else { + // Destroy the storage cell that contains the contents of this Global. + v8__Global__Reset(self.data.cast().as_ptr()) + } + } + } +} + +pub trait Handle: Sized { + type Data; + + #[doc(hidden)] + fn get_handle_info(&self) -> HandleInfo; + + /// Returns a reference to the V8 heap object that this handle represents. + /// The handle does not get cloned, nor is it converted to a `Local` handle. + /// + /// # Panics + /// + /// This function panics in the following situations: + /// - The handle is not hosted by the specified Isolate. + /// - The Isolate that hosts this handle has been disposed. + fn get<'a>(&'a self, isolate: &mut Isolate) -> &'a Self::Data { + let HandleInfo { data, host } = self.get_handle_info(); + host.assert_match_isolate(isolate); + unsafe { &*data.as_ptr() } + } + + /// Reads the inner value contained in this handle, _without_ verifying that + /// the this handle is hosted by the currently active `Isolate`. + /// + /// # Safety + /// + /// Using a V8 heap object with another `Isolate` than the `Isolate` that + /// hosts it is not permitted under any circumstance. Doing so leads to + /// undefined behavior, likely a crash. + /// + /// # Panics + /// + /// This function panics if the `Isolate` that hosts the handle has been + /// disposed. + unsafe fn get_unchecked(&self) -> &Self::Data { + let HandleInfo { data, host } = self.get_handle_info(); + if let HandleHost::DisposedIsolate = host { + panic!("attempt to access Handle hosted by disposed Isolate"); + } + &*data.as_ptr() + } +} + +impl<'s, T> Handle for Local<'s, T> { + type Data = T; + fn get_handle_info(&self) -> HandleInfo { + HandleInfo::new(self.as_non_null(), HandleHost::Scope) + } +} + +impl<'a, 's: 'a, T> Handle for &'a Local<'s, T> { + type Data = T; + fn get_handle_info(&self) -> HandleInfo { + HandleInfo::new(self.as_non_null(), HandleHost::Scope) + } +} + +impl Handle for Global { + type Data = T; + fn get_handle_info(&self) -> HandleInfo { + HandleInfo::new(self.data, (&self.isolate_handle).into()) + } +} + +impl<'a, T> Handle for &'a Global { + type Data = T; + fn get_handle_info(&self) -> HandleInfo { + HandleInfo::new(self.data, (&self.isolate_handle).into()) + } +} + +impl<'s, T, Rhs: Handle> PartialEq for Local<'s, T> +where + T: PartialEq, +{ + fn eq(&self, other: &Rhs) -> bool { + let i1 = self.get_handle_info(); + let i2 = other.get_handle_info(); + i1.host.match_host(i2.host, None) + && unsafe { i1.data.as_ref() == i2.data.as_ref() } + } +} + +impl<'s, T, Rhs: Handle> PartialEq for Global +where + T: PartialEq, +{ + fn eq(&self, other: &Rhs) -> bool { + let i1 = self.get_handle_info(); + let i2 = other.get_handle_info(); + i1.host.match_host(i2.host, None) + && unsafe { i1.data.as_ref() == i2.data.as_ref() } + } +} + +#[derive(Copy, Clone)] +pub struct HandleInfo { + data: NonNull, + host: HandleHost, +} + +impl HandleInfo { + fn new(data: NonNull, host: HandleHost) -> Self { + Self { data, host } + } +} + +#[derive(Copy, Clone)] +enum HandleHost { + // Note: the `HandleHost::Scope` variant does not indicate that the handle + // it applies to is not associated with an `Isolate`. It only means that + // the handle is a `Local` handle that was unable to provide a pointer to + // the `Isolate` that hosts it (the handle) and the currently entered + // scope. + Scope, + Isolate(NonNull), + DisposedIsolate, +} + +impl From<&'_ mut Isolate> for HandleHost { + fn from(isolate: &'_ mut Isolate) -> Self { + Self::Isolate(NonNull::from(isolate)) + } +} + +impl From<&'_ IsolateHandle> for HandleHost { + fn from(isolate_handle: &IsolateHandle) -> Self { + NonNull::new(unsafe { isolate_handle.get_isolate_ptr() }) + .map(Self::Isolate) + .unwrap_or(Self::DisposedIsolate) + } +} + +impl HandleHost { + /// Compares two `HandleHost` values, returning `true` if they refer to the + /// same `Isolate`, or `false` if they refer to different isolates. + /// + /// If the caller knows which `Isolate` the currently entered scope (if any) + /// belongs to, it should pass on this information via the second argument + /// (`scope_isolate_opt`). + /// + /// # Panics + /// + /// This function panics if one of the `HandleHost` values refers to an + /// `Isolate` that has been disposed. + /// + /// # Safety / Bugs + /// + /// The current implementation is a bit too forgiving. If it cannot decide + /// whether two hosts refer to the same `Isolate`, it just returns `true`. + /// Note that this can only happen when the caller does _not_ provide a value + /// for the `scope_isolate_opt` argument. + fn match_host( + self, + other: Self, + scope_isolate_opt: Option<&mut Isolate>, + ) -> bool { + let scope_isolate_opt_nn = scope_isolate_opt.map(NonNull::from); + match (self, other, scope_isolate_opt_nn) { + (Self::Scope, Self::Scope, _) => true, + (Self::Isolate(ile1), Self::Isolate(ile2), _) => ile1 == ile2, + (Self::Scope, Self::Isolate(ile1), Some(ile2)) => ile1 == ile2, + (Self::Isolate(ile1), Self::Scope, Some(ile2)) => ile1 == ile2, + // TODO(pisciaureus): If the caller didn't provide a `scope_isolate_opt` + // value that works, we can't do a meaningful check. So all we do for now + // is pretend the Isolates match and hope for the best. This eventually + // needs to be tightened up. + (Self::Scope, Self::Isolate(_), _) => true, + (Self::Isolate(_), Self::Scope, _) => true, + // Handles hosted in an Isolate that has been disposed aren't good for + // anything, even if a pair of handles used to to be hosted in the same + // now-disposed solate. + (Self::DisposedIsolate, ..) | (_, Self::DisposedIsolate, _) => { + panic!("attempt to access Handle hosted by disposed Isolate") + } + } + } + + fn assert_match_host(self, other: Self, scope_opt: Option<&mut Isolate>) { + assert!( + self.match_host(other, scope_opt), + "attempt to use Handle in an Isolate that is not its host" + ) + } + + fn match_isolate(self, isolate: &mut Isolate) -> bool { + self.match_host(isolate.into(), Some(isolate)) + } + + fn assert_match_isolate(self, isolate: &mut Isolate) { + self.assert_match_host(isolate.into(), Some(isolate)) + } + + fn get_isolate(self) -> NonNull { + match self { + Self::Scope => panic!("host Isolate for Handle not available"), + Self::Isolate(ile) => ile, + Self::DisposedIsolate => panic!("attempt to access disposed Isolate"), + } + } + + fn get_isolate_handle(self) -> IsolateHandle { + unsafe { self.get_isolate().as_mut().thread_safe_handle() } + } +} diff --git a/src/lib.rs b/src/lib.rs index 00fac51a..a7a6cfe9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,10 +40,9 @@ mod data; mod exception; mod external_references; mod function; -mod global; +mod handle; mod isolate; mod isolate_create_params; -mod local; mod module; mod number; mod object; @@ -78,7 +77,9 @@ pub use exception::*; pub use external_references::ExternalReference; pub use external_references::ExternalReferences; pub use function::*; -pub use global::Global; +pub use handle::Global; +pub use handle::Handle; +pub use handle::Local; pub use isolate::HostImportModuleDynamicallyCallback; pub use isolate::HostInitializeImportMetaObjectCallback; pub use isolate::Isolate; @@ -87,7 +88,6 @@ pub use isolate::MessageCallback; pub use isolate::OwnedIsolate; pub use isolate::PromiseRejectCallback; pub use isolate_create_params::CreateParams; -pub use local::Local; pub use module::*; pub use object::*; pub use platform::new_default_platform; diff --git a/src/local.rs b/src/local.rs deleted file mode 100644 index 217cd9c9..00000000 --- a/src/local.rs +++ /dev/null @@ -1,90 +0,0 @@ -use std::marker::PhantomData; -use std::mem::transmute; -use std::ops::Deref; -use std::ptr::NonNull; - -/// An object reference managed by the v8 garbage collector. -/// -/// All objects returned from v8 have to be tracked by the garbage -/// collector so that it knows that the objects are still alive. Also, -/// because the garbage collector may move objects, it is unsafe to -/// point directly to an object. Instead, all objects are stored in -/// handles which are known by the garbage collector and updated -/// whenever an object moves. Handles should always be passed by value -/// (except in cases like out-parameters) and they should never be -/// allocated on the heap. -/// -/// There are two types of handles: local and persistent handles. -/// -/// Local handles are light-weight and transient and typically used in -/// local operations. They are managed by HandleScopes. That means that a -/// HandleScope must exist on the stack when they are created and that they are -/// only valid inside of the HandleScope active during their creation. -/// For passing a local handle to an outer HandleScope, an EscapableHandleScope -/// and its Escape() method must be used. -/// -/// Persistent handles can be used when storing objects across several -/// independent operations and have to be explicitly deallocated when they're no -/// longer used. -/// -/// It is safe to extract the object stored in the handle by -/// dereferencing the handle (for instance, to extract the *Object from -/// a Local); the value will still be governed by a handle -/// behind the scenes and the same rules apply to these values as to -/// their handles. -/// -/// Note: Local handles in Rusty V8 differ from the V8 C++ API in that they are -/// never empty. In situations where empty handles are needed, use -/// Option. -#[repr(C)] -pub struct Local<'s, T>(NonNull, PhantomData<&'s ()>); - -impl<'s, T> Copy for Local<'s, T> {} - -impl<'s, T> Clone for Local<'s, T> { - fn clone(&self) -> Self { - *self - } -} - -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<'s, A>) -> Self - where - Local<'s, A>: From, - { - transmute(other) - } - - pub(crate) unsafe fn from_raw(ptr: *const T) -> Option { - 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 slice_into_raw(slice: &[Self]) -> &[*const T] { - unsafe { &*(slice as *const [Self] as *const [*const T]) } - } -} - -impl<'s, T> Deref for Local<'s, T> { - type Target = T; - fn deref(&self) -> &T { - unsafe { self.0.as_ref() } - } -} - -#[test] -fn test_size_of_local() { - use crate::Value; - use std::mem::size_of; - assert_eq!(size_of::>(), size_of::<*const Value>()); - assert_eq!(size_of::>>(), size_of::<*const Value>()); -} diff --git a/tests/test_api.rs b/tests/test_api.rs index 5f1a84d2..0a9aa1a3 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -87,47 +87,38 @@ fn handle_scope_non_lexical_lifetime() { fn global_handles() { let _setup_guard = setup(); 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(); - let mut _g4 = v8::Global::::new(); - let g5 = v8::Global::::new(); - let mut g6 = v8::Global::::new(); + let g1: v8::Global; + let mut g2: Option> = None; + let g3: v8::Global; + let g4: v8::Global; + let mut g5: Option> = None; + let g6; { 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); - g2.set(scope, l2); - g3.set(scope, &g2); - _g4 = v8::Global::new_from(scope, l2); - let l6 = v8::Integer::new(scope, 100); - g6.set(scope, l6); + g1 = v8::Global::new(scope, l1); + g2.replace(v8::Global::new(scope, l2)); + g3 = v8::Global::new(scope, g2.as_ref().unwrap()); + g4 = v8::Global::new(scope, l2); + let l5 = v8::Integer::new(scope, 100); + g5.replace(v8::Global::new(scope, l5)); + g6 = g1.clone(); } { 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()); - assert_eq!(g2.get(scope).unwrap().value(), 123); - assert!(!g3.is_empty()); - assert_eq!(g3.get(scope).unwrap().value(), 123); - assert!(!_g4.is_empty()); - assert_eq!(_g4.get(scope).unwrap().value(), 123); - assert!(g5.is_empty()); - let num = g6.get(scope).unwrap(); - g6.reset(scope); - assert_eq!(num.value(), 100); + assert_eq!(g1.get(scope).to_rust_string_lossy(scope), "bla"); + assert_eq!(g2.as_ref().unwrap().get(scope).value(), 123); + assert_eq!(g3.get(scope).value(), 123); + assert_eq!(g4.get(scope).value(), 123); + { + let num = g5.as_ref().unwrap().get(scope); + assert_eq!(num.value(), 100); + } + g5.take(); + assert!(g6 == g1); + assert_eq!(g6.get(scope).to_rust_string_lossy(scope), "bla"); } - g1.reset(isolate); - assert!(g1.is_empty()); - g2.reset(isolate); - assert!(g2.is_empty()); - g3.reset(isolate); - assert!(g3.is_empty()); - _g4.reset(isolate); - assert!(_g4.is_empty()); - assert!(g5.is_empty()); } #[test] @@ -135,17 +126,17 @@ fn global_handle_drop() { let _setup_guard = setup(); // Global 'g1' will be dropped _after_ the Isolate has been disposed. - let mut g1 = v8::Global::::new(); + let _g1: v8::Global; 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); + _g1 = v8::Global::new(scope, l1); // Global 'g2' will be dropped _before_ the Isolate has been disposed. let l2 = v8::String::new(scope, "bar").unwrap(); - let _g2 = v8::Global::new_from(scope, l2); + let _g2 = v8::Global::new(scope, l2); } #[test] @@ -341,10 +332,10 @@ fn get_isolate_from_handle() { // Check that we can get the isolate from a 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(scope, expect_some, local); + // Check that we can still get it after converting it to a Global. + let global = v8::Global::new(scope, local); + let local2 = v8::Local::new(scope, &global); + check_handle_helper(scope, expect_some, local2); }; fn check_eval<'s>( @@ -1997,6 +1988,9 @@ fn value_checker() { assert!(value == value); assert!(value == v8::Local::::try_from(value).unwrap()); assert!(value == v8::null(scope)); + assert!(value == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == v8::null(scope)); assert!(value != v8::undefined(scope)); assert!(value != v8::Boolean::new(scope, false)); assert!(value != v8::Integer::new(scope, 0)); @@ -2008,6 +2002,10 @@ fn value_checker() { assert!(value == value); assert!(value == v8::Local::::try_from(value).unwrap()); assert!(value == v8::Boolean::new(scope, true)); + assert!(value == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == eval(scope, "!false").unwrap()); + assert!(v8::Global::new(scope, value) != eval(scope, "1").unwrap()); assert!(value != v8::Boolean::new(scope, false)); let value = eval(scope, "false").unwrap(); @@ -2017,6 +2015,10 @@ fn value_checker() { assert!(value == value); assert!(value == v8::Local::::try_from(value).unwrap()); assert!(value == v8::Boolean::new(scope, false)); + assert!(value == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == eval(scope, "!true").unwrap()); + assert!(v8::Global::new(scope, value) != eval(scope, "0").unwrap()); assert!(value != v8::Boolean::new(scope, true)); assert!(value != v8::null(scope)); assert!(value != v8::undefined(scope)); @@ -2036,22 +2038,27 @@ fn value_checker() { assert!(value.is_symbol()); assert!(value == value); assert!(value == v8::Local::::try_from(value).unwrap()); - assert!(value == v8::Global::new_from(scope, value).get(scope).unwrap()); + assert!(value == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == v8::Global::new(scope, value)); assert!(value != eval(scope, "Symbol()").unwrap()); + assert!(v8::Global::new(scope, value) != eval(scope, "Symbol()").unwrap()); let value = eval(scope, "() => 0").unwrap(); assert!(value.is_function()); assert!(value == value); assert!(value == v8::Local::::try_from(value).unwrap()); - assert!(value == v8::Global::new_from(scope, value).get(scope).unwrap()); + assert!(value == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == v8::Global::new(scope, value)); assert!(value != eval(scope, "() => 0").unwrap()); + assert!(v8::Global::new(scope, value) != eval(scope, "() => 0").unwrap()); let value = eval(scope, "async () => 0").unwrap(); assert!(value.is_async_function()); assert!(value == value); assert!(value == v8::Local::::try_from(value).unwrap()); - assert!(value == v8::Global::new_from(scope, value).get(scope).unwrap()); + assert!(v8::Global::new(scope, value) == v8::Global::new(scope, value)); assert!(value != v8::Object::new(scope)); + assert!(v8::Global::new(scope, value) != v8::Object::new(scope)); let value = eval(scope, "[]").unwrap(); assert!(value.is_array()); @@ -2128,8 +2135,10 @@ fn value_checker() { assert!(value.is_object()); assert!(value == value); assert!(value == v8::Local::::try_from(value).unwrap()); - assert!(value == v8::Global::new_from(scope, value).get(scope).unwrap()); + assert!(value == v8::Global::new(scope, value)); + assert!(v8::Global::new(scope, value) == v8::Global::new(scope, value)); assert!(value != v8::Object::new(scope)); + assert!(v8::Global::new(scope, value) != v8::Object::new(scope)); let value = eval(scope, "new Date()").unwrap(); assert!(value.is_date());