Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support custom host objects in ValueSerializer #1322

Merged
merged 3 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3178,6 +3178,13 @@ extern "C" {
void v8__ValueSerializer__Delegate__ThrowDataCloneError(
v8::ValueSerializer::Delegate* self, v8::Local<v8::String> message);

bool v8__ValueSerializer__Delegate__HasCustomHostObject(
v8::ValueSerializer::Delegate* self, v8::Isolate* isolate);

MaybeBool v8__ValueSerializer__Delegate__IsHostObject(
v8::ValueSerializer::Delegate* self, v8::Isolate* isolate,
v8::Local<v8::Object> object);

MaybeBool v8__ValueSerializer__Delegate__WriteHostObject(
v8::ValueSerializer::Delegate* self, v8::Isolate* isolate,
v8::Local<v8::Object> object);
Expand All @@ -3203,6 +3210,16 @@ struct v8__ValueSerializer__Delegate : public v8::ValueSerializer::Delegate {
v8__ValueSerializer__Delegate__ThrowDataCloneError(this, message);
}

bool HasCustomHostObject(v8::Isolate* isolate) override {
return v8__ValueSerializer__Delegate__HasCustomHostObject(this, isolate);
}

v8::Maybe<bool> IsHostObject(v8::Isolate* isolate,
v8::Local<v8::Object> object) override {
return maybe_bool_to_maybe(
v8__ValueSerializer__Delegate__IsHostObject(this, isolate, object));
}

v8::Maybe<bool> WriteHostObject(v8::Isolate* isolate,
v8::Local<v8::Object> object) override {
return maybe_bool_to_maybe(
Expand Down
45 changes: 45 additions & 0 deletions src/value_serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,34 @@ pub unsafe extern "C" fn v8__ValueSerializer__Delegate__ThrowDataCloneError(
.throw_data_clone_error(scope, message)
}

#[no_mangle]
pub unsafe extern "C" fn v8__ValueSerializer__Delegate__HasCustomHostObject(
this: &mut CxxValueSerializerDelegate,
_isolate: *mut Isolate,
) -> bool {
let value_serializer_heap = ValueSerializerHeap::dispatch_mut(this);
let scope =
&mut crate::scope::CallbackScope::new(value_serializer_heap.context);
value_serializer_heap
.value_serializer_impl
.as_mut()
.has_custom_host_object(scope)
lrowe marked this conversation as resolved.
Show resolved Hide resolved
}

#[no_mangle]
pub unsafe extern "C" fn v8__ValueSerializer__Delegate__IsHostObject(
this: &mut CxxValueSerializerDelegate,
_isolate: *mut Isolate,
object: Local<Object>,
) -> MaybeBool {
let value_serializer_heap = ValueSerializerHeap::dispatch_mut(this);
let scope =
&mut crate::scope::CallbackScope::new(value_serializer_heap.context);
let value_serializer_impl =
value_serializer_heap.value_serializer_impl.as_mut();
MaybeBool::from(value_serializer_impl.is_host_object(scope, object))
}

#[no_mangle]
pub unsafe extern "C" fn v8__ValueSerializer__Delegate__WriteHostObject(
this: &mut CxxValueSerializerDelegate,
Expand Down Expand Up @@ -211,6 +239,23 @@ pub trait ValueSerializerImpl {
message: Local<'s, String>,
);

fn has_custom_host_object(&mut self, _scope: &mut HandleScope) -> bool {
false
}

fn is_host_object<'s>(
&mut self,
scope: &mut HandleScope<'s>,
_object: Local<'s, Object>,
) -> Option<bool> {
let msg =
String::new(scope, "Deno serializer: is_host_object not implemented")
.unwrap();
let exc = Exception::error(scope, msg);
scope.throw_exception(exc);
None
}

fn write_host_object<'s>(
&mut self,
scope: &mut HandleScope<'s>,
Expand Down
213 changes: 206 additions & 7 deletions tests/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7534,12 +7534,18 @@ impl<'a> v8::ValueSerializerImpl for Custom1Value<'a> {

fn write_host_object<'s>(
&mut self,
_scope: &mut v8::HandleScope<'s>,
_object: v8::Local<'s, v8::Object>,
scope: &mut v8::HandleScope<'s>,
object: v8::Local<'s, v8::Object>,
value_serializer: &mut dyn v8::ValueSerializerHelper,
) -> Option<bool> {
value_serializer.write_uint64(1);
None
let key = v8::String::new(scope, "hostObject").unwrap();
let value = object
.get(scope, key.into())
.unwrap()
.uint32_value(scope)
.unwrap();
value_serializer.write_uint32(value);
Some(true)
}
}

Expand All @@ -7559,12 +7565,18 @@ impl<'a> v8::ValueDeserializerImpl for Custom1Value<'a> {

fn read_host_object<'s>(
&mut self,
_scope: &mut v8::HandleScope<'s>,
scope: &mut v8::HandleScope<'s>,
value_deserializer: &mut dyn v8::ValueDeserializerHelper,
) -> Option<v8::Local<'s, v8::Object>> {
let mut value = 0;
value_deserializer.read_uint64(&mut value);
None
value_deserializer.read_uint32(&mut value);
let template = v8::ObjectTemplate::new(scope);
template.set_internal_field_count(1);
let host_object = template.new_instance(scope).unwrap();
let key = v8::String::new(scope, "readHostObject").unwrap();
let value = v8::Integer::new_from_unsigned(scope, value);
host_object.set(scope, key.into(), value.into());
Some(host_object)
}
}

Expand Down Expand Up @@ -7788,6 +7800,64 @@ fn value_serializer_and_deserializer_array_buffers() {
}
}

#[test]
fn value_serializer_and_deserializer_embedder_host_object() {
let buffer;
let expected: u32 = 123;
let mut array_buffers = ArrayBuffers::new();
{
let _setup_guard = setup::parallel_test();
let isolate = &mut v8::Isolate::new(Default::default());

let scope = &mut v8::HandleScope::new(isolate);

let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);

let template = v8::ObjectTemplate::new(scope);
template.set_internal_field_count(1);
let host_object = template.new_instance(scope).unwrap();
let key = v8::String::new(scope, "hostObject").unwrap();
let value = v8::Integer::new_from_unsigned(scope, expected);
host_object.set(scope, key.into(), value.into());

let mut value_serializer =
Custom1Value::serializer(scope, &mut array_buffers);
assert_eq!(
value_serializer.write_value(context, host_object.into()),
Some(true)
);

buffer = value_serializer.release();
}

{
let _setup_guard = setup::parallel_test();
let isolate = &mut v8::Isolate::new(Default::default());

let scope = &mut v8::HandleScope::new(isolate);

let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);

let mut value_deserializer =
Custom1Value::deserializer(scope, &buffer, &mut array_buffers);
let host_object_out = value_deserializer
.read_value(context)
.unwrap()
.to_object(scope)
.unwrap();
drop(value_deserializer);
let key = v8::String::new(scope, "readHostObject").unwrap();
let value = host_object_out
.get(scope, key.into())
.unwrap()
.uint32_value(scope)
.unwrap();
assert_eq!(value, expected);
}
}

struct Custom2Value {}

impl<'a> Custom2Value {
Expand Down Expand Up @@ -7847,6 +7917,135 @@ fn value_serializer_not_implemented() {
);
}

struct Custom3Value {}

impl<'a> Custom3Value {
fn serializer<'s>(
scope: &mut v8::HandleScope<'s>,
) -> v8::ValueSerializer<'a, 's> {
v8::ValueSerializer::new(scope, Box::new(Self {}))
}

fn deserializer<'s>(
scope: &mut v8::HandleScope<'s>,
data: &[u8],
) -> v8::ValueDeserializer<'a, 's> {
v8::ValueDeserializer::new(scope, Box::new(Self {}), data)
}
}

impl v8::ValueSerializerImpl for Custom3Value {
#[allow(unused_variables)]
fn throw_data_clone_error<'s>(
&mut self,
scope: &mut v8::HandleScope<'s>,
message: v8::Local<'s, v8::String>,
) {
let error = v8::Exception::error(scope, message);
scope.throw_exception(error);
}

fn has_custom_host_object(&mut self, _scope: &mut v8::HandleScope) -> bool {
true
}

fn is_host_object<'s>(
&mut self,
scope: &mut v8::HandleScope<'s>,
object: v8::Local<'s, v8::Object>,
) -> Option<bool> {
let key = v8::String::new(scope, "hostObject").unwrap();
object.has_own_property(scope, key.into())
}

fn write_host_object<'s>(
&mut self,
scope: &mut v8::HandleScope<'s>,
object: v8::Local<'s, v8::Object>,
value_serializer: &mut dyn v8::ValueSerializerHelper,
) -> Option<bool> {
let key = v8::String::new(scope, "hostObject").unwrap();
let value = object
.get(scope, key.into())
.unwrap()
.uint32_value(scope)
.unwrap();
value_serializer.write_uint32(value);
Some(true)
}
}

impl v8::ValueDeserializerImpl for Custom3Value {
fn read_host_object<'s>(
&mut self,
scope: &mut v8::HandleScope<'s>,
value_deserializer: &mut dyn v8::ValueDeserializerHelper,
) -> Option<v8::Local<'s, v8::Object>> {
let mut value = 0;
value_deserializer.read_uint32(&mut value);
let host_object = v8::Object::new(scope);
let key = v8::String::new(scope, "readHostObject").unwrap();
let value = v8::Integer::new_from_unsigned(scope, value);
host_object.set(scope, key.into(), value.into());
Some(host_object)
}
}

#[test]
fn value_serializer_and_deserializer_custom_host_object() {
let buffer;
let expected: u32 = 123;
{
let _setup_guard = setup::parallel_test();
let isolate = &mut v8::Isolate::new(Default::default());

let scope = &mut v8::HandleScope::new(isolate);

let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);

let host_object = v8::Object::new(scope);
let key = v8::String::new(scope, "hostObject").unwrap();
let value = v8::Integer::new_from_unsigned(scope, expected);
host_object.set(scope, key.into(), value.into());

let mut value_serializer = Custom3Value::serializer(scope);
assert_eq!(
value_serializer.write_value(context, host_object.into()),
Some(true)
);

buffer = value_serializer.release();
}

{
let _setup_guard = setup::parallel_test();
let isolate = &mut v8::Isolate::new(Default::default());

let scope = &mut v8::HandleScope::new(isolate);

let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);

let mut value_deserializer = Custom3Value::deserializer(scope, &buffer);
let host_object_out = value_deserializer
.read_value(context)
.unwrap()
.to_object(scope)
.unwrap();
drop(value_deserializer);
let key = v8::String::new(scope, "readHostObject").unwrap();
let has_prop = host_object_out.has_own_property(scope, key.into()).unwrap();
assert!(has_prop);
let value = host_object_out
.get(scope, key.into())
.unwrap()
.uint32_value(scope)
.unwrap();
assert_eq!(value, expected);
}
}

#[test]
fn memory_pressure_notification() {
let _setup_guard = setup::parallel_test();
Expand Down
Loading