0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 09:31:22 -05:00

feat: add structured cloning to Deno.core (#9458)

This commit adds two new "Deno.core" APIs:
- "Deno.core.serialize"
- "Deno.core.deserialize"

These APIs are used to provide structured cloning of values
and will be used for further web worker implementation.

Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
Inteon 2021-02-16 14:20:21 +01:00 committed by GitHub
parent c6b3982e78
commit e2a91190c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 182 additions and 15 deletions

View file

@ -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()
},
@ -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);
@ -427,9 +435,7 @@ fn eval_context(
let source = match v8::Local::<v8::String>::try_from(args.get(0)) {
Ok(s) => s,
Err(_) => {
let msg = v8::String::new(scope, "Invalid argument").unwrap();
let exception = v8::Exception::type_error(scope, msg);
scope.throw_exception(exception);
throw_type_error(scope, "Invalid argument");
return;
}
};
@ -550,9 +556,7 @@ fn encode(
let text = match v8::Local::<v8::String>::try_from(args.get(0)) {
Ok(s) => s,
Err(_) => {
let msg = v8::String::new(scope, "Invalid argument").unwrap();
let exception = v8::Exception::type_error(scope, msg);
scope.throw_exception(exception);
throw_type_error(scope, "Invalid argument");
return;
}
};
@ -583,9 +587,7 @@ fn decode(
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);
throw_type_error(scope, "Invalid argument");
return;
}
};
@ -625,6 +627,90 @@ fn decode(
};
}
struct SerializeDeserialize {}
impl v8::ValueSerializerImpl for SerializeDeserialize {
#[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);
}
}
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());
}
_ => {
throw_type_error(scope, "Invalid argument");
}
}
}
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(_) => {
throw_type_error(scope, "Invalid argument");
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,
@ -633,9 +719,7 @@ fn queue_microtask(
match v8::Local::<v8::Function>::try_from(args.get(0)) {
Ok(f) => scope.enqueue_microtask(f),
Err(_) => {
let msg = v8::String::new(scope, "Invalid argument").unwrap();
let exception = v8::Exception::type_error(scope, msg);
scope.throw_exception(exception);
throw_type_error(scope, "Invalid argument");
}
};
}
@ -725,9 +809,7 @@ fn get_promise_details(
let promise = match v8::Local::<v8::Promise>::try_from(args.get(0)) {
Ok(val) => val,
Err(_) => {
let msg = v8::String::new(scope, "Invalid argument").unwrap();
let exception = v8::Exception::type_error(scope, msg);
scope.throw_exception(exception);
throw_type_error(scope, "Invalid argument");
return;
}
};

View file

@ -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 = {

View file

@ -0,0 +1,69 @@
// 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() {
const emptyString = "";
const emptyStringSerialized = [34, 0];
assertArrayEquals(Deno.core.serialize(emptyString), emptyStringSerialized);
assert(
Deno.core.deserialize(new Uint8Array(emptyStringSerialized)) ===
emptyString,
);
const primitiveValueArray = ["test", "a", null, undefined];
// deno-fmt-ignore
const primitiveValueArraySerialized = [
65, 4, 34, 4, 116, 101, 115, 116,
34, 1, 97, 48, 95, 36, 0, 4,
];
assertArrayEquals(
Deno.core.serialize(primitiveValueArray),
primitiveValueArraySerialized,
);
assertArrayEquals(
Deno.core.deserialize(
new Uint8Array(primitiveValueArraySerialized),
),
primitiveValueArray,
);
const circularObject = { test: null, test2: "dd", test3: "aa" };
circularObject.test = circularObject;
// deno-fmt-ignore
const circularObjectSerialized = [
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.serialize(circularObject),
circularObjectSerialized,
);
const deserializedCircularObject = Deno.core.deserialize(
new Uint8Array(circularObjectSerialized),
);
assert(deserializedCircularObject.test == deserializedCircularObject);
}
main();