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,
|
options?: WorkerOptions,
|
||||||
);
|
);
|
||||||
postMessage(message: any, transfer: Transferable[]): void;
|
postMessage(message: any, transfer: Transferable[]): void;
|
||||||
postMessage(message: any, options?: PostMessageOptions): void;
|
postMessage(message: any, options?: StructuredSerializeOptions): void;
|
||||||
addEventListener<K extends keyof WorkerEventMap>(
|
addEventListener<K extends keyof WorkerEventMap>(
|
||||||
type: K,
|
type: K,
|
||||||
listener: (this: Worker, ev: WorkerEventMap[K]) => any,
|
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;
|
| null;
|
||||||
close(): void;
|
close(): void;
|
||||||
postMessage(message: any, transfer: Transferable[]): void;
|
postMessage(message: any, transfer: Transferable[]): void;
|
||||||
postMessage(message: any, options?: PostMessageOptions): void;
|
postMessage(message: any, options?: StructuredSerializeOptions): void;
|
||||||
addEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(
|
addEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(
|
||||||
type: K,
|
type: K,
|
||||||
listener: (
|
listener: (
|
||||||
|
@ -108,7 +108,10 @@ declare var onmessageerror:
|
||||||
| null;
|
| null;
|
||||||
declare function close(): void;
|
declare function close(): void;
|
||||||
declare function postMessage(message: any, transfer: Transferable[]): 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 navigator: WorkerNavigator;
|
||||||
declare var onerror:
|
declare var onerror:
|
||||||
| ((this: DedicatedWorkerGlobalScope, ev: ErrorEvent) => any)
|
| ((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::Local;
|
||||||
use v8::MapFnTo;
|
use v8::MapFnTo;
|
||||||
use v8::SharedArrayBuffer;
|
use v8::SharedArrayBuffer;
|
||||||
|
use v8::ValueDeserializerHelper;
|
||||||
|
use v8::ValueSerializerHelper;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref EXTERNAL_REFERENCES: v8::ExternalReferences =
|
pub static ref EXTERNAL_REFERENCES: v8::ExternalReferences =
|
||||||
|
@ -827,6 +829,7 @@ fn serialize(
|
||||||
let serialize_deserialize = Box::new(SerializeDeserialize { host_objects });
|
let serialize_deserialize = Box::new(SerializeDeserialize { host_objects });
|
||||||
let mut value_serializer =
|
let mut value_serializer =
|
||||||
v8::ValueSerializer::new(scope, serialize_deserialize);
|
v8::ValueSerializer::new(scope, serialize_deserialize);
|
||||||
|
value_serializer.write_header();
|
||||||
match value_serializer.write_value(scope.get_current_context(), value) {
|
match value_serializer.write_value(scope.get_current_context(), value) {
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
let vector = value_serializer.release();
|
let vector = value_serializer.release();
|
||||||
|
@ -884,6 +887,15 @@ fn deserialize(
|
||||||
let serialize_deserialize = Box::new(SerializeDeserialize { host_objects });
|
let serialize_deserialize = Box::new(SerializeDeserialize { host_objects });
|
||||||
let mut value_deserializer =
|
let mut value_deserializer =
|
||||||
v8::ValueDeserializer::new(scope, serialize_deserialize, &zero_copy);
|
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());
|
let value = value_deserializer.read_value(scope.get_current_context());
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
|
|
|
@ -19,7 +19,7 @@ function assertArrayEquals(a1, a2) {
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
const emptyString = "";
|
const emptyString = "";
|
||||||
const emptyStringSerialized = [34, 0];
|
const emptyStringSerialized = [255, 13, 34, 0];
|
||||||
assertArrayEquals(Deno.core.serialize(emptyString), emptyStringSerialized);
|
assertArrayEquals(Deno.core.serialize(emptyString), emptyStringSerialized);
|
||||||
assert(
|
assert(
|
||||||
Deno.core.deserialize(new Uint8Array(emptyStringSerialized)) ===
|
Deno.core.deserialize(new Uint8Array(emptyStringSerialized)) ===
|
||||||
|
@ -29,7 +29,7 @@ function main() {
|
||||||
const primitiveValueArray = ["test", "a", null, undefined];
|
const primitiveValueArray = ["test", "a", null, undefined];
|
||||||
// deno-fmt-ignore
|
// deno-fmt-ignore
|
||||||
const primitiveValueArraySerialized = [
|
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,
|
34, 1, 97, 48, 95, 36, 0, 4,
|
||||||
];
|
];
|
||||||
assertArrayEquals(
|
assertArrayEquals(
|
||||||
|
@ -48,11 +48,11 @@ function main() {
|
||||||
circularObject.test = circularObject;
|
circularObject.test = circularObject;
|
||||||
// deno-fmt-ignore
|
// deno-fmt-ignore
|
||||||
const circularObjectSerialized = [
|
const circularObjectSerialized = [
|
||||||
111, 34, 4, 116, 101, 115, 116, 94,
|
255, 13, 111, 34, 4, 116, 101, 115,
|
||||||
0, 34, 5, 116, 101, 115, 116, 50,
|
116, 94, 0, 34, 5, 116, 101, 115,
|
||||||
34, 2, 100, 100, 34, 5, 116, 101,
|
116, 50, 34, 2, 100, 100, 34, 5,
|
||||||
115, 116, 51, 34, 2, 97, 97, 123,
|
116, 101, 115, 116, 51, 34, 2, 97,
|
||||||
3,
|
97, 123, 3,
|
||||||
];
|
];
|
||||||
|
|
||||||
assertArrayEquals(
|
assertArrayEquals(
|
||||||
|
|
|
@ -89,7 +89,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {any} message
|
* @param {any} message
|
||||||
* @param {object[] | PostMessageOptions} transferOrOptions
|
* @param {object[] | StructuredSerializeOptions} transferOrOptions
|
||||||
*/
|
*/
|
||||||
postMessage(message, transferOrOptions = {}) {
|
postMessage(message, transferOrOptions = {}) {
|
||||||
webidl.assertBranded(this, MessagePort);
|
webidl.assertBranded(this, MessagePort);
|
||||||
|
@ -108,10 +108,13 @@
|
||||||
);
|
);
|
||||||
options = { transfer };
|
options = { transfer };
|
||||||
} else {
|
} else {
|
||||||
options = webidl.converters.PostMessageOptions(transferOrOptions, {
|
options = webidl.converters.StructuredSerializeOptions(
|
||||||
prefix,
|
transferOrOptions,
|
||||||
context: "Argument 2",
|
{
|
||||||
});
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const { transfer } = options;
|
const { transfer } = options;
|
||||||
if (transfer.includes(this)) {
|
if (transfer.includes(this)) {
|
||||||
|
@ -247,23 +250,37 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.converters.PostMessageOptions = webidl.createDictionaryConverter(
|
webidl.converters.StructuredSerializeOptions = webidl
|
||||||
"PostMessageOptions",
|
.createDictionaryConverter(
|
||||||
[
|
"StructuredSerializeOptions",
|
||||||
{
|
[
|
||||||
key: "transfer",
|
{
|
||||||
converter: webidl.converters["sequence<object>"],
|
key: "transfer",
|
||||||
get defaultValue() {
|
converter: webidl.converters["sequence<object>"],
|
||||||
return [];
|
get defaultValue() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
],
|
);
|
||||||
);
|
|
||||||
|
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 = {
|
window.__bootstrap.messagePort = {
|
||||||
MessageChannel,
|
MessageChannel,
|
||||||
MessagePort,
|
MessagePort,
|
||||||
deserializeJsMessageData,
|
deserializeJsMessageData,
|
||||||
serializeJsMessageData,
|
serializeJsMessageData,
|
||||||
|
structuredClone,
|
||||||
};
|
};
|
||||||
})(globalThis);
|
})(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;
|
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[];
|
transfer?: Transferable[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -710,7 +718,7 @@ declare class MessagePort extends EventTarget {
|
||||||
* objects or port, or if message could not be cloned.
|
* objects or port, or if message could not be cloned.
|
||||||
*/
|
*/
|
||||||
postMessage(message: any, transfer: Transferable[]): void;
|
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
|
* Begins dispatching messages received on the port. This is implictly called
|
||||||
* when assiging a value to `this.onmessage`.
|
* when assiging a value to `this.onmessage`.
|
||||||
|
@ -737,3 +745,8 @@ declare class MessagePort extends EventTarget {
|
||||||
options?: boolean | EventListenerOptions,
|
options?: boolean | EventListenerOptions,
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare function structuredClone(
|
||||||
|
value: any,
|
||||||
|
options?: StructuredSerializeOptions,
|
||||||
|
): any;
|
||||||
|
|
|
@ -318,10 +318,13 @@
|
||||||
);
|
);
|
||||||
options = { transfer };
|
options = { transfer };
|
||||||
} else {
|
} else {
|
||||||
options = webidl.converters.PostMessageOptions(transferOrOptions, {
|
options = webidl.converters.StructuredSerializeOptions(
|
||||||
prefix,
|
transferOrOptions,
|
||||||
context: "Argument 2",
|
{
|
||||||
});
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const { transfer } = options;
|
const { transfer } = options;
|
||||||
const data = serializeJsMessageData(message, transfer);
|
const data = serializeJsMessageData(message, transfer);
|
||||||
|
|
|
@ -105,10 +105,13 @@ delete Object.prototype.__proto__;
|
||||||
);
|
);
|
||||||
options = { transfer };
|
options = { transfer };
|
||||||
} else {
|
} else {
|
||||||
options = webidl.converters.PostMessageOptions(transferOrOptions, {
|
options = webidl.converters.StructuredSerializeOptions(
|
||||||
prefix,
|
transferOrOptions,
|
||||||
context: "Argument 2",
|
{
|
||||||
});
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const { transfer } = options;
|
const { transfer } = options;
|
||||||
const data = serializeJsMessageData(message, transfer);
|
const data = serializeJsMessageData(message, transfer);
|
||||||
|
@ -373,6 +376,7 @@ delete Object.prototype.__proto__;
|
||||||
performance: util.writable(performance.performance),
|
performance: util.writable(performance.performance),
|
||||||
setInterval: util.writable(timers.setInterval),
|
setInterval: util.writable(timers.setInterval),
|
||||||
setTimeout: util.writable(timers.setTimeout),
|
setTimeout: util.writable(timers.setTimeout),
|
||||||
|
structuredClone: util.writable(messagePort.structuredClone),
|
||||||
|
|
||||||
GPU: util.nonEnumerable(webgpu.GPU),
|
GPU: util.nonEnumerable(webgpu.GPU),
|
||||||
GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
|
GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
|
||||||
|
|
Loading…
Reference in a new issue