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: add structured cloning to Deno.core #9458

Merged
merged 10 commits into from
Feb 16, 2021
96 changes: 96 additions & 0 deletions core/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ lazy_static! {
v8::ExternalReference {
function: decode.map_fn_to()
},
v8::ExternalReference {
function: serialize.map_fn_to()
},
v8::ExternalReference {
function: deserialize.map_fn_to()
},
v8::ExternalReference {
function: get_promise_details.map_fn_to()
},
Expand Down Expand Up @@ -124,6 +130,8 @@ pub fn initialize_context<'s>(
set_func(scope, core_val, "evalContext", eval_context);
set_func(scope, core_val, "encode", encode);
set_func(scope, core_val, "decode", decode);
set_func(scope, core_val, "serialize", serialize);
set_func(scope, core_val, "deserialize", deserialize);
set_func(scope, core_val, "getPromiseDetails", get_promise_details);
set_func(scope, core_val, "getProxyDetails", get_proxy_details);

Expand Down Expand Up @@ -625,6 +633,94 @@ fn decode(
};
}

struct SerializeDeserialize {}

impl v8::ValueSerializerImpl for SerializeDeserialize {
#[allow(unused_variables)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What unused variables?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is required by v8::ValueSerializerImpl but is never used in core/bindings.rs (yet I think)

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);
}
}

impl v8::ValueDeserializerImpl for SerializeDeserialize {}

fn serialize(
scope: &mut v8::HandleScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
let serialize_deserialize = Box::new(SerializeDeserialize {});
let mut value_serializer =
v8::ValueSerializer::new(scope, serialize_deserialize);
match value_serializer.write_value(scope.get_current_context(), args.get(0)) {
Some(true) => {
let vector = value_serializer.release();
let buf = {
let buf_len = vector.len();
let backing_store = v8::ArrayBuffer::new_backing_store_from_boxed_slice(
vector.into_boxed_slice(),
);
let backing_store_shared = backing_store.make_shared();
let ab =
v8::ArrayBuffer::with_backing_store(scope, &backing_store_shared);
v8::Uint8Array::new(scope, ab, 0, buf_len)
.expect("Failed to create UintArray8")
};

rv.set(buf.into());
}
_ => {
let msg = v8::String::new(scope, "Invalid argument").unwrap();
let exception = v8::Exception::type_error(scope, msg);
scope.throw_exception(exception);
}
}
}

fn deserialize(
scope: &mut v8::HandleScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
let view = match v8::Local::<v8::ArrayBufferView>::try_from(args.get(0)) {
Ok(view) => view,
Err(_) => {
let msg = v8::String::new(scope, "Invalid argument").unwrap();
let exception = v8::Exception::type_error(scope, msg);
scope.throw_exception(exception);
return;
}
};

let backing_store = view.buffer(scope).unwrap().get_backing_store();
let buf = unsafe {
get_backing_store_slice(
&backing_store,
view.byte_offset(),
view.byte_length(),
)
};

let serialize_deserialize = Box::new(SerializeDeserialize {});
let mut value_deserializer =
v8::ValueDeserializer::new(scope, serialize_deserialize, buf);
let value = value_deserializer.read_value(scope.get_current_context());

match value {
Some(deserialized) => rv.set(deserialized),
None => {
let msg = v8::String::new(scope, "string too long").unwrap();
let exception = v8::Exception::range_error(scope, msg);
scope.throw_exception(exception);
}
};
}

fn queue_microtask(
scope: &mut v8::HandleScope,
args: v8::FunctionCallbackArguments,
Expand Down
16 changes: 16 additions & 0 deletions core/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2038,6 +2038,22 @@ pub mod tests {
});
}

#[test]
fn test_serialize_deserialize() {
run_in_task(|mut cx| {
let (mut runtime, _dispatch_count) = setup(Mode::Async);
runtime
.execute(
"serialize_deserialize_test.js",
include_str!("serialize_deserialize_test.js"),
)
.unwrap();
if let Poll::Ready(Err(_)) = runtime.poll_event_loop(&mut cx) {
unreachable!();
}
});
}

#[test]
fn will_snapshot() {
let snapshot = {
Expand Down
42 changes: 42 additions & 0 deletions core/serialize_deserialize_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";

function assert(cond) {
if (!cond) {
throw Error("assert");
}
}

function assertArrayEquals(a1, a2) {
if (a1.length !== a2.length) throw Error("assert");

for (const index in a1) {
if (a1[index] !== a2[index]) {
throw Error("assert");
}
}
}

function main() {
assertArrayEquals(Deno.core.serialize(""), [34, 0]);

// deno-fmt-ignore
assertArrayEquals(Deno.core.serialize(["test", "a", null, undefined]), [65,4,34,4,116,101,115,116,34,1,97,48,95,36,0,4]);

const a = { test: null, test2: "dd", test3: "aa" };
a.test = a;

// deno-fmt-ignore
assertArrayEquals(Deno.core.serialize(a), [111,34,4,116,101,115,116,94,0,34,5,116,101,115,116,50,34,2,100,100,34,5,116,101,115,116,51,34,2,97,97,123,3]);

assertArrayEquals(Deno.core.deserialize(new Uint8Array([34, 0])), "");

// deno-fmt-ignore
assertArrayEquals(Deno.core.deserialize(new Uint8Array([65,4,34,4,116,101,115,116,34,1,97,48,95,36,0,4])), ["test", "a", null, undefined]);

// deno-fmt-ignore
const b = Deno.core.deserialize(new Uint8Array([111,34,4,116,101,115,116,94,0,34,5,116,101,115,116,50,34,2,100,100,34,5,116,101,115,116,51,34,2,97,97,123,3]));
assert(b.test == b);
}

main();