0
0
Fork 0
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:
Bert Belder 2019-12-21 18:17:03 +01:00
parent f36e74a648
commit c107eb871f
No known key found for this signature in database
GPG key ID: 7A77887B2E2ED461
6 changed files with 251 additions and 3 deletions

View file

@ -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
View 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
}
}

View file

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

View file

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

View file

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

View file

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