mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-12-27 01:29:19 -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(); }
|
||||
|
||||
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(
|
||||
uninit_t<v8::ScriptCompiler::Source>& buf, v8::String* source_string,
|
||||
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 exception;
|
||||
mod function;
|
||||
mod global;
|
||||
mod handle_scope;
|
||||
mod isolate;
|
||||
mod local;
|
||||
|
@ -48,6 +49,7 @@ pub use exception::*;
|
|||
pub use function::{
|
||||
Function, FunctionCallbackInfo, FunctionTemplate, ReturnValue,
|
||||
};
|
||||
pub use global::Global;
|
||||
pub use handle_scope::HandleScope;
|
||||
pub use isolate::Isolate;
|
||||
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::ptr::NonNull;
|
||||
|
||||
#[repr(C)]
|
||||
/// An object reference managed by the v8 garbage collector.
|
||||
///
|
||||
/// 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
|
||||
/// never empty. In situations where empty handles are needed, use
|
||||
/// Option<Local>.
|
||||
#[repr(C)]
|
||||
pub struct Local<'sc, T>(NonNull<T>, PhantomData<&'sc ()>);
|
||||
|
||||
impl<'sc, T> Copy for Local<'sc, T> {}
|
||||
|
||||
impl<'sc, T> Clone for Local<'sc, T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0, self.1)
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
pub(crate) fn as_non_null(self) -> NonNull<T> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sc, T> Deref for Local<'sc, T> {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef SUPPORT_H_
|
||||
#define SUPPORT_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#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*), "");
|
||||
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
|
||||
|
||||
#endif // SUPPORT_H_
|
||||
|
|
|
@ -76,6 +76,48 @@ fn handle_scope_numbers() {
|
|||
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]
|
||||
fn test_string() {
|
||||
setup();
|
||||
|
|
Loading…
Reference in a new issue