From 64549731b6cfe3729cb42896ab1f8229b62ff968 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Fri, 5 May 2023 19:22:42 +0800 Subject: [PATCH] src: throw DataCloneError on transfering untransferable objects The HTML StructuredSerializeWithTransfer algorithm defines that when an untransferable object is in the transfer list, a DataCloneError is thrown. An array buffer that is already transferred is also considered as untransferable. PR-URL: https://github.com/nodejs/node/pull/47604 Reviewed-By: James M Snell Reviewed-By: Benjamin Gruenbaum Reviewed-By: Joyee Cheung --- doc/api/worker_threads.md | 44 +++++++++++++++++-- lib/internal/buffer.js | 10 +++++ lib/internal/modules/esm/worker.js | 16 +++++-- lib/worker_threads.js | 2 + src/env_properties.h | 4 +- src/node_messaging.cc | 16 ++++--- test/addons/worker-buffer-callback/test.js | 5 ++- .../test_worker_buffer_callback/test.js | 5 ++- .../test-buffer-pool-untransferable.js | 5 ++- .../test-worker-message-port-arraybuffer.js | 7 +++ ...est-worker-message-port-transfer-native.js | 2 +- ...ge-transfer-port-mark-as-untransferable.js | 34 +++++++++++--- 12 files changed, 126 insertions(+), 24 deletions(-) diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index f3f500c9075058..09b695eb96ea29 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -130,8 +130,11 @@ added: - v12.19.0 --> +* `object` {any} Any arbitrary JavaScript value. + Mark an object as not transferable. If `object` occurs in the transfer list of -a [`port.postMessage()`][] call, it is ignored. +a [`port.postMessage()`][] call, an error is thrown. This is a no-op if +`object` is a primitive value. In particular, this makes sense for objects that can be cloned, rather than transferred, and which are used by other objects on the sending side. @@ -150,11 +153,17 @@ const typedArray2 = new Float64Array(pooledBuffer); markAsUntransferable(pooledBuffer); const { port1 } = new MessageChannel(); -port1.postMessage(typedArray1, [ typedArray1.buffer ]); +try { + // This will throw an error, because pooledBuffer is not transferable. + port1.postMessage(typedArray1, [ typedArray1.buffer ]); +} catch (error) { + // error.name === 'DataCloneError' +} // The following line prints the contents of typedArray1 -- it still owns -// its memory and has been cloned, not transferred. Without -// `markAsUntransferable()`, this would print an empty Uint8Array. +// its memory and has not been transferred. Without +// `markAsUntransferable()`, this would print an empty Uint8Array and the +// postMessage call would have succeeded. // typedArray2 is intact as well. console.log(typedArray1); console.log(typedArray2); @@ -162,6 +171,29 @@ console.log(typedArray2); There is no equivalent to this API in browsers. +## `worker.isMarkedAsUntransferable(object)` + + + +* `object` {any} Any JavaScript value. +* Returns: {boolean} + +Check if an object is marked as not transferable with +[`markAsUntransferable()`][]. + +```js +const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads'); + +const pooledBuffer = new ArrayBuffer(8); +markAsUntransferable(pooledBuffer); + +isMarkedAsUntransferable(pooledBuffer); // Returns true. +``` + +There is no equivalent to this API in browsers. + ## `worker.moveMessagePortToContext(port, contextifiedSandbox)`