mirror of
https://github.com/denoland/rusty_v8.git
synced 2025-03-09 13:38:51 -04: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:
parent
850af2e857
commit
c13625148f
7 changed files with 453 additions and 343 deletions
|
@ -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));
|
return make_pod<v8::Data*>(std::move(global));
|
||||||
}
|
}
|
||||||
|
|
||||||
void v8__Global__Reset__0(const v8::Data*& self) {
|
void v8__Global__Reset(const v8::Data* data) {
|
||||||
auto global = ptr_to_global(self);
|
auto global = ptr_to_global(data);
|
||||||
global.Reset();
|
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(
|
void v8__ScriptCompiler__Source__CONSTRUCT(
|
||||||
|
|
37
src/data.rs
37
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 {
|
macro_rules! impl_partial_eq {
|
||||||
{ $rhs:ident for $type:ident use identity } => {
|
{ $rhs:ident for $type:ident use identity } => {
|
||||||
impl<'s> PartialEq<Local<'s, $rhs>> for Local<'s, $type> {
|
impl<'s> PartialEq<$rhs> for $type {
|
||||||
fn eq(&self, other: &Local<'s, $rhs>) -> bool {
|
fn eq(&self, other: &$rhs) -> bool {
|
||||||
self.eq_identity((*other).into())
|
unsafe {
|
||||||
|
v8__Data__EQ(
|
||||||
|
self as *const _ as *const Data,
|
||||||
|
other as *const _ as *const Data,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
{ $rhs:ident for $type:ident use strict_equals } => {
|
{ $rhs:ident for $type:ident use strict_equals } => {
|
||||||
impl<'s> PartialEq<Local<'s, $rhs>> for Local<'s, $type> {
|
impl<'s> PartialEq<$rhs> for $type {
|
||||||
fn eq(&self, other: &Local<'s, $rhs>) -> bool {
|
fn eq(&self, other: &$rhs) -> bool {
|
||||||
self.strict_equals((*other).into())
|
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)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct TryFromTypeError {
|
pub struct TryFromTypeError {
|
||||||
expected_type: &'static str,
|
expected_type: &'static str,
|
||||||
|
|
179
src/global.rs
179
src/global.rs
|
@ -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
373
src/handle.rs
Normal 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() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,10 +40,9 @@ mod data;
|
||||||
mod exception;
|
mod exception;
|
||||||
mod external_references;
|
mod external_references;
|
||||||
mod function;
|
mod function;
|
||||||
mod global;
|
mod handle;
|
||||||
mod isolate;
|
mod isolate;
|
||||||
mod isolate_create_params;
|
mod isolate_create_params;
|
||||||
mod local;
|
|
||||||
mod module;
|
mod module;
|
||||||
mod number;
|
mod number;
|
||||||
mod object;
|
mod object;
|
||||||
|
@ -78,7 +77,9 @@ pub use exception::*;
|
||||||
pub use external_references::ExternalReference;
|
pub use external_references::ExternalReference;
|
||||||
pub use external_references::ExternalReferences;
|
pub use external_references::ExternalReferences;
|
||||||
pub use function::*;
|
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::HostImportModuleDynamicallyCallback;
|
||||||
pub use isolate::HostInitializeImportMetaObjectCallback;
|
pub use isolate::HostInitializeImportMetaObjectCallback;
|
||||||
pub use isolate::Isolate;
|
pub use isolate::Isolate;
|
||||||
|
@ -87,7 +88,6 @@ pub use isolate::MessageCallback;
|
||||||
pub use isolate::OwnedIsolate;
|
pub use isolate::OwnedIsolate;
|
||||||
pub use isolate::PromiseRejectCallback;
|
pub use isolate::PromiseRejectCallback;
|
||||||
pub use isolate_create_params::CreateParams;
|
pub use isolate_create_params::CreateParams;
|
||||||
pub use local::Local;
|
|
||||||
pub use module::*;
|
pub use module::*;
|
||||||
pub use object::*;
|
pub use object::*;
|
||||||
pub use platform::new_default_platform;
|
pub use platform::new_default_platform;
|
||||||
|
|
90
src/local.rs
90
src/local.rs
|
@ -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>());
|
|
||||||
}
|
|
|
@ -87,47 +87,38 @@ fn handle_scope_non_lexical_lifetime() {
|
||||||
fn global_handles() {
|
fn global_handles() {
|
||||||
let _setup_guard = setup();
|
let _setup_guard = setup();
|
||||||
let isolate = &mut v8::Isolate::new(Default::default());
|
let isolate = &mut v8::Isolate::new(Default::default());
|
||||||
let mut g1 = v8::Global::<v8::String>::new();
|
let g1: v8::Global<v8::String>;
|
||||||
let mut g2 = v8::Global::<v8::Integer>::new();
|
let mut g2: Option<v8::Global<v8::Integer>> = None;
|
||||||
let mut g3 = v8::Global::<v8::Integer>::new();
|
let g3: v8::Global<v8::Integer>;
|
||||||
let mut _g4 = v8::Global::<v8::Integer>::new();
|
let g4: v8::Global<v8::Integer>;
|
||||||
let g5 = v8::Global::<v8::Script>::new();
|
let mut g5: Option<v8::Global<v8::Integer>> = None;
|
||||||
let mut g6 = v8::Global::<v8::Integer>::new();
|
let g6;
|
||||||
{
|
{
|
||||||
let scope = &mut v8::HandleScope::new(isolate);
|
let scope = &mut v8::HandleScope::new(isolate);
|
||||||
let l1 = v8::String::new(scope, "bla").unwrap();
|
let l1 = v8::String::new(scope, "bla").unwrap();
|
||||||
let l2 = v8::Integer::new(scope, 123);
|
let l2 = v8::Integer::new(scope, 123);
|
||||||
g1.set(scope, l1);
|
g1 = v8::Global::new(scope, l1);
|
||||||
g2.set(scope, l2);
|
g2.replace(v8::Global::new(scope, l2));
|
||||||
g3.set(scope, &g2);
|
g3 = v8::Global::new(scope, g2.as_ref().unwrap());
|
||||||
_g4 = v8::Global::new_from(scope, l2);
|
g4 = v8::Global::new(scope, l2);
|
||||||
let l6 = v8::Integer::new(scope, 100);
|
let l5 = v8::Integer::new(scope, 100);
|
||||||
g6.set(scope, l6);
|
g5.replace(v8::Global::new(scope, l5));
|
||||||
|
g6 = g1.clone();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let scope = &mut v8::HandleScope::new(isolate);
|
let scope = &mut v8::HandleScope::new(isolate);
|
||||||
assert!(!g1.is_empty());
|
assert_eq!(g1.get(scope).to_rust_string_lossy(scope), "bla");
|
||||||
assert_eq!(g1.get(scope).unwrap().to_rust_string_lossy(scope), "bla");
|
assert_eq!(g2.as_ref().unwrap().get(scope).value(), 123);
|
||||||
assert!(!g2.is_empty());
|
assert_eq!(g3.get(scope).value(), 123);
|
||||||
assert_eq!(g2.get(scope).unwrap().value(), 123);
|
assert_eq!(g4.get(scope).value(), 123);
|
||||||
assert!(!g3.is_empty());
|
{
|
||||||
assert_eq!(g3.get(scope).unwrap().value(), 123);
|
let num = g5.as_ref().unwrap().get(scope);
|
||||||
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!(num.value(), 100);
|
||||||
}
|
}
|
||||||
g1.reset(isolate);
|
g5.take();
|
||||||
assert!(g1.is_empty());
|
assert!(g6 == g1);
|
||||||
g2.reset(isolate);
|
assert_eq!(g6.get(scope).to_rust_string_lossy(scope), "bla");
|
||||||
assert!(g2.is_empty());
|
}
|
||||||
g3.reset(isolate);
|
|
||||||
assert!(g3.is_empty());
|
|
||||||
_g4.reset(isolate);
|
|
||||||
assert!(_g4.is_empty());
|
|
||||||
assert!(g5.is_empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -135,17 +126,17 @@ fn global_handle_drop() {
|
||||||
let _setup_guard = setup();
|
let _setup_guard = setup();
|
||||||
|
|
||||||
// Global 'g1' will be dropped _after_ the Isolate has been disposed.
|
// 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 isolate = &mut v8::Isolate::new(Default::default());
|
||||||
let scope = &mut v8::HandleScope::new(isolate);
|
let scope = &mut v8::HandleScope::new(isolate);
|
||||||
|
|
||||||
let l1 = v8::String::new(scope, "foo").unwrap();
|
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.
|
// Global 'g2' will be dropped _before_ the Isolate has been disposed.
|
||||||
let l2 = v8::String::new(scope, "bar").unwrap();
|
let l2 = v8::String::new(scope, "bar").unwrap();
|
||||||
let _g2 = v8::Global::new_from(scope, l2);
|
let _g2 = v8::Global::new(scope, l2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -341,10 +332,10 @@ fn get_isolate_from_handle() {
|
||||||
// Check that we can get the isolate from a Local.
|
// Check that we can get the isolate from a Local.
|
||||||
check_handle_helper(scope, 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.
|
// Check that we can still get it after converting it to a Global.
|
||||||
let global = v8::Global::new_from(scope, local);
|
let global = v8::Global::new(scope, local);
|
||||||
let local = global.get(scope).unwrap();
|
let local2 = v8::Local::new(scope, &global);
|
||||||
check_handle_helper(scope, expect_some, local);
|
check_handle_helper(scope, expect_some, local2);
|
||||||
};
|
};
|
||||||
|
|
||||||
fn check_eval<'s>(
|
fn check_eval<'s>(
|
||||||
|
@ -1997,6 +1988,9 @@ fn value_checker() {
|
||||||
assert!(value == value);
|
assert!(value == value);
|
||||||
assert!(value == v8::Local::<v8::Primitive>::try_from(value).unwrap());
|
assert!(value == v8::Local::<v8::Primitive>::try_from(value).unwrap());
|
||||||
assert!(value == v8::null(scope));
|
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::undefined(scope));
|
||||||
assert!(value != v8::Boolean::new(scope, false));
|
assert!(value != v8::Boolean::new(scope, false));
|
||||||
assert!(value != v8::Integer::new(scope, 0));
|
assert!(value != v8::Integer::new(scope, 0));
|
||||||
|
@ -2008,6 +2002,10 @@ fn value_checker() {
|
||||||
assert!(value == value);
|
assert!(value == value);
|
||||||
assert!(value == v8::Local::<v8::Boolean>::try_from(value).unwrap());
|
assert!(value == v8::Local::<v8::Boolean>::try_from(value).unwrap());
|
||||||
assert!(value == v8::Boolean::new(scope, true));
|
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));
|
assert!(value != v8::Boolean::new(scope, false));
|
||||||
|
|
||||||
let value = eval(scope, "false").unwrap();
|
let value = eval(scope, "false").unwrap();
|
||||||
|
@ -2017,6 +2015,10 @@ fn value_checker() {
|
||||||
assert!(value == value);
|
assert!(value == value);
|
||||||
assert!(value == v8::Local::<v8::Boolean>::try_from(value).unwrap());
|
assert!(value == v8::Local::<v8::Boolean>::try_from(value).unwrap());
|
||||||
assert!(value == v8::Boolean::new(scope, false));
|
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::Boolean::new(scope, true));
|
||||||
assert!(value != v8::null(scope));
|
assert!(value != v8::null(scope));
|
||||||
assert!(value != v8::undefined(scope));
|
assert!(value != v8::undefined(scope));
|
||||||
|
@ -2036,22 +2038,27 @@ fn value_checker() {
|
||||||
assert!(value.is_symbol());
|
assert!(value.is_symbol());
|
||||||
assert!(value == value);
|
assert!(value == value);
|
||||||
assert!(value == v8::Local::<v8::Symbol>::try_from(value).unwrap());
|
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!(value != eval(scope, "Symbol()").unwrap());
|
||||||
|
assert!(v8::Global::new(scope, value) != eval(scope, "Symbol()").unwrap());
|
||||||
|
|
||||||
let value = eval(scope, "() => 0").unwrap();
|
let value = eval(scope, "() => 0").unwrap();
|
||||||
assert!(value.is_function());
|
assert!(value.is_function());
|
||||||
assert!(value == value);
|
assert!(value == value);
|
||||||
assert!(value == v8::Local::<v8::Function>::try_from(value).unwrap());
|
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!(value != eval(scope, "() => 0").unwrap());
|
||||||
|
assert!(v8::Global::new(scope, value) != eval(scope, "() => 0").unwrap());
|
||||||
|
|
||||||
let value = eval(scope, "async () => 0").unwrap();
|
let value = eval(scope, "async () => 0").unwrap();
|
||||||
assert!(value.is_async_function());
|
assert!(value.is_async_function());
|
||||||
assert!(value == value);
|
assert!(value == value);
|
||||||
assert!(value == v8::Local::<v8::Function>::try_from(value).unwrap());
|
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!(value != v8::Object::new(scope));
|
||||||
|
assert!(v8::Global::new(scope, value) != v8::Object::new(scope));
|
||||||
|
|
||||||
let value = eval(scope, "[]").unwrap();
|
let value = eval(scope, "[]").unwrap();
|
||||||
assert!(value.is_array());
|
assert!(value.is_array());
|
||||||
|
@ -2128,8 +2135,10 @@ fn value_checker() {
|
||||||
assert!(value.is_object());
|
assert!(value.is_object());
|
||||||
assert!(value == value);
|
assert!(value == value);
|
||||||
assert!(value == v8::Local::<v8::Object>::try_from(value).unwrap());
|
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!(value != v8::Object::new(scope));
|
||||||
|
assert!(v8::Global::new(scope, value) != v8::Object::new(scope));
|
||||||
|
|
||||||
let value = eval(scope, "new Date()").unwrap();
|
let value = eval(scope, "new Date()").unwrap();
|
||||||
assert!(value.is_date());
|
assert!(value.is_date());
|
||||||
|
|
Loading…
Add table
Reference in a new issue