From 25c4f7f4d017f51acb5af98a37cbced4dfae57e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Sun, 22 Dec 2019 15:05:39 +0100 Subject: [PATCH] add v8::EscapableHandleScope (#113) --- src/binding.cc | 22 ++++++++++++++ src/handle_scope.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- src/number.rs | 7 ++--- src/string.rs | 5 ++-- tests/test_api.rs | 45 ++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 8 deletions(-) diff --git a/src/binding.cc b/src/binding.cc index 91cc61ac..de927bf7 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -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"); @@ -111,6 +114,25 @@ v8::Isolate* v8__HandleScope__GetIsolate(const v8::HandleScope& self) { return self.GetIsolate(); } +void v8__EscapableHandleScope__CONSTRUCT( + uninit_t& buf, v8::Isolate* isolate) { + construct_in_place(buf, isolate); +} + +void v8__EscapableHandleScope__DESTRUCT(v8::EscapableHandleScope& self) { + self.~EscapableHandleScope(); +} + +v8::Value* v8__EscapableHandleScope__Escape(v8::EscapableHandleScope& self, + v8::Local 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& buf, v8::Isolate* isolate) { construct_in_place(buf, isolate); } diff --git a/src/handle_scope.rs b/src/handle_scope.rs index 947f3689..77aa70c3 100644 --- a/src/handle_scope.rs +++ b/src/handle_scope.rs @@ -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( @@ -12,6 +14,19 @@ extern "C" { fn v8__HandleScope__GetIsolate<'sc>( this: &'sc HandleScope, ) -> &'sc mut Isolate; + + fn v8__EscapableHandleScope__CONSTRUCT( + buf: &mut MaybeUninit, + 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)] @@ -55,3 +70,61 @@ impl<'sc> AsMut 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) -> Self { + let isolate = isolate.as_mut(); + let mut scope: MaybeUninit = 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> for EscapableHandleScope<'sc> { + fn as_ref(&self) -> &Self { + self + } +} + +impl<'sc> AsMut> for EscapableHandleScope<'sc> { + fn as_mut(&mut self) -> &mut Self { + self + } +} + +impl<'sc> AsRef for EscapableHandleScope<'sc> { + fn as_ref(&self) -> &Isolate { + unsafe { v8__EscapableHandleScope__GetIsolate(self) } + } +} + +impl<'sc> AsMut for EscapableHandleScope<'sc> { + fn as_mut(&mut self) -> &mut Isolate { + unsafe { v8__EscapableHandleScope__GetIsolate(self) } + } +} diff --git a/src/lib.rs b/src/lib.rs index 9c1b2282..b3a9d826 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/number.rs b/src/number.rs index 8fd01921..a1c552ab 100644 --- a/src/number.rs +++ b/src/number.rs @@ -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" { @@ -23,7 +22,7 @@ pub struct Number(Opaque); impl Number { pub fn new<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl AsMut, value: f64, ) -> Local<'sc, Number> { unsafe { @@ -50,7 +49,7 @@ pub struct Integer(Opaque); impl Integer { pub fn new<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl AsMut, value: i32, ) -> Local<'sc, Integer> { unsafe { @@ -60,7 +59,7 @@ impl Integer { } pub fn new_from_unsigned<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl AsMut, value: u32, ) -> Local<'sc, Integer> { unsafe { diff --git a/src/string.rs b/src/string.rs index 47d610f5..5577515c 100644 --- a/src/string.rs +++ b/src/string.rs @@ -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; @@ -67,7 +66,7 @@ pub struct String(Opaque); impl String { pub fn new_from_utf8<'sc>( - scope: &mut HandleScope<'sc>, + scope: &mut impl AsMut, buffer: &[u8], new_type: NewStringType, ) -> Option> { @@ -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, value: &str, ) -> Option> { Self::new_from_utf8(scope, value.as_ref(), NewStringType::Normal) diff --git a/tests/test_api.rs b/tests/test_api.rs index d97ad800..a4dcb6df 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -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 = cast(v8::Number::new(&mut escapable_scope, 78.9)); + escapable_scope.escape(number) + }; + let number: Local = 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 = 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 = cast(str_val); + assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1)); + }); + drop(locker); + isolate.exit(); + drop(g); +} + #[test] fn array_buffer() { setup();