Skip to content

Commit

Permalink
add v8::EscapableHandleScope (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartlomieju committed Dec 22, 2019
1 parent c107eb8 commit 25c4f7f
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 8 deletions.
22 changes: 22 additions & 0 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ static_assert(sizeof(v8::ScriptOrigin) == sizeof(size_t) * 7,
static_assert(sizeof(v8::HandleScope) == sizeof(size_t) * 3,
"HandleScope size mismatch");

static_assert(sizeof(v8::EscapableHandleScope) == sizeof(size_t) * 4,
"EscapableHandleScope size mismatch");

static_assert(sizeof(v8::PromiseRejectMessage) == sizeof(size_t) * 3,
"PromiseRejectMessage size mismatch");

Expand Down Expand Up @@ -111,6 +114,25 @@ v8::Isolate* v8__HandleScope__GetIsolate(const v8::HandleScope& self) {
return self.GetIsolate();
}

void v8__EscapableHandleScope__CONSTRUCT(
uninit_t<v8::EscapableHandleScope>& buf, v8::Isolate* isolate) {
construct_in_place<v8::EscapableHandleScope>(buf, isolate);
}

void v8__EscapableHandleScope__DESTRUCT(v8::EscapableHandleScope& self) {
self.~EscapableHandleScope();
}

v8::Value* v8__EscapableHandleScope__Escape(v8::EscapableHandleScope& self,
v8::Local<v8::Value> value) {
return local_to_ptr(self.Escape(value));
}

v8::Isolate* v8__EscapableHandleScope__GetIsolate(
const v8::EscapableHandleScope& self) {
return self.GetIsolate();
}

void v8__Locker__CONSTRUCT(uninit_t<v8::Locker>& buf, v8::Isolate* isolate) {
construct_in_place<v8::Locker>(buf, isolate);
}
Expand Down
73 changes: 73 additions & 0 deletions src/handle_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::marker::PhantomData;
use std::mem::MaybeUninit;

use crate::isolate::Isolate;
use crate::Local;
use crate::Value;

extern "C" {
fn v8__HandleScope__CONSTRUCT(
Expand All @@ -12,6 +14,19 @@ extern "C" {
fn v8__HandleScope__GetIsolate<'sc>(
this: &'sc HandleScope,
) -> &'sc mut Isolate;

fn v8__EscapableHandleScope__CONSTRUCT(
buf: &mut MaybeUninit<EscapableHandleScope>,
isolate: &Isolate,
);
fn v8__EscapableHandleScope__DESTRUCT(this: &mut EscapableHandleScope);
fn v8__EscapableHandleScope__Escape(
this: &mut EscapableHandleScope,
value: *mut Value,
) -> *mut Value;
fn v8__EscapableHandleScope__GetIsolate<'sc>(
this: &'sc EscapableHandleScope,
) -> &'sc mut Isolate;
}

#[repr(C)]
Expand Down Expand Up @@ -55,3 +70,61 @@ impl<'sc> AsMut<Isolate> for HandleScope<'sc> {
unsafe { v8__HandleScope__GetIsolate(self) }
}
}

#[repr(C)]
/// A HandleScope which first allocates a handle in the current scope
/// which will be later filled with the escape value.
pub struct EscapableHandleScope<'sc>([usize; 4], PhantomData<&'sc mut ()>);

impl<'sc> EscapableHandleScope<'sc> {
pub fn new(isolate: &mut impl AsMut<Isolate>) -> Self {
let isolate = isolate.as_mut();
let mut scope: MaybeUninit<Self> = MaybeUninit::uninit();
unsafe {
v8__EscapableHandleScope__CONSTRUCT(&mut scope, isolate);
scope.assume_init()
}
}

/// Pushes the value into the previous scope and returns a handle to it.
/// Cannot be called twice.
pub fn escape<'parent>(
&mut self,
mut value: Local<'sc, Value>,
) -> Local<'parent, Value> {
unsafe {
Local::from_raw(v8__EscapableHandleScope__Escape(self, &mut *value))
.unwrap()
}
}
}

impl<'sc> Drop for EscapableHandleScope<'sc> {
fn drop(&mut self) {
unsafe { v8__EscapableHandleScope__DESTRUCT(self) }
}
}

impl<'sc> AsRef<EscapableHandleScope<'sc>> for EscapableHandleScope<'sc> {
fn as_ref(&self) -> &Self {
self
}
}

impl<'sc> AsMut<EscapableHandleScope<'sc>> for EscapableHandleScope<'sc> {
fn as_mut(&mut self) -> &mut Self {
self
}
}

impl<'sc> AsRef<Isolate> for EscapableHandleScope<'sc> {
fn as_ref(&self) -> &Isolate {
unsafe { v8__EscapableHandleScope__GetIsolate(self) }
}
}

impl<'sc> AsMut<Isolate> for EscapableHandleScope<'sc> {
fn as_mut(&mut self) -> &mut Isolate {
unsafe { v8__EscapableHandleScope__GetIsolate(self) }
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub use function::{
Function, FunctionCallbackInfo, FunctionTemplate, ReturnValue,
};
pub use global::Global;
pub use handle_scope::HandleScope;
pub use handle_scope::{EscapableHandleScope, HandleScope};
pub use isolate::Isolate;
pub use isolate::OwnedIsolate;
pub use local::Local;
Expand Down
7 changes: 3 additions & 4 deletions src/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::ops::Deref;
use crate::isolate::Isolate;
use crate::support::Opaque;
use crate::value::Value;
use crate::HandleScope;
use crate::Local;

extern "C" {
Expand All @@ -23,7 +22,7 @@ pub struct Number(Opaque);

impl Number {
pub fn new<'sc>(
scope: &mut HandleScope<'sc>,
scope: &mut impl AsMut<Isolate>,
value: f64,
) -> Local<'sc, Number> {
unsafe {
Expand All @@ -50,7 +49,7 @@ pub struct Integer(Opaque);

impl Integer {
pub fn new<'sc>(
scope: &mut HandleScope<'sc>,
scope: &mut impl AsMut<Isolate>,
value: i32,
) -> Local<'sc, Integer> {
unsafe {
Expand All @@ -60,7 +59,7 @@ impl Integer {
}

pub fn new_from_unsigned<'sc>(
scope: &mut HandleScope<'sc>,
scope: &mut impl AsMut<Isolate>,
value: u32,
) -> Local<'sc, Integer> {
unsafe {
Expand Down
5 changes: 2 additions & 3 deletions src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::isolate::Isolate;
use crate::support::char;
use crate::support::int;
use crate::support::Opaque;
use crate::HandleScope;
use crate::Local;
use crate::Value;

Expand Down Expand Up @@ -67,7 +66,7 @@ pub struct String(Opaque);

impl String {
pub fn new_from_utf8<'sc>(
scope: &mut HandleScope<'sc>,
scope: &mut impl AsMut<Isolate>,
buffer: &[u8],
new_type: NewStringType,
) -> Option<Local<'sc, String>> {
Expand Down Expand Up @@ -119,7 +118,7 @@ impl String {

// Convenience function not present in the original V8 API.
pub fn new<'sc>(
scope: &mut HandleScope<'sc>,
scope: &mut impl AsMut<Isolate>,
value: &str,
) -> Option<Local<'sc, String>> {
Self::new_from_utf8(scope, value.as_ref(), NewStringType::Normal)
Expand Down
45 changes: 45 additions & 0 deletions tests/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,51 @@ fn test_string() {
drop(locker);
}

#[test]
#[allow(clippy::float_cmp)]
fn escapable_handle_scope() {
let g = setup();
let mut params = v8::Isolate::create_params();
params.set_array_buffer_allocator(v8::Allocator::new_default_allocator());
let mut isolate = v8::Isolate::new(params);
let mut locker = v8::Locker::new(&isolate);
isolate.enter();
v8::HandleScope::enter(&mut locker, |scope1| {
// After dropping EscapableHandleScope, we should be able to
// read escaped values.
let number_val = {
let mut escapable_scope = v8::EscapableHandleScope::new(scope1);
let number: Local<v8::Value> = cast(v8::Number::new(&mut escapable_scope, 78.9));
escapable_scope.escape(number)
};
let number: Local<v8::Number> = cast(number_val);
assert_eq!(number.value(), 78.9);

let str_val = {
let mut escapable_scope = v8::EscapableHandleScope::new(scope1);
let string = v8::String::new(&mut escapable_scope, "Hello 🦕 world!").unwrap();
escapable_scope.escape(cast(string))
};
let string: Local<v8::String> = cast(str_val);
assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1));

let str_val = {
let mut escapable_scope = v8::EscapableHandleScope::new(scope1);
let nested_str_val = {
let mut nested_escapable_scope = v8::EscapableHandleScope::new(&mut escapable_scope);
let string = v8::String::new(&mut nested_escapable_scope, "Hello 🦕 world!").unwrap();
nested_escapable_scope.escape(cast(string))
};
escapable_scope.escape(nested_str_val)
};
let string: Local<v8::String> = cast(str_val);
assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1));
});
drop(locker);
isolate.exit();
drop(g);
}

#[test]
fn array_buffer() {
setup();
Expand Down

0 comments on commit 25c4f7f

Please sign in to comment.