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:
parent
c6b3982e78
commit
e2a91190c3
3 changed files with 182 additions and 15 deletions
112
core/bindings.rs
112
core/bindings.rs
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 = {
|
||||
|
|
69
core/serialize_deserialize_test.js
Normal file
69
core/serialize_deserialize_test.js
Normal 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();
|
Loading…
Add table
Reference in a new issue