mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-11-25 15:29:43 -05:00
Add global (a.k.a Persistent) handles (#112)
This commit is contained in:
parent
f36e74a648
commit
c107eb871f
6 changed files with 251 additions and 3 deletions
|
@ -117,6 +117,28 @@ void v8__Locker__CONSTRUCT(uninit_t<v8::Locker>& buf, v8::Isolate* isolate) {
|
||||||
|
|
||||||
void v8__Locker__DESTRUCT(v8::Locker& self) { self.~Locker(); }
|
void v8__Locker__DESTRUCT(v8::Locker& self) { self.~Locker(); }
|
||||||
|
|
||||||
|
v8::Value* v8__Local__New(v8::Isolate* isolate, v8::Value* other) {
|
||||||
|
return local_to_ptr(v8::Local<v8::Value>::New(isolate, ptr_to_local(other)));
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Value* v8__Global__New(v8::Isolate* isolate, v8::Value* other) {
|
||||||
|
auto global = v8::Global<v8::Value>(isolate, ptr_to_local(other));
|
||||||
|
return global_to_ptr(global);
|
||||||
|
}
|
||||||
|
|
||||||
|
void v8__Global__Reset__0(v8::Value*& self) {
|
||||||
|
auto global = ptr_to_global(self);
|
||||||
|
global.Reset();
|
||||||
|
self = global_to_ptr(global);
|
||||||
|
}
|
||||||
|
|
||||||
|
void v8__Global__Reset__2(v8::Value*& self, v8::Isolate* isolate,
|
||||||
|
v8::Value* const& other) {
|
||||||
|
auto global = ptr_to_global(self);
|
||||||
|
global.Reset(isolate, ptr_to_local(other));
|
||||||
|
self = global_to_ptr(global);
|
||||||
|
}
|
||||||
|
|
||||||
void v8__ScriptCompiler__Source__CONSTRUCT(
|
void v8__ScriptCompiler__Source__CONSTRUCT(
|
||||||
uninit_t<v8::ScriptCompiler::Source>& buf, v8::String* source_string,
|
uninit_t<v8::ScriptCompiler::Source>& buf, v8::String* source_string,
|
||||||
v8::ScriptOrigin& origin) {
|
v8::ScriptOrigin& origin) {
|
||||||
|
|
161
src/global.rs
Normal file
161
src/global.rs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
use std::mem::transmute;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use crate::HandleScope;
|
||||||
|
use crate::Isolate;
|
||||||
|
use crate::Local;
|
||||||
|
use crate::Value;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn v8__Local__New(isolate: *mut Isolate, other: *mut Value) -> *mut Value;
|
||||||
|
|
||||||
|
fn v8__Global__New(isolate: *mut Isolate, other: *mut Value) -> *mut Value;
|
||||||
|
|
||||||
|
fn v8__Global__Reset__0(this: &mut *mut Value);
|
||||||
|
|
||||||
|
fn v8__Global__Reset__2(
|
||||||
|
this: &mut *mut Value,
|
||||||
|
isolate: *mut Isolate,
|
||||||
|
other: &*mut Value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Global<T> {
|
||||||
|
value: Option<NonNull<T>>,
|
||||||
|
isolate: Option<NonNull<Isolate>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T> Send for Global<T> {}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
impl<T> Global<T> {
|
||||||
|
/// Construct a Global with no storage cell.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
value: None,
|
||||||
|
isolate: 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(
|
||||||
|
isolate: &mut impl AsMut<Isolate>,
|
||||||
|
other: impl AnyHandle<T>,
|
||||||
|
) -> Self {
|
||||||
|
let isolate = isolate.as_mut();
|
||||||
|
let other_value = other.read(isolate);
|
||||||
|
Self {
|
||||||
|
value: other_value
|
||||||
|
.map(|v| unsafe { transmute(v8__Global__New(isolate, transmute(v))) }),
|
||||||
|
isolate: other_value.map(|_| isolate.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<'sc>(
|
||||||
|
&self,
|
||||||
|
scope: &'_ mut (impl AsMut<Isolate> + AsMut<HandleScope<'sc>>),
|
||||||
|
) -> Option<Local<'sc, T>> {
|
||||||
|
self.check_isolate(scope.as_mut());
|
||||||
|
match &self.value {
|
||||||
|
None => None,
|
||||||
|
Some(p) => unsafe { Local::from_raw(p.as_ptr()) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If non-empty, destroy the underlying storage cell
|
||||||
|
/// and create a new one with the contents of other if other is non empty.
|
||||||
|
pub fn set(
|
||||||
|
&mut self,
|
||||||
|
isolate: &mut impl AsMut<Isolate>,
|
||||||
|
other: impl AnyHandle<T>,
|
||||||
|
) {
|
||||||
|
let isolate = isolate.as_mut();
|
||||||
|
self.check_isolate(isolate);
|
||||||
|
let other_value = other.read(isolate);
|
||||||
|
match (&mut self.value, &other_value) {
|
||||||
|
(None, None) => {}
|
||||||
|
(target, None) => unsafe {
|
||||||
|
v8__Global__Reset__0(
|
||||||
|
&mut *(target as *mut Option<NonNull<T>> as *mut *mut Value),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(target, source) => unsafe {
|
||||||
|
v8__Global__Reset__2(
|
||||||
|
&mut *(target as *mut Option<NonNull<T>> as *mut *mut Value),
|
||||||
|
isolate,
|
||||||
|
&*(source as *const Option<NonNull<T>> as *const *mut Value),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self.isolate = other_value.map(|_| isolate.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If non-empty, destroy the underlying storage cell
|
||||||
|
/// IsEmpty() will return true after this call.
|
||||||
|
pub fn reset(&mut self, isolate: &mut impl AsMut<Isolate>) {
|
||||||
|
self.set(isolate, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_isolate(&self, other: &Isolate) {
|
||||||
|
match self.value {
|
||||||
|
None => assert_eq!(self.isolate, None),
|
||||||
|
Some(_) => assert_eq!(self.isolate.unwrap(), other.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Global<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for Global<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.is_empty() {
|
||||||
|
panic!("Global handle dropped while holding a value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AnyHandle<T> {
|
||||||
|
fn read(self, isolate: &Isolate) -> Option<NonNull<T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'sc, T> AnyHandle<T> for Local<'sc, T> {
|
||||||
|
fn read(self, _isolate: &Isolate) -> Option<NonNull<T>> {
|
||||||
|
Some(self.as_non_null())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'sc, T> AnyHandle<T> for Option<Local<'sc, T>> {
|
||||||
|
fn read(self, _isolate: &Isolate) -> Option<NonNull<T>> {
|
||||||
|
self.map(|local| local.as_non_null())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'sc, T> AnyHandle<T> for &Global<T> {
|
||||||
|
fn read(self, isolate: &Isolate) -> Option<NonNull<T>> {
|
||||||
|
self.check_isolate(isolate);
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ mod array_buffer;
|
||||||
mod context;
|
mod context;
|
||||||
mod exception;
|
mod exception;
|
||||||
mod function;
|
mod function;
|
||||||
|
mod global;
|
||||||
mod handle_scope;
|
mod handle_scope;
|
||||||
mod isolate;
|
mod isolate;
|
||||||
mod local;
|
mod local;
|
||||||
|
@ -48,6 +49,7 @@ pub use exception::*;
|
||||||
pub use function::{
|
pub use function::{
|
||||||
Function, FunctionCallbackInfo, FunctionTemplate, ReturnValue,
|
Function, FunctionCallbackInfo, FunctionTemplate, ReturnValue,
|
||||||
};
|
};
|
||||||
|
pub use global::Global;
|
||||||
pub use handle_scope::HandleScope;
|
pub use handle_scope::HandleScope;
|
||||||
pub use isolate::Isolate;
|
pub use isolate::Isolate;
|
||||||
pub use isolate::OwnedIsolate;
|
pub use isolate::OwnedIsolate;
|
||||||
|
|
10
src/local.rs
10
src/local.rs
|
@ -4,7 +4,6 @@ use std::ops::Deref;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
/// An object reference managed by the v8 garbage collector.
|
/// An object reference managed by the v8 garbage collector.
|
||||||
///
|
///
|
||||||
/// All objects returned from v8 have to be tracked by the garbage
|
/// All objects returned from v8 have to be tracked by the garbage
|
||||||
|
@ -38,20 +37,25 @@ use std::ptr::NonNull;
|
||||||
/// Note: Local handles in Rusty V8 differ from the V8 C++ API in that they are
|
/// 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
|
/// never empty. In situations where empty handles are needed, use
|
||||||
/// Option<Local>.
|
/// Option<Local>.
|
||||||
|
#[repr(C)]
|
||||||
pub struct Local<'sc, T>(NonNull<T>, PhantomData<&'sc ()>);
|
pub struct Local<'sc, T>(NonNull<T>, PhantomData<&'sc ()>);
|
||||||
|
|
||||||
impl<'sc, T> Copy for Local<'sc, T> {}
|
impl<'sc, T> Copy for Local<'sc, T> {}
|
||||||
|
|
||||||
impl<'sc, T> Clone for Local<'sc, T> {
|
impl<'sc, T> Clone for Local<'sc, T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self(self.0, self.1)
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'sc, T> Local<'sc, T> {
|
impl<'sc, T> Local<'sc, T> {
|
||||||
pub unsafe fn from_raw(ptr: *mut T) -> Option<Self> {
|
pub(crate) unsafe fn from_raw(ptr: *mut T) -> Option<Self> {
|
||||||
Some(Self(NonNull::new(ptr)?, PhantomData))
|
Some(Self(NonNull::new(ptr)?, PhantomData))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_non_null(self) -> NonNull<T> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'sc, T> Deref for Local<'sc, T> {
|
impl<'sc, T> Deref for Local<'sc, T> {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef SUPPORT_H_
|
#ifndef SUPPORT_H_
|
||||||
#define SUPPORT_H_
|
#define SUPPORT_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
@ -70,6 +71,22 @@ inline static v8::MaybeLocal<T> ptr_to_maybe_local(T* ptr) {
|
||||||
static_assert(sizeof(v8::MaybeLocal<T>) == sizeof(T*), "");
|
static_assert(sizeof(v8::MaybeLocal<T>) == sizeof(T*), "");
|
||||||
return *reinterpret_cast<v8::MaybeLocal<T>*>(&ptr);
|
return *reinterpret_cast<v8::MaybeLocal<T>*>(&ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline static T* global_to_ptr(v8::Global<T>& global) {
|
||||||
|
static_assert(sizeof(v8::Global<T>) == sizeof(T*), "");
|
||||||
|
T* ptr = nullptr;
|
||||||
|
std::swap(ptr, reinterpret_cast<T*&>(global));
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline static v8::Global<T> ptr_to_global(T* ptr) {
|
||||||
|
v8::Global<T> global;
|
||||||
|
std::swap(ptr, *reinterpret_cast<T**>(&global));
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace support
|
} // namespace support
|
||||||
|
|
||||||
#endif // SUPPORT_H_
|
#endif // SUPPORT_H_
|
||||||
|
|
|
@ -76,6 +76,48 @@ fn handle_scope_numbers() {
|
||||||
drop(g);
|
drop(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn global_handles() {
|
||||||
|
let _g = setup();
|
||||||
|
let mut params = v8::Isolate::create_params();
|
||||||
|
params.set_array_buffer_allocator(v8::Allocator::new_default_allocator());
|
||||||
|
let isolate = v8::Isolate::new(params);
|
||||||
|
let mut locker = v8::Locker::new(&isolate);
|
||||||
|
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();
|
||||||
|
v8::HandleScope::enter(&mut locker, |scope| {
|
||||||
|
let l1 = v8::String::new(scope, "bla").unwrap();
|
||||||
|
let l2 = v8::Integer::new(scope, 123);
|
||||||
|
g1.set(scope, l1);
|
||||||
|
g2.set(scope, l2);
|
||||||
|
g3.set(scope, &g2);
|
||||||
|
g4 = v8::Global::new_from(scope, l2);
|
||||||
|
});
|
||||||
|
v8::HandleScope::enter(&mut locker, |scope| {
|
||||||
|
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());
|
||||||
|
});
|
||||||
|
g1.reset(&mut locker);
|
||||||
|
assert!(g1.is_empty());
|
||||||
|
g2.reset(&mut locker);
|
||||||
|
assert!(g2.is_empty());
|
||||||
|
g3.reset(&mut locker);
|
||||||
|
assert!(g3.is_empty());
|
||||||
|
g4.reset(&mut locker);
|
||||||
|
assert!(g4.is_empty());
|
||||||
|
assert!(g5.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string() {
|
fn test_string() {
|
||||||
setup();
|
setup();
|
||||||
|
|
Loading…
Reference in a new issue