0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2025-01-11 08:34:01 -05:00

Add APIs to store/retrieve embedder data in a Context or Isolate snapshot (#449)

Co-authored-by: Bert Belder <bertbelder@gmail.com>
This commit is contained in:
devsnek 2020-09-09 20:39:49 -04:00 committed by GitHub
parent 888e5deea1
commit 405a5a5158
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 264 additions and 109 deletions

View file

@ -153,6 +153,11 @@ uint32_t v8__Isolate__GetNumberOfDataSlots(v8::Isolate* isolate) {
return SLOT_NUM_EXTERNAL(isolate);
}
const v8::Data* v8__Isolate__GetDataFromSnapshotOnce(v8::Isolate* isolate,
size_t index) {
return maybe_local_to_ptr(isolate->GetDataFromSnapshotOnce<v8::Data>(index));
}
v8::MicrotasksPolicy v8__Isolate__GetMicrotasksPolicy(
const v8::Isolate* isolate) {
return isolate->GetMicrotasksPolicy();
@ -296,11 +301,7 @@ const v8::Module* v8__ScriptCompiler__CompileModule(
v8::ScriptCompiler::NoCacheReason no_cache_reason) {
v8::MaybeLocal<v8::Module> maybe_local = v8::ScriptCompiler::CompileModule(
isolate, source, options, no_cache_reason);
if (maybe_local.IsEmpty()) {
return nullptr;
} else {
return local_to_ptr(maybe_local.ToLocalChecked());
}
return maybe_local_to_ptr(maybe_local);
}
bool v8__Data__EQ(const v8::Data& self, const v8::Data& other) {
@ -1012,6 +1013,12 @@ const v8::Object* v8__Context__Global(const v8::Context& self) {
return local_to_ptr(ptr_to_local(&self)->Global());
}
const v8::Data* v8__Context__GetDataFromSnapshotOnce(v8::Context& self,
size_t index) {
return maybe_local_to_ptr(
ptr_to_local(&self)->GetDataFromSnapshotOnce<v8::Data>(index));
}
const v8::String* v8__Message__Get(const v8::Message& self) {
return local_to_ptr(self.Get());
}
@ -1519,6 +1526,17 @@ void v8__SnapshotCreator__SetDefaultContext(v8::SnapshotCreator* self,
self->SetDefaultContext(ptr_to_local(&context), SerializeInternalFields);
}
size_t v8__SnapshotCreator__AddData_to_isolate(v8::SnapshotCreator* self,
const v8::Data& data) {
return self->AddData(ptr_to_local(&data));
}
size_t v8__SnapshotCreator__AddData_to_context(v8::SnapshotCreator* self,
const v8::Context& context,
const v8::Data& data) {
return self->AddData(ptr_to_local(&context), ptr_to_local(&data));
}
v8::StartupData v8__SnapshotCreator__CreateBlob(
v8::SnapshotCreator* self,
v8::SnapshotCreator::FunctionCodeHandling function_code_handling) {

View file

@ -1,5 +1,6 @@
// Copyright 2019-2020 the Deno authors. All rights reserved. MIT license.
use std::any::type_name;
use std::convert::From;
use std::convert::TryFrom;
use std::error::Error;
@ -89,11 +90,11 @@ macro_rules! impl_from {
macro_rules! impl_try_from {
{ $source:ident for $target:ident if $value:pat => $check:expr } => {
impl<'s> TryFrom<Local<'s, $source>> for Local<'s, $target> {
type Error = TryFromTypeError;
type Error = DataError;
fn try_from(l: Local<'s, $source>) -> Result<Self, Self::Error> {
match l {
$value if $check => Ok(unsafe { transmute(l) }),
_ => Err(TryFromTypeError::new(stringify!($target)))
_ => Err(DataError::bad_type::<$target, $source>())
}
}
}
@ -152,23 +153,45 @@ macro_rules! impl_partial_eq {
}
#[derive(Clone, Copy, Debug)]
pub struct TryFromTypeError {
expected_type: &'static str,
pub enum DataError {
BadType {
actual: &'static str,
expected: &'static str,
},
NoData {
expected: &'static str,
},
}
impl TryFromTypeError {
fn new(expected_type: &'static str) -> Self {
Self { expected_type }
impl DataError {
pub(crate) fn bad_type<E: 'static, A: 'static>() -> Self {
Self::BadType {
expected: type_name::<E>(),
actual: type_name::<A>(),
}
}
pub(crate) fn no_data<E: 'static>() -> Self {
Self::NoData {
expected: type_name::<E>(),
}
}
}
impl Display for TryFromTypeError {
impl Display for DataError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} expected", self.expected_type)
match self {
Self::BadType { expected, actual } => {
write!(f, "expected type `{}`, got `{}`", expected, actual)
}
Self::NoData { expected } => {
write!(f, "expected `Some({})`, found `None`", expected)
}
}
}
}
impl Error for TryFromTypeError {}
impl Error for DataError {}
/// The superclass of objects that can reside on V8's heap.
#[repr(C)]
@ -587,6 +610,11 @@ impl_partial_eq! { UnboundScript for UnboundScript use identity }
pub struct Value(Opaque);
impl_deref! { Data for Value }
// TODO: Also implement `TryFrom<Data>` for all subtypes of `Value`,
// so a `Local<Data>` can be directly cast to any `Local` with a JavaScript
// value type in it. We need this to make certain APIs work, such as
// `scope.get_context_data_from_snapshot_once::<v8::Number>()` and
// `scope.get_isolate_data_from_snapshot_once::<v8::Number>()`.
impl_try_from! { Data for Value if d => d.is_value() }
impl_from! { External for Value }
impl_from! { Object for Value }

View file

@ -1,12 +1,12 @@
use std::convert::TryFrom;
use std::marker::PhantomData;
use crate::scope::CallbackScope;
use crate::support::MapFnFrom;
use crate::support::MapFnTo;
use crate::support::ToCFn;
use crate::support::UnitType;
use crate::support::{int, Opaque};
use crate::CallbackScope;
use crate::Context;
use crate::Function;
use crate::HandleScope;

View file

@ -81,6 +81,8 @@ use std::alloc::alloc;
use std::alloc::Layout;
use std::any::type_name;
use std::cell::Cell;
use std::convert::TryInto;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::num::NonZeroUsize;
@ -93,6 +95,7 @@ use crate::function::FunctionCallbackInfo;
use crate::function::PropertyCallbackInfo;
use crate::Context;
use crate::Data;
use crate::DataError;
use crate::Handle;
use crate::Isolate;
use crate::Local;
@ -224,6 +227,61 @@ impl<'s> HandleScope<'s, ()> {
}
}
impl<'s> HandleScope<'s> {
/// Return data that was previously attached to the isolate snapshot via
/// SnapshotCreator, and removes the reference to it. If called again with
/// same `index` argument, this function returns `DataError::NoData`.
///
/// The value that was stored in the snapshot must either match or be
/// convertible to type parameter `T`, otherwise `DataError::BadType` is
/// returned.
pub fn get_isolate_data_from_snapshot_once<T>(
&mut self,
index: usize,
) -> Result<Local<'s, T>, DataError>
where
T: 'static,
for<'l> Local<'l, Data>: TryInto<Local<'l, T>, Error = DataError>,
{
unsafe {
self
.cast_local(|sd| {
raw::v8__Isolate__GetDataFromSnapshotOnce(sd.get_isolate_ptr(), index)
})
.ok_or_else(DataError::no_data::<T>)
.and_then(|data| data.try_into())
}
}
/// Return data that was previously attached to the context snapshot via
/// SnapshotCreator, and removes the reference to it. If called again with
/// same `index` argument, this function returns `DataError::NoData`.
///
/// The value that was stored in the snapshot must either match or be
/// convertible to type parameter `T`, otherwise `DataError::BadType` is
/// returned.
pub fn get_context_data_from_snapshot_once<T>(
&mut self,
index: usize,
) -> Result<Local<'s, T>, DataError>
where
T: 'static,
for<'l> Local<'l, Data>: TryInto<Local<'l, T>, Error = DataError>,
{
unsafe {
self
.cast_local(|sd| {
raw::v8__Context__GetDataFromSnapshotOnce(
sd.get_current_context(),
index,
)
})
.ok_or_else(DataError::no_data::<T>)
.and_then(|data| data.try_into())
}
}
}
/// A HandleScope which first allocates a handle in the current scope
/// which will be later filled with the escape value.
// TODO(piscisaureus): type parameter `C` is not very useful in practice; being
@ -1560,6 +1618,10 @@ mod raw {
isolate: *mut Isolate,
exception: *const Value,
) -> *const Value;
pub(super) fn v8__Isolate__GetDataFromSnapshotOnce(
this: *mut Isolate,
index: usize,
) -> *const Data;
pub(super) fn v8__Context__EQ(
this: *const Context,
@ -1569,6 +1631,10 @@ mod raw {
pub(super) fn v8__Context__Exit(this: *const Context);
pub(super) fn v8__Context__GetIsolate(this: *const Context)
-> *mut Isolate;
pub(super) fn v8__Context__GetDataFromSnapshotOnce(
this: *const Context,
index: usize,
) -> *const Data;
pub(super) fn v8__HandleScope__CONSTRUCT(
buf: *mut MaybeUninit<HandleScope>,

View file

@ -4,6 +4,7 @@ use crate::support::char;
use crate::support::int;
use crate::support::intptr_t;
use crate::Context;
use crate::Data;
use crate::Isolate;
use crate::Local;
use crate::OwnedIsolate;
@ -30,6 +31,15 @@ extern "C" {
this: *mut SnapshotCreator,
context: *const Context,
);
fn v8__SnapshotCreator__AddData_to_isolate(
this: *mut SnapshotCreator,
data: *const Data,
) -> usize;
fn v8__SnapshotCreator__AddData_to_context(
this: *mut SnapshotCreator,
context: *const Context,
data: *const Data,
) -> usize;
fn v8__StartupData__DESTRUCT(this: *mut StartupData);
}
@ -112,6 +122,34 @@ impl SnapshotCreator {
unsafe { v8__SnapshotCreator__SetDefaultContext(self, &*context) };
}
/// Attach arbitrary `v8::Data` to the isolate snapshot, which can be
/// retrieved via `HandleScope::get_context_data_from_snapshot_once()` after
/// deserialization. This data does not survive when a new snapshot is created
/// from an existing snapshot.
pub fn add_isolate_data<T>(&mut self, data: Local<T>) -> usize
where
for<'l> Local<'l, T>: Into<Local<'l, Data>>,
{
unsafe { v8__SnapshotCreator__AddData_to_isolate(self, &*data.into()) }
}
/// Attach arbitrary `v8::Data` to the context snapshot, which can be
/// retrieved via `HandleScope::get_context_data_from_snapshot_once()` after
/// deserialization. This data does not survive when a new snapshot is
/// created from an existing snapshot.
pub fn add_context_data<T>(
&mut self,
context: Local<Context>,
data: Local<T>,
) -> usize
where
for<'l> Local<'l, T>: Into<Local<'l, Data>>,
{
unsafe {
v8__SnapshotCreator__AddData_to_context(self, &*context, &*data.into())
}
}
/// Creates a snapshot data blob.
/// This must not be called from within a handle scope.
pub fn create_blob(

View file

@ -3,6 +3,7 @@
#[macro_use]
extern crate lazy_static;
use std::any::type_name;
use std::convert::{Into, TryFrom, TryInto};
use std::ffi::c_void;
use std::ptr::NonNull;
@ -2101,6 +2102,9 @@ fn snapshot_creator() {
let _setup_guard = setup();
// First we create the snapshot, there is a single global variable 'a' set to
// the value 3.
let isolate_data_index;
let context_data_index;
let context_data_index_2;
let startup_data = {
let mut snapshot_creator = v8::SnapshotCreator::new(None);
// TODO(ry) this shouldn't be necessary. workaround unfinished business in
@ -2119,6 +2123,13 @@ fn snapshot_creator() {
script.run(scope).unwrap();
snapshot_creator.set_default_context(context);
isolate_data_index =
snapshot_creator.add_isolate_data(v8::Number::new(scope, 1.0));
context_data_index =
snapshot_creator.add_context_data(context, v8::Number::new(scope, 2.0));
context_data_index_2 =
snapshot_creator.add_context_data(context, v8::Number::new(scope, 3.0));
}
std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary.
snapshot_creator
@ -2140,6 +2151,26 @@ fn snapshot_creator() {
let result = script.run(scope).unwrap();
let true_val = v8::Boolean::new(scope, true).into();
assert!(result.same_value(true_val));
let isolate_data = scope
.get_isolate_data_from_snapshot_once::<v8::Value>(isolate_data_index);
assert!(isolate_data.unwrap() == v8::Number::new(scope, 1.0));
let no_data_err = scope
.get_isolate_data_from_snapshot_once::<v8::Value>(isolate_data_index);
assert!(matches!(no_data_err, Err(v8::DataError::NoData { .. })));
let context_data = scope
.get_context_data_from_snapshot_once::<v8::Value>(context_data_index);
assert!(context_data.unwrap() == v8::Number::new(scope, 2.0));
let no_data_err = scope
.get_context_data_from_snapshot_once::<v8::Value>(context_data_index);
assert!(matches!(no_data_err, Err(v8::DataError::NoData { .. })));
let bad_type_err = scope
.get_context_data_from_snapshot_once::<v8::Private>(
context_data_index_2,
);
assert!(matches!(bad_type_err, Err(v8::DataError::BadType { .. })));
}
}
}
@ -2782,20 +2813,16 @@ fn try_from_value() {
{
let value: v8::Local<v8::Value> = v8::undefined(scope).into();
let _primitive = v8::Local::<v8::Primitive>::try_from(value).unwrap();
assert_eq!(
v8::Local::<v8::Object>::try_from(value)
.err()
.unwrap()
.to_string(),
"Object expected"
);
assert_eq!(
v8::Local::<v8::Int32>::try_from(value)
.err()
.unwrap()
.to_string(),
"Int32 expected"
);
assert!(matches!(
v8::Local::<v8::Object>::try_from(value),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::Object>()
));
assert!(matches!(
v8::Local::<v8::Int32>::try_from(value),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::Int32>()
));
}
{
@ -2803,20 +2830,16 @@ fn try_from_value() {
let primitive = v8::Local::<v8::Primitive>::try_from(value).unwrap();
let _boolean = v8::Local::<v8::Boolean>::try_from(value).unwrap();
let _boolean = v8::Local::<v8::Boolean>::try_from(primitive).unwrap();
assert_eq!(
v8::Local::<v8::String>::try_from(value)
.err()
.unwrap()
.to_string(),
"String expected"
);
assert_eq!(
v8::Local::<v8::Number>::try_from(primitive)
.err()
.unwrap()
.to_string(),
"Number expected"
);
assert!(matches!(
v8::Local::<v8::String>::try_from(value),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::String>()
));
assert!(matches!(
v8::Local::<v8::Number>::try_from(primitive),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::Number>()
));
}
{
@ -2831,27 +2854,21 @@ fn try_from_value() {
let _int32 = v8::Local::<v8::Int32>::try_from(primitive).unwrap();
let _int32 = v8::Local::<v8::Int32>::try_from(integer).unwrap();
let _int32 = v8::Local::<v8::Int32>::try_from(number).unwrap();
assert_eq!(
v8::Local::<v8::String>::try_from(value)
.err()
.unwrap()
.to_string(),
"String expected"
);
assert_eq!(
v8::Local::<v8::Boolean>::try_from(primitive)
.err()
.unwrap()
.to_string(),
"Boolean expected"
);
assert_eq!(
v8::Local::<v8::Uint32>::try_from(integer)
.err()
.unwrap()
.to_string(),
"Uint32 expected"
);
assert!(matches!(
v8::Local::<v8::String>::try_from(value),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::String>()
));
assert!(matches!(
v8::Local::<v8::Boolean>::try_from(primitive),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::Boolean>()
));
assert!(matches!(
v8::Local::<v8::Uint32>::try_from(integer),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::Uint32>()
));
}
{
@ -2859,48 +2876,36 @@ fn try_from_value() {
let object = v8::Local::<v8::Object>::try_from(value).unwrap();
let _function = v8::Local::<v8::Function>::try_from(value).unwrap();
let _function = v8::Local::<v8::Function>::try_from(object).unwrap();
assert_eq!(
v8::Local::<v8::Primitive>::try_from(value)
.err()
.unwrap()
.to_string(),
"Primitive expected"
);
assert_eq!(
v8::Local::<v8::BigInt>::try_from(value)
.err()
.unwrap()
.to_string(),
"BigInt expected"
);
assert_eq!(
v8::Local::<v8::NumberObject>::try_from(value)
.err()
.unwrap()
.to_string(),
"NumberObject expected"
);
assert_eq!(
v8::Local::<v8::NumberObject>::try_from(object)
.err()
.unwrap()
.to_string(),
"NumberObject expected"
);
assert_eq!(
v8::Local::<v8::Set>::try_from(value)
.err()
.unwrap()
.to_string(),
"Set expected"
);
assert_eq!(
v8::Local::<v8::Set>::try_from(object)
.err()
.unwrap()
.to_string(),
"Set expected"
);
assert!(matches!(
v8::Local::<v8::Primitive>::try_from(value),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::Primitive>()
));
assert!(matches!(
v8::Local::<v8::BigInt>::try_from(value),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::BigInt>()
));
assert!(matches!(
v8::Local::<v8::NumberObject>::try_from(value),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::NumberObject>()
));
assert!(matches!(
v8::Local::<v8::NumberObject>::try_from(object),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::NumberObject>()
));
assert!(matches!(
v8::Local::<v8::Set>::try_from(value),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::Set>()
));
assert!(matches!(
v8::Local::<v8::Set>::try_from(object),
Err(v8::DataError::BadType { expected, .. })
if expected == type_name::<v8::Set>()
));
}
}
}