mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
feat(extensions/web): add structuredClone function (#11572)
Co-authored-by: Luca Casonato <hello@lcas.dev>
This commit is contained in:
parent
02c74fb709
commit
16ae4a0d57
9 changed files with 107 additions and 36 deletions
2
cli/dts/lib.deno.shared_globals.d.ts
vendored
2
cli/dts/lib.deno.shared_globals.d.ts
vendored
|
@ -411,7 +411,7 @@ declare class Worker extends EventTarget {
|
|||
options?: WorkerOptions,
|
||||
);
|
||||
postMessage(message: any, transfer: Transferable[]): void;
|
||||
postMessage(message: any, options?: PostMessageOptions): void;
|
||||
postMessage(message: any, options?: StructuredSerializeOptions): void;
|
||||
addEventListener<K extends keyof WorkerEventMap>(
|
||||
type: K,
|
||||
listener: (this: Worker, ev: WorkerEventMap[K]) => any,
|
||||
|
|
7
cli/dts/lib.deno.worker.d.ts
vendored
7
cli/dts/lib.deno.worker.d.ts
vendored
|
@ -70,7 +70,7 @@ declare class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
|
|||
| null;
|
||||
close(): void;
|
||||
postMessage(message: any, transfer: Transferable[]): void;
|
||||
postMessage(message: any, options?: PostMessageOptions): void;
|
||||
postMessage(message: any, options?: StructuredSerializeOptions): void;
|
||||
addEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(
|
||||
type: K,
|
||||
listener: (
|
||||
|
@ -108,7 +108,10 @@ declare var onmessageerror:
|
|||
| null;
|
||||
declare function close(): void;
|
||||
declare function postMessage(message: any, transfer: Transferable[]): void;
|
||||
declare function postMessage(message: any, options?: PostMessageOptions): void;
|
||||
declare function postMessage(
|
||||
message: any,
|
||||
options?: StructuredSerializeOptions,
|
||||
): void;
|
||||
declare var navigator: WorkerNavigator;
|
||||
declare var onerror:
|
||||
| ((this: DedicatedWorkerGlobalScope, ev: ErrorEvent) => any)
|
||||
|
|
19
cli/tests/unit/structured_clone_test.ts
Normal file
19
cli/tests/unit/structured_clone_test.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { assert, assertEquals } from "./test_util.ts";
|
||||
|
||||
// Basic tests for the structured clone algorithm. Mainly tests TypeScript
|
||||
// typings. Actual functionality is tested in WPT.
|
||||
|
||||
Deno.test("self.structuredClone", async () => {
|
||||
const arrayOriginal = ["hello world"];
|
||||
const channelOriginal = new MessageChannel();
|
||||
const [arrayCloned, portTransferred] = self
|
||||
.structuredClone([arrayOriginal, channelOriginal.port2], {
|
||||
transfer: [channelOriginal.port2],
|
||||
});
|
||||
assert(arrayOriginal !== arrayCloned); // not the same identity
|
||||
assertEquals(arrayCloned, arrayOriginal); // but same value
|
||||
channelOriginal.port1.postMessage("1");
|
||||
await new Promise((resolve) => portTransferred.onmessage = () => resolve(1));
|
||||
channelOriginal.port1.close();
|
||||
portTransferred.close();
|
||||
});
|
|
@ -26,6 +26,8 @@ use v8::HandleScope;
|
|||
use v8::Local;
|
||||
use v8::MapFnTo;
|
||||
use v8::SharedArrayBuffer;
|
||||
use v8::ValueDeserializerHelper;
|
||||
use v8::ValueSerializerHelper;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref EXTERNAL_REFERENCES: v8::ExternalReferences =
|
||||
|
@ -827,6 +829,7 @@ fn serialize(
|
|||
let serialize_deserialize = Box::new(SerializeDeserialize { host_objects });
|
||||
let mut value_serializer =
|
||||
v8::ValueSerializer::new(scope, serialize_deserialize);
|
||||
value_serializer.write_header();
|
||||
match value_serializer.write_value(scope.get_current_context(), value) {
|
||||
Some(true) => {
|
||||
let vector = value_serializer.release();
|
||||
|
@ -884,6 +887,15 @@ fn deserialize(
|
|||
let serialize_deserialize = Box::new(SerializeDeserialize { host_objects });
|
||||
let mut value_deserializer =
|
||||
v8::ValueDeserializer::new(scope, serialize_deserialize, &zero_copy);
|
||||
let parsed_header = value_deserializer
|
||||
.read_header(scope.get_current_context())
|
||||
.unwrap_or_default();
|
||||
if !parsed_header {
|
||||
let msg = v8::String::new(scope, "could not deserialize value").unwrap();
|
||||
let exception = v8::Exception::range_error(scope, msg);
|
||||
scope.throw_exception(exception);
|
||||
return;
|
||||
}
|
||||
let value = value_deserializer.read_value(scope.get_current_context());
|
||||
|
||||
match value {
|
||||
|
|
|
@ -19,7 +19,7 @@ function assertArrayEquals(a1, a2) {
|
|||
|
||||
function main() {
|
||||
const emptyString = "";
|
||||
const emptyStringSerialized = [34, 0];
|
||||
const emptyStringSerialized = [255, 13, 34, 0];
|
||||
assertArrayEquals(Deno.core.serialize(emptyString), emptyStringSerialized);
|
||||
assert(
|
||||
Deno.core.deserialize(new Uint8Array(emptyStringSerialized)) ===
|
||||
|
@ -29,7 +29,7 @@ function main() {
|
|||
const primitiveValueArray = ["test", "a", null, undefined];
|
||||
// deno-fmt-ignore
|
||||
const primitiveValueArraySerialized = [
|
||||
65, 4, 34, 4, 116, 101, 115, 116,
|
||||
255, 13, 65, 4, 34, 4, 116, 101, 115, 116,
|
||||
34, 1, 97, 48, 95, 36, 0, 4,
|
||||
];
|
||||
assertArrayEquals(
|
||||
|
@ -48,11 +48,11 @@ function main() {
|
|||
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,
|
||||
255, 13, 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(
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
|
||||
/**
|
||||
* @param {any} message
|
||||
* @param {object[] | PostMessageOptions} transferOrOptions
|
||||
* @param {object[] | StructuredSerializeOptions} transferOrOptions
|
||||
*/
|
||||
postMessage(message, transferOrOptions = {}) {
|
||||
webidl.assertBranded(this, MessagePort);
|
||||
|
@ -108,10 +108,13 @@
|
|||
);
|
||||
options = { transfer };
|
||||
} else {
|
||||
options = webidl.converters.PostMessageOptions(transferOrOptions, {
|
||||
options = webidl.converters.StructuredSerializeOptions(
|
||||
transferOrOptions,
|
||||
{
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
const { transfer } = options;
|
||||
if (transfer.includes(this)) {
|
||||
|
@ -247,8 +250,9 @@
|
|||
};
|
||||
}
|
||||
|
||||
webidl.converters.PostMessageOptions = webidl.createDictionaryConverter(
|
||||
"PostMessageOptions",
|
||||
webidl.converters.StructuredSerializeOptions = webidl
|
||||
.createDictionaryConverter(
|
||||
"StructuredSerializeOptions",
|
||||
[
|
||||
{
|
||||
key: "transfer",
|
||||
|
@ -260,10 +264,23 @@
|
|||
],
|
||||
);
|
||||
|
||||
function structuredClone(value, options) {
|
||||
const prefix = "Failed to execute 'structuredClone'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
options = webidl.converters.StructuredSerializeOptions(options, {
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
});
|
||||
const messageData = serializeJsMessageData(value, options.transfer);
|
||||
const [data] = deserializeJsMessageData(messageData);
|
||||
return data;
|
||||
}
|
||||
|
||||
window.__bootstrap.messagePort = {
|
||||
MessageChannel,
|
||||
MessagePort,
|
||||
deserializeJsMessageData,
|
||||
serializeJsMessageData,
|
||||
structuredClone,
|
||||
};
|
||||
})(globalThis);
|
||||
|
|
17
extensions/web/lib.deno_web.d.ts
vendored
17
extensions/web/lib.deno_web.d.ts
vendored
|
@ -673,7 +673,15 @@ declare class MessageEvent<T = any> extends Event {
|
|||
|
||||
type Transferable = ArrayBuffer | MessagePort;
|
||||
|
||||
interface PostMessageOptions {
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* This type has been renamed to StructuredSerializeOptions. Use that type for
|
||||
* new code.
|
||||
*/
|
||||
type PostMessageOptions = StructuredSerializeOptions;
|
||||
|
||||
interface StructuredSerializeOptions {
|
||||
transfer?: Transferable[];
|
||||
}
|
||||
|
||||
|
@ -710,7 +718,7 @@ declare class MessagePort extends EventTarget {
|
|||
* objects or port, or if message could not be cloned.
|
||||
*/
|
||||
postMessage(message: any, transfer: Transferable[]): void;
|
||||
postMessage(message: any, options?: PostMessageOptions): void;
|
||||
postMessage(message: any, options?: StructuredSerializeOptions): void;
|
||||
/**
|
||||
* Begins dispatching messages received on the port. This is implictly called
|
||||
* when assiging a value to `this.onmessage`.
|
||||
|
@ -737,3 +745,8 @@ declare class MessagePort extends EventTarget {
|
|||
options?: boolean | EventListenerOptions,
|
||||
): void;
|
||||
}
|
||||
|
||||
declare function structuredClone(
|
||||
value: any,
|
||||
options?: StructuredSerializeOptions,
|
||||
): any;
|
||||
|
|
|
@ -318,10 +318,13 @@
|
|||
);
|
||||
options = { transfer };
|
||||
} else {
|
||||
options = webidl.converters.PostMessageOptions(transferOrOptions, {
|
||||
options = webidl.converters.StructuredSerializeOptions(
|
||||
transferOrOptions,
|
||||
{
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
const { transfer } = options;
|
||||
const data = serializeJsMessageData(message, transfer);
|
||||
|
|
|
@ -105,10 +105,13 @@ delete Object.prototype.__proto__;
|
|||
);
|
||||
options = { transfer };
|
||||
} else {
|
||||
options = webidl.converters.PostMessageOptions(transferOrOptions, {
|
||||
options = webidl.converters.StructuredSerializeOptions(
|
||||
transferOrOptions,
|
||||
{
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
const { transfer } = options;
|
||||
const data = serializeJsMessageData(message, transfer);
|
||||
|
@ -373,6 +376,7 @@ delete Object.prototype.__proto__;
|
|||
performance: util.writable(performance.performance),
|
||||
setInterval: util.writable(timers.setInterval),
|
||||
setTimeout: util.writable(timers.setTimeout),
|
||||
structuredClone: util.writable(messagePort.structuredClone),
|
||||
|
||||
GPU: util.nonEnumerable(webgpu.GPU),
|
||||
GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
|
||||
|
|
Loading…
Reference in a new issue