diff options
author | Luca Casonato <hello@lcas.dev> | 2021-08-25 13:48:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-25 13:48:53 +0200 |
commit | 5d814a4c244d489b4ae51002a0cf1d3c2fe16058 (patch) | |
tree | 3597ab7d4c438b96c34dac52f9f537989129001e /ext/web/13_message_port.js | |
parent | f84cd9403db3545c8058a9c28474b3c99d4c2dd4 (diff) |
feat: ArrayBuffer in structured clone transfer (#11840)
Diffstat (limited to 'ext/web/13_message_port.js')
-rw-r--r-- | ext/web/13_message_port.js | 73 |
1 files changed, 65 insertions, 8 deletions
diff --git a/ext/web/13_message_port.js b/ext/web/13_message_port.js index d0d9e160e..2768c0a92 100644 --- a/ext/web/13_message_port.js +++ b/ext/web/13_message_port.js @@ -15,6 +15,8 @@ const { defineEventHandler } = window.__bootstrap.event; const { DOMException } = window.__bootstrap.domException; const { + ArrayBuffer, + ArrayPrototypeFilter, ArrayPrototypeIncludes, ArrayPrototypePush, ObjectSetPrototypeOf, @@ -22,6 +24,9 @@ SymbolFor, SymbolToStringTag, TypeError, + WeakSet, + WeakSetPrototypeAdd, + WeakSetPrototypeHas, } = window.__bootstrap.primordials; class MessageChannel { @@ -139,11 +144,11 @@ this[_id], ); if (data === null) break; - let message, transfer; + let message, transferables; try { const v = deserializeJsMessageData(data); message = v[0]; - transfer = v[1]; + transferables = v[1]; } catch (err) { const event = new MessageEvent("messageerror", { data: err }); this.dispatchEvent(event); @@ -151,7 +156,10 @@ } const event = new MessageEvent("message", { data: message, - ports: transfer, + ports: ArrayPrototypeFilter( + transferables, + (t) => t instanceof MessagePort, + ), }); this.dispatchEvent(event); } @@ -193,12 +201,22 @@ function deserializeJsMessageData(messageData) { /** @type {object[]} */ const transferables = []; + const hostObjects = []; + const arrayBufferIdsInTransferables = []; + const transferedArrayBuffers = []; for (const transferable of messageData.transferables) { switch (transferable.kind) { case "messagePort": { const port = createMessagePort(transferable.data); ArrayPrototypePush(transferables, port); + ArrayPrototypePush(hostObjects, port); + break; + } + case "arrayBuffer": { + ArrayPrototypePush(transferedArrayBuffers, transferable.data); + const i = ArrayPrototypePush(transferables, null); + ArrayPrototypePush(arrayBufferIdsInTransferables, i); break; } default: @@ -207,21 +225,53 @@ } const data = core.deserialize(messageData.data, { - hostObjects: transferables, + hostObjects, + transferedArrayBuffers, }); + for (const i in arrayBufferIdsInTransferables) { + const id = arrayBufferIdsInTransferables[i]; + transferables[id] = transferedArrayBuffers[i]; + } + return [data, transferables]; } + const detachedArrayBuffers = new WeakSet(); + /** * @param {any} data - * @param {object[]} tranferables + * @param {object[]} transferables * @returns {globalThis.__bootstrap.messagePort.MessageData} */ - function serializeJsMessageData(data, tranferables) { + function serializeJsMessageData(data, transferables) { + const transferedArrayBuffers = ArrayPrototypeFilter( + transferables, + (a) => a instanceof ArrayBuffer, + ); + + for (const arrayBuffer of transferedArrayBuffers) { + // This is hacky with both false positives and false negatives for + // detecting detached array buffers. V8 needs to add a way to tell if a + // buffer is detached or not. + if (WeakSetPrototypeHas(detachedArrayBuffers, arrayBuffer)) { + throw new DOMException( + "Can not transfer detached ArrayBuffer", + "DataCloneError", + ); + } + WeakSetPrototypeAdd(detachedArrayBuffers, arrayBuffer); + } + let serializedData; try { - serializedData = core.serialize(data, { hostObjects: tranferables }); + serializedData = core.serialize(data, { + hostObjects: ArrayPrototypeFilter( + transferables, + (a) => a instanceof MessagePort, + ), + transferedArrayBuffers, + }); } catch (err) { throw new DOMException(err.message, "DataCloneError"); } @@ -229,7 +279,8 @@ /** @type {globalThis.__bootstrap.messagePort.Transferable[]} */ const serializedTransferables = []; - for (const transferable of tranferables) { + let arrayBufferI = 0; + for (const transferable of transferables) { if (transferable instanceof MessagePort) { webidl.assertBranded(transferable, MessagePort); const id = transferable[_id]; @@ -244,6 +295,12 @@ kind: "messagePort", data: id, }); + } else if (transferable instanceof ArrayBuffer) { + ArrayPrototypePush(serializedTransferables, { + kind: "arrayBuffer", + data: transferedArrayBuffers[arrayBufferI], + }); + arrayBufferI++; } else { throw new DOMException("Value not transferable", "DataCloneError"); } |