0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-12-25 08:39:15 -05:00

Refactor handle types 'Local' and 'Global' (#410)

* Merged all handle type implementations into one file ('handle.h').
* Made it so that `Global` handles cannot be empty.
* Renamed the `AsHandle` trait to `Handle`, and made it more generally
  useful.
* Simplified how `PartialEq` is implemented for V8 heap objects and/or
  the `Local`/`Global` handles that reference them.
This commit is contained in:
Bert Belder 2020-06-30 05:12:13 +02:00
parent 850af2e857
commit c13625148f
No known key found for this signature in database
GPG key ID: 7A77887B2E2ED461
7 changed files with 453 additions and 343 deletions

View file

@ -220,17 +220,9 @@ const v8::Data* v8__Global__New(v8::Isolate* isolate, const v8::Data& other) {
return make_pod<v8::Data*>(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<v8::Data*>(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<v8::Data*>(std::move(global));
}
void v8__ScriptCompiler__Source__CONSTRUCT(

View file

@ -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<Local<'s, $rhs>> 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<Local<'s, $rhs>> 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<Self>) -> bool {
unsafe { v8__Data__EQ(self, &*other) }
}
}
#[derive(Clone, Copy, Debug)]
pub struct TryFromTypeError {
expected_type: &'static str,

View file

@ -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<T> {
value: Option<NonNull<T>>,
isolate_handle: Option<IsolateHandle>,
}
impl<T> Global<T> {
/// 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<T>) -> 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<T> from this global handle.
pub fn get<'s>(
&self,
scope: &mut HandleScope<'s, ()>,
) -> Option<Local<'s, T>> {
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<T>) {
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<NonNull<T>> as *mut *const Data),
)
},
(target, source) => unsafe {
v8__Global__Reset__2(
&mut *(target as *mut Option<NonNull<T>> as *mut *const Data),
scope,
&*(source as *const Option<NonNull<T>> 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<T> Default for Global<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Drop for Global<T> {
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<NonNull<T>> as *mut *const Data),
)
},
}
}
}
pub trait AnyHandle<T> {
fn read(self, isolate: &mut Isolate) -> Option<NonNull<T>>;
}
impl<'s, T> AnyHandle<T> for Local<'s, T> {
fn read(self, _isolate: &mut Isolate) -> Option<NonNull<T>> {
Some(self.as_non_null())
}
}
impl<'s, T> AnyHandle<T> for Option<Local<'s, T>> {
fn read(self, _isolate: &mut Isolate) -> Option<NonNull<T>> {
self.map(|local| local.as_non_null())
}
}
impl<'s, T> AnyHandle<T> for &Global<T> {
fn read(self, isolate: &mut Isolate) -> Option<NonNull<T>> {
self.check_isolate(isolate);
self.value
}
}

373
src/handle.rs Normal file
View file

@ -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<Object>); 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<Local>.
#[repr(C)]
pub struct Local<'s, T>(NonNull<T>, 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<Data = T>,
) -> 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<A>(other: Local<'s, A>) -> Self
where
Local<'s, A>: From<Self>,
{
transmute(other)
}
pub(crate) unsafe fn from_raw(ptr: *const T) -> Option<Self> {
NonNull::new(ptr as *mut _).map(|nn| Self::from_non_null(nn))
}
pub(crate) unsafe fn from_non_null(nn: NonNull<T>) -> Self {
Self(nn, PhantomData)
}
pub(crate) fn as_non_null(self) -> NonNull<T> {
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<T> {
data: NonNull<T>,
isolate_handle: IsolateHandle,
}
impl<T> Global<T> {
/// Construct a new Global from an existing Handle.
pub fn new(isolate: &mut Isolate, handle: impl Handle<Data = T>) -> 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<T>) -> 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<T> Clone for Global<T> {
fn clone(&self) -> Self {
let HandleInfo { data, host } = self.get_handle_info();
unsafe { Self::new_raw(host.get_isolate().as_mut(), data) }
}
}
impl<T> Drop for Global<T> {
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<Self::Data>;
/// 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<T> {
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<T> {
HandleInfo::new(self.as_non_null(), HandleHost::Scope)
}
}
impl<T> Handle for Global<T> {
type Data = T;
fn get_handle_info(&self) -> HandleInfo<T> {
HandleInfo::new(self.data, (&self.isolate_handle).into())
}
}
impl<'a, T> Handle for &'a Global<T> {
type Data = T;
fn get_handle_info(&self) -> HandleInfo<T> {
HandleInfo::new(self.data, (&self.isolate_handle).into())
}
}
impl<'s, T, Rhs: Handle> PartialEq<Rhs> for Local<'s, T>
where
T: PartialEq<Rhs::Data>,
{
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<Rhs> for Global<T>
where
T: PartialEq<Rhs::Data>,
{
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<T> {
data: NonNull<T>,
host: HandleHost,
}
impl<T> HandleInfo<T> {
fn new(data: NonNull<T>, 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<Isolate>),
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<Isolate> {
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() }
}
}

View file

@ -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;

View file

@ -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<Object>); 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<Local>.
#[repr(C)]
pub struct Local<'s, T>(NonNull<T>, 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<A>(other: Local<'s, A>) -> Self
where
Local<'s, A>: From<Self>,
{
transmute(other)
}
pub(crate) unsafe fn from_raw(ptr: *const T) -> Option<Self> {
NonNull::new(ptr as *mut _).map(|nn| Self::from_non_null(nn))
}
pub(crate) unsafe fn from_non_null(nn: NonNull<T>) -> Self {
Self(nn, PhantomData)
}
pub(crate) fn as_non_null(self) -> NonNull<T> {
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::<Local<Value>>(), size_of::<*const Value>());
assert_eq!(size_of::<Option<Local<Value>>>(), size_of::<*const Value>());
}

View file

@ -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::<v8::String>::new();
let mut g2 = v8::Global::<v8::Integer>::new();
let mut g3 = v8::Global::<v8::Integer>::new();
let mut _g4 = v8::Global::<v8::Integer>::new();
let g5 = v8::Global::<v8::Script>::new();
let mut g6 = v8::Global::<v8::Integer>::new();
let g1: v8::Global<v8::String>;
let mut g2: Option<v8::Global<v8::Integer>> = None;
let g3: v8::Global<v8::Integer>;
let g4: v8::Global<v8::Integer>;
let mut g5: Option<v8::Global<v8::Integer>> = 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!(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);
}
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());
g5.take();
assert!(g6 == g1);
assert_eq!(g6.get(scope).to_rust_string_lossy(scope), "bla");
}
}
#[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::<v8::String>::new();
let _g1: v8::Global<v8::String>;
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::<v8::Primitive>::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::<v8::Boolean>::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::<v8::Boolean>::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::<v8::Symbol>::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::<v8::Function>::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::<v8::Function>::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::<v8::Object>::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());