diff --git a/CHANGELOG.md b/CHANGELOG.md index 37d75170f75c..5714583fe6ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ - `JSON.rawJSON` - `JSON.isRawJSON` - `JSON.stringify` patched for support `JSON.rawJSON` +- Added [`ArrayBuffer.prototype.transfer` and friends Stage 3 proposal](https://github.com/tc39/proposal-arraybuffer-transfer): + - Built-ins: + - `ArrayBuffer.prototype.detached` + - `ArrayBuffer.prototype.transfer` (only in runtime with native `structuredClone` with `ArrayBuffer` transfer support) + - `ArrayBuffer.prototype.transferToFixedLength` (only in runtime with native `structuredClone` with `ArrayBuffer` transfer support) + - In backwards, in runtimes with native `ArrayBuffer.prototype.transfer`, but without proper `structuredClone`, added `ArrayBuffer` transfer support to `structuredClone` polyfill - [Iterator Helpers](https://github.com/tc39/proposal-iterator-helpers) proposal: - Splitted into 2 ([sync](https://github.com/tc39/proposal-iterator-helpers) and [async](https://github.com/tc39/proposal-async-iterator-helpers)) proposals, async version moved back to Stage 2, [January 2023 TC39 meeting](https://github.com/babel/proposals/issues/86#issuecomment-1410926068) - Allowed interleaved mapping in `AsyncIterator` helpers, [proposal-iterator-helpers/262](https://github.com/tc39/proposal-iterator-helpers/pull/262) diff --git a/README.md b/README.md index 061ee4f86866..bccdb66fb418 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ structuredClone(new Set([1, 2, 3])); // => new Set([1, 2, 3]) - [`Array` grouping](#array-grouping) - [New `Set` methods](#new-set-methods) - [`JSON.parse` source text access](#jsonparse-source-text-access) + - [`ArrayBuffer.prototype.transfer` and friends](#arraybufferprototypetransfer-and-friends) - [Explicit resource management](#explicit-resource-management) - [Well-formed unicode strings](#well-formed-unicode-strings) - [Stage 2 proposals](#stage-2-proposals) @@ -2309,6 +2310,36 @@ JSON.parse(String(wayTooBig), digitsToBigInt) === wayTooBig; // true const embedded = JSON.stringify({ tooBigForNumber }, bigIntToRawJSON); embedded === '{"tooBigForNumber":9007199254740993}'; // true ``` +##### [`ArrayBuffer.prototype.transfer` and friends](#https://github.com/tc39/proposal-arraybuffer-transfer)[⬆](#index) +Note: **`ArrayBuffer.prototype.{ transfer, transferToFixedLength }` polyfilled only in runtime with native `structuredClone` with `ArrayBuffer` transfer support.** +Modules [`esnext.array-buffer.detached`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array-buffer.detached.js), [`esnext.array-buffer.transfer`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array-buffer.transfer.js), [`esnext.array-buffer.transfer-to-fixed-length`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array-buffer.transfer-to-fixed-length.js). +```js +class ArrayBuffer { + readonly attribute detached: boolean; + transfer(newLength?: number): ArrayBuffer; + transferToFixedLength(newLength?: number): ArrayBuffer; +} +``` +[*CommonJS entry points:*](#commonjs-api) +```js +core-js/proposals/array-buffer-transfer +core-js/actual|full/array-buffer +core-js/actual|full/array-buffer/detached +core-js/actual|full/array-buffer/transfer +core-js/actual|full/array-buffer/transfer-to-fixed-length +``` +[*Examples*](https://tinyurl.com/2y99jj9k): +```js +const buffer = Int8Array.of(1, 2, 3, 4, 5, 6, 7, 8).buffer; +console.log(buffer.byteLength); // => 8 +console.log(buffer.detached); // => false +const newBuffer = buffer.transfer(4); +console.log(buffer.byteLength); // => 0 +console.log(buffer.detached); // => true +console.log(newBuffer.byteLength); // => 4 +console.log(newBuffer.detached); // => false +console.log([...new Int8Array(newBuffer)]); // => [1, 2, 3, 4] +``` ##### [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management)[⬆](#index) Note: **This is only built-ins for this proposal, `using` syntax support requires transpiler support.** diff --git a/packages/core-js-compat/src/data.mjs b/packages/core-js-compat/src/data.mjs index 874038e3ad4f..df2ed8d48fca 100644 --- a/packages/core-js-compat/src/data.mjs +++ b/packages/core-js-compat/src/data.mjs @@ -1893,6 +1893,12 @@ export const data = { }, // TODO: Remove from `core-js@4` 'esnext.array.with': null, + 'esnext.array-buffer.detached': { + }, + 'esnext.array-buffer.transfer': { + }, + 'esnext.array-buffer.transfer-to-fixed-length': { + }, 'esnext.async-disposable-stack.constructor': { }, 'esnext.async-iterator.constructor': { diff --git a/packages/core-js-compat/src/modules-by-versions.mjs b/packages/core-js-compat/src/modules-by-versions.mjs index 6de85ffeb644..73dbf0fac2dd 100644 --- a/packages/core-js-compat/src/modules-by-versions.mjs +++ b/packages/core-js-compat/src/modules-by-versions.mjs @@ -185,6 +185,9 @@ export default { 'es.typed-array.to-reversed', 'es.typed-array.to-sorted', 'es.typed-array.with', + 'esnext.array-buffer.detached', + 'esnext.array-buffer.transfer', + 'esnext.array-buffer.transfer-to-fixed-length', 'esnext.function.demethodize', 'esnext.iterator.range', 'esnext.json.is-raw-json', diff --git a/packages/core-js-pure/override/internals/array-buffer-byte-length.js b/packages/core-js-pure/override/internals/array-buffer-byte-length.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/internals/array-buffer-byte-length.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js-pure/override/internals/array-buffer-is-detached.js b/packages/core-js-pure/override/internals/array-buffer-is-detached.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/internals/array-buffer-is-detached.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js-pure/override/internals/array-buffer-transfer.js b/packages/core-js-pure/override/internals/array-buffer-transfer.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/internals/array-buffer-transfer.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js-pure/override/modules/esnext.array-buffer.detached.js b/packages/core-js-pure/override/modules/esnext.array-buffer.detached.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/modules/esnext.array-buffer.detached.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js-pure/override/modules/esnext.array-buffer.transfer-to-fixed-length.js b/packages/core-js-pure/override/modules/esnext.array-buffer.transfer-to-fixed-length.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/modules/esnext.array-buffer.transfer-to-fixed-length.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js-pure/override/modules/esnext.array-buffer.transfer.js b/packages/core-js-pure/override/modules/esnext.array-buffer.transfer.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/modules/esnext.array-buffer.transfer.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js/actual/array-buffer/constructor.js b/packages/core-js/actual/array-buffer/constructor.js index 4c615c87f9dc..c6e19354cd9a 100644 --- a/packages/core-js/actual/array-buffer/constructor.js +++ b/packages/core-js/actual/array-buffer/constructor.js @@ -1,3 +1,6 @@ var parent = require('../../stable/array-buffer/constructor'); +require('../../modules/esnext.array-buffer.detached'); +require('../../modules/esnext.array-buffer.transfer'); +require('../../modules/esnext.array-buffer.transfer-to-fixed-length'); module.exports = parent; diff --git a/packages/core-js/actual/array-buffer/detached.js b/packages/core-js/actual/array-buffer/detached.js new file mode 100644 index 000000000000..023c3bd489f9 --- /dev/null +++ b/packages/core-js/actual/array-buffer/detached.js @@ -0,0 +1,2 @@ +require('../../stable/array-buffer'); +require('../../modules/esnext.array-buffer.detached'); diff --git a/packages/core-js/actual/array-buffer/index.js b/packages/core-js/actual/array-buffer/index.js index d9e47296ebf4..8637cd1ef364 100644 --- a/packages/core-js/actual/array-buffer/index.js +++ b/packages/core-js/actual/array-buffer/index.js @@ -1,3 +1,6 @@ var parent = require('../../stable/array-buffer'); +require('../../modules/esnext.array-buffer.detached'); +require('../../modules/esnext.array-buffer.transfer'); +require('../../modules/esnext.array-buffer.transfer-to-fixed-length'); module.exports = parent; diff --git a/packages/core-js/actual/array-buffer/transfer-to-fixed-length.js b/packages/core-js/actual/array-buffer/transfer-to-fixed-length.js new file mode 100644 index 000000000000..35bc596e3181 --- /dev/null +++ b/packages/core-js/actual/array-buffer/transfer-to-fixed-length.js @@ -0,0 +1,2 @@ +require('../../stable/array-buffer'); +require('../../modules/esnext.array-buffer.transfer-to-fixed-length'); diff --git a/packages/core-js/actual/array-buffer/transfer.js b/packages/core-js/actual/array-buffer/transfer.js new file mode 100644 index 000000000000..33cdc6bad96a --- /dev/null +++ b/packages/core-js/actual/array-buffer/transfer.js @@ -0,0 +1,2 @@ +require('../../stable/array-buffer'); +require('../../modules/esnext.array-buffer.transfer'); diff --git a/packages/core-js/full/array-buffer/detached.js b/packages/core-js/full/array-buffer/detached.js new file mode 100644 index 000000000000..cdbabc40aa67 --- /dev/null +++ b/packages/core-js/full/array-buffer/detached.js @@ -0,0 +1,3 @@ +var parent = require('../../actual/array-buffer/detached'); + +module.exports = parent; diff --git a/packages/core-js/full/array-buffer/transfer-to-fixed-length.js b/packages/core-js/full/array-buffer/transfer-to-fixed-length.js new file mode 100644 index 000000000000..40dc56a72165 --- /dev/null +++ b/packages/core-js/full/array-buffer/transfer-to-fixed-length.js @@ -0,0 +1,3 @@ +var parent = require('../../actual/array-buffer/transfer-to-fixed-length'); + +module.exports = parent; diff --git a/packages/core-js/full/array-buffer/transfer.js b/packages/core-js/full/array-buffer/transfer.js new file mode 100644 index 000000000000..c9a74e404749 --- /dev/null +++ b/packages/core-js/full/array-buffer/transfer.js @@ -0,0 +1,3 @@ +var parent = require('../../actual/array-buffer/transfer'); + +module.exports = parent; diff --git a/packages/core-js/internals/array-buffer-byte-length.js b/packages/core-js/internals/array-buffer-byte-length.js new file mode 100644 index 000000000000..b9b449d1c718 --- /dev/null +++ b/packages/core-js/internals/array-buffer-byte-length.js @@ -0,0 +1,12 @@ +var uncurryThisAccessor = require('../internals/function-uncurry-this-accessor'); +var classof = require('../internals/classof-raw'); + +var $TypeError = TypeError; + +// Includes +// - Perform ? RequireInternalSlot(O, [[ArrayBufferData]]). +// - If IsSharedArrayBuffer(O) is true, throw a TypeError exception. +module.exports = uncurryThisAccessor(ArrayBuffer.prototype, 'byteLength', 'get') || function (O) { + if (classof(O) != 'ArrayBuffer') throw $TypeError('ArrayBuffer expected'); + return O.byteLength; +}; diff --git a/packages/core-js/internals/array-buffer-is-detached.js b/packages/core-js/internals/array-buffer-is-detached.js new file mode 100644 index 000000000000..f6679b9db1f9 --- /dev/null +++ b/packages/core-js/internals/array-buffer-is-detached.js @@ -0,0 +1,14 @@ +var uncurryThis = require('../internals/function-uncurry-this'); +var arrayBufferByteLength = require('../internals/array-buffer-byte-length'); + +var slice = uncurryThis(ArrayBuffer.prototype.slice); + +module.exports = function (O) { + if (arrayBufferByteLength(O) !== 0) return false; + try { + slice(O, 0, 0); + return false; + } catch (error) { + return true; + } +}; diff --git a/packages/core-js/internals/array-buffer-transfer.js b/packages/core-js/internals/array-buffer-transfer.js new file mode 100644 index 000000000000..93b822bb2bf1 --- /dev/null +++ b/packages/core-js/internals/array-buffer-transfer.js @@ -0,0 +1,33 @@ +var global = require('../internals/global'); +var uncurryThis = require('../internals/function-uncurry-this'); +var uncurryThisAccessor = require('../internals/function-uncurry-this-accessor'); +var toIndex = require('../internals/to-index'); +var isDetached = require('../internals/array-buffer-is-detached'); +var arrayBufferByteLength = require('../internals/array-buffer-byte-length'); +var PROPER_TRANSFER = require('../internals/structured-clone-proper-transfer'); + +var TypeError = global.TypeError; +var structuredClone = global.structuredClone; +var ArrayBuffer = global.ArrayBuffer; +var DataView = global.DataView; +var ArrayBufferPrototype = ArrayBuffer.prototype; +var DataViewPrototype = DataView.prototype; +var slice = uncurryThis(ArrayBufferPrototype.slice); +var isResizable = uncurryThisAccessor(ArrayBufferPrototype, 'resizable', 'get'); +var maxByteLength = uncurryThisAccessor(ArrayBufferPrototype, 'maxByteLength', 'get'); +var getInt8 = uncurryThis(DataViewPrototype.getInt8); +var setInt8 = uncurryThis(DataViewPrototype.setInt8); + +module.exports = PROPER_TRANSFER && function (arrayBuffer, newLength, preserveResizability) { + var byteLength = arrayBufferByteLength(arrayBuffer); + var newByteLength = newLength === undefined ? byteLength : toIndex(newLength); + if (isDetached(arrayBuffer)) throw TypeError('ArrayBuffer is detached'); + var newBuffer = structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); + if (byteLength <= newByteLength) return newBuffer; + if (!preserveResizability || !isResizable || !isResizable(newBuffer)) return slice(newBuffer, 0, newByteLength); + var newNewBuffer = new ArrayBuffer(newByteLength, maxByteLength && { maxByteLength: maxByteLength(newBuffer) }); + var a = new DataView(newBuffer); + var b = new DataView(newNewBuffer); + for (var i = 0; i < newByteLength; i++) setInt8(b, i, getInt8(a, i)); + return newNewBuffer; +}; diff --git a/packages/core-js/internals/array-buffer.js b/packages/core-js/internals/array-buffer.js index 3b5e8d03caeb..66b3d09f1003 100644 --- a/packages/core-js/internals/array-buffer.js +++ b/packages/core-js/internals/array-buffer.js @@ -105,7 +105,10 @@ if (!NATIVE_ARRAY_BUFFER) { bytes: fill(Array(byteLength), 0), byteLength: byteLength }); - if (!DESCRIPTORS) this.byteLength = byteLength; + if (!DESCRIPTORS) { + this.byteLength = byteLength; + this.detached = false; + } }; ArrayBufferPrototype = $ArrayBuffer[PROTOTYPE]; diff --git a/packages/core-js/internals/structured-clone-proper-transfer.js b/packages/core-js/internals/structured-clone-proper-transfer.js new file mode 100644 index 000000000000..eacb18a3a381 --- /dev/null +++ b/packages/core-js/internals/structured-clone-proper-transfer.js @@ -0,0 +1,17 @@ +var global = require('../internals/global'); +var fails = require('../internals/fails'); +var V8 = require('../internals/engine-v8-version'); +var IS_BROWSER = require('../internals/engine-is-browser'); +var IS_DENO = require('../internals/engine-is-deno'); +var IS_NODE = require('../internals/engine-is-node'); + +var structuredClone = global.structuredClone; + +module.exports = !!structuredClone && !fails(function () { + // prevent V8 ArrayBufferDetaching protector cell invalidation and performance degradation + // https://github.com/zloirock/core-js/issues/679 + if ((IS_DENO && V8 > 92) || (IS_NODE && V8 > 94) || (IS_BROWSER && V8 > 97)) return false; + var buffer = new ArrayBuffer(8); + var clone = structuredClone(buffer, { transfer: [buffer] }); + return buffer.byteLength != 0 || clone.byteLength != 8; +}); diff --git a/packages/core-js/modules/esnext.array-buffer.detached.js b/packages/core-js/modules/esnext.array-buffer.detached.js new file mode 100644 index 000000000000..3aa6d9c36ab6 --- /dev/null +++ b/packages/core-js/modules/esnext.array-buffer.detached.js @@ -0,0 +1,15 @@ +'use strict'; +var DESCRIPTORS = require('../internals/descriptors'); +var defineBuiltInAccessor = require('../internals/define-built-in-accessor'); +var isDetached = require('../internals/array-buffer-is-detached'); + +var ArrayBufferPrototype = ArrayBuffer.prototype; + +if (DESCRIPTORS && !('detached' in ArrayBufferPrototype)) { + defineBuiltInAccessor(ArrayBufferPrototype, 'detached', { + configurable: true, + get: function detached() { + return isDetached(this); + } + }); +} diff --git a/packages/core-js/modules/esnext.array-buffer.transfer-to-fixed-length.js b/packages/core-js/modules/esnext.array-buffer.transfer-to-fixed-length.js new file mode 100644 index 000000000000..55a9f8a7f08a --- /dev/null +++ b/packages/core-js/modules/esnext.array-buffer.transfer-to-fixed-length.js @@ -0,0 +1,11 @@ +'use strict'; +var $ = require('../internals/export'); +var $transfer = require('../internals/array-buffer-transfer'); + +// `ArrayBuffer.prototype.transferToFixedLength` method +// https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfertofixedlength +if ($transfer) $({ target: 'ArrayBuffer', proto: true }, { + transferToFixedLength: function transferToFixedLength() { + return $transfer(this, arguments.length ? arguments[0] : undefined, false); + } +}); diff --git a/packages/core-js/modules/esnext.array-buffer.transfer.js b/packages/core-js/modules/esnext.array-buffer.transfer.js new file mode 100644 index 000000000000..197658dee9d0 --- /dev/null +++ b/packages/core-js/modules/esnext.array-buffer.transfer.js @@ -0,0 +1,11 @@ +'use strict'; +var $ = require('../internals/export'); +var $transfer = require('../internals/array-buffer-transfer'); + +// `ArrayBuffer.prototype.transfer` method +// https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfer +if ($transfer) $({ target: 'ArrayBuffer', proto: true }, { + transfer: function transfer() { + return $transfer(this, arguments.length ? arguments[0] : undefined, true); + } +}); diff --git a/packages/core-js/modules/web.structured-clone.js b/packages/core-js/modules/web.structured-clone.js index 75c598089049..75621119c83f 100644 --- a/packages/core-js/modules/web.structured-clone.js +++ b/packages/core-js/modules/web.structured-clone.js @@ -22,10 +22,7 @@ var getRegExpFlags = require('../internals/regexp-get-flags'); var MapHelpers = require('../internals/map-helpers'); var SetHelpers = require('../internals/set-helpers'); var ERROR_STACK_INSTALLABLE = require('../internals/error-stack-installable'); -var V8 = require('../internals/engine-v8-version'); -var IS_BROWSER = require('../internals/engine-is-browser'); -var IS_DENO = require('../internals/engine-is-deno'); -var IS_NODE = require('../internals/engine-is-node'); +var PROPER_TRANSFER = require('../internals/structured-clone-proper-transfer'); var Object = global.Object; var Array = global.Array; @@ -427,15 +424,6 @@ var structuredCloneInternal = function (value, map) { return cloned; }; -var PROPER_TRANSFER = nativeStructuredClone && !fails(function () { - // prevent V8 ArrayBufferDetaching protector cell invalidation and performance degradation - // https://github.com/zloirock/core-js/issues/679 - if ((IS_DENO && V8 > 92) || (IS_NODE && V8 > 94) || (IS_BROWSER && V8 > 97)) return false; - var buffer = new ArrayBuffer(8); - var clone = nativeStructuredClone(buffer, { transfer: [buffer] }); - return buffer.byteLength != 0 || clone.byteLength != 8; -}); - var tryToTransfer = function (rawTransfer, map) { if (!isObject(rawTransfer)) throw TypeError('Transfer option cannot be converted to a sequence'); @@ -478,6 +466,9 @@ var tryToTransfer = function (rawTransfer, map) { } catch (error) { /* empty */ } break; case 'ArrayBuffer': + if (!isCallable(value.transfer)) throwUnpolyfillable(type, TRANSFERRING); + transferred = value.transfer(); + break; case 'MediaSourceHandle': case 'MessagePort': case 'OffscreenCanvas': diff --git a/packages/core-js/proposals/array-buffer-transfer.js b/packages/core-js/proposals/array-buffer-transfer.js new file mode 100644 index 000000000000..7d72f5b45752 --- /dev/null +++ b/packages/core-js/proposals/array-buffer-transfer.js @@ -0,0 +1,3 @@ +require('../modules/esnext.array-buffer.detached'); +require('../modules/esnext.array-buffer.transfer'); +require('../modules/esnext.array-buffer.transfer-to-fixed-length'); diff --git a/packages/core-js/stage/3.js b/packages/core-js/stage/3.js index ccd50ad95610..26cc6b2eec8b 100644 --- a/packages/core-js/stage/3.js +++ b/packages/core-js/stage/3.js @@ -2,6 +2,7 @@ var parent = require('./4'); require('../proposals/array-from-async-stage-2'); require('../proposals/array-grouping-stage-3-2'); +require('../proposals/array-buffer-transfer'); require('../proposals/explicit-resource-management'); require('../proposals/iterator-helpers-stage-3-2'); require('../proposals/json-parse-with-source'); diff --git a/tests/compat/tests.js b/tests/compat/tests.js index f693f4464ad2..0d5206b25730 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -1463,6 +1463,15 @@ GLOBAL.tests = { 'esnext.array.unique-by': function () { return [].uniqueBy; }, + 'esnext.array-buffer.detached': function () { + return 'detached' in ArrayBuffer.prototype; + }, + 'esnext.array-buffer.transfer': function () { + return ArrayBuffer.prototype.transfer; + }, + 'esnext.array-buffer.transfer-to-fixed-length': function () { + return ArrayBuffer.prototype.transferToFixedLength; + }, 'esnext.async-disposable-stack.constructor': function () { return typeof AsyncDisposableStack == 'function'; }, diff --git a/tests/entries/unit.mjs b/tests/entries/unit.mjs index 340b60ef94a1..b3c96500dff3 100644 --- a/tests/entries/unit.mjs +++ b/tests/entries/unit.mjs @@ -638,6 +638,9 @@ for (PATH of ['core-js-pure', 'core-js']) { ok(typeof load(NS, 'array/virtual/group-to-map') == 'function'); ok(typeof load(NS, 'array/virtual/group-by') == 'function'); ok(typeof load(NS, 'array/virtual/group-by-to-map') == 'function'); + load(NS, 'array-buffer/detached'); + load(NS, 'array-buffer/transfer'); + load(NS, 'array-buffer/transfer-to-fixed-length'); ok(typeof load(NS, 'async-iterator') == 'function'); ok(typeof load(NS, 'async-iterator/drop') == 'function'); ok(typeof load(NS, 'async-iterator/every') == 'function'); @@ -890,6 +893,7 @@ for (PATH of ['core-js-pure', 'core-js']) { load('proposals/array-is-template-object'); load('proposals/array-last'); load('proposals/array-unique'); + load('proposals/array-buffer-transfer'); load('proposals/async-explicit-resource-management'); load('proposals/async-iteration'); load('proposals/async-iterator-helpers'); diff --git a/tests/helpers/helpers.js b/tests/helpers/helpers.js index 19077cf0941f..8e25a6844aa0 100644 --- a/tests/helpers/helpers.js +++ b/tests/helpers/helpers.js @@ -159,3 +159,22 @@ export function fromSource(source) { return Function(`return ${ source }`)(); } catch { /* empty */ } } + +export function arrayToBuffer(array) { + const { length } = array; + const buffer = new ArrayBuffer(length); + const view = new DataView(buffer); + for (let i = 0; i < length; ++i) { + view.setUint8(i, array[i]); + } + return buffer; +} + +export function bufferToArray(buffer) { + const array = []; + const view = new DataView(buffer); + for (let i = 0, { byteLength } = view; i < byteLength; ++i) { + array.push(view.getUint8(i)); + } + return array; +} diff --git a/tests/unit-global/es.array-buffer.slice.js b/tests/unit-global/es.array-buffer.slice.js index ed7569ffbf42..b5acbcc5887a 100644 --- a/tests/unit-global/es.array-buffer.slice.js +++ b/tests/unit-global/es.array-buffer.slice.js @@ -1,24 +1,8 @@ +import { arrayToBuffer, bufferToArray } from '../helpers/helpers'; + QUnit.test('ArrayBuffer#slice', assert => { const { slice } = ArrayBuffer.prototype; - function arrayToBuffer(it) { - const buffer = new ArrayBuffer(it.length); - const view = new DataView(buffer); - for (let i = 0, { length } = it; i < length; ++i) { - view.setUint8(i, it[i]); - } - return buffer; - } - - function bufferToArray(it) { - const results = []; - const view = new DataView(it); - for (let i = 0, { byteLength } = view; i < byteLength; ++i) { - results.push(view.getUint8(i)); - } - return results; - } - assert.isFunction(slice); assert.arity(slice, 2); assert.name(slice, 'slice'); diff --git a/tests/unit-global/esnext.array-buffer.detached.js b/tests/unit-global/esnext.array-buffer.detached.js new file mode 100644 index 000000000000..dc459e3fe640 --- /dev/null +++ b/tests/unit-global/esnext.array-buffer.detached.js @@ -0,0 +1,32 @@ +/* eslint-disable es/no-shared-array-buffer -- testing */ +import { DESCRIPTORS } from '../helpers/constants'; + +QUnit.test('ArrayBuffer#detached', assert => { + assert.same(new ArrayBuffer(8).detached, false, 'default'); + + const detached = new ArrayBuffer(8); + try { + structuredClone(detached, { transfer: [detached] }); + } catch (error) { /* empty */ } + + if (detached.length === 0) { + assert.same(detached.detached, true, 'detached'); + } + + if (DESCRIPTORS) { + const { get, configurable, enumerable } = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'detached'); + assert.same(configurable, true, 'configurable'); + assert.same(enumerable, false, 'non-enumerable'); + assert.isFunction(get); + assert.looksNative(get); + assert.throws(() => get.call(null), TypeError, 'non-generic-1'); + assert.throws(() => get(), TypeError, 'non-generic-2'); + assert.throws(() => get.call(1), TypeError, 'non-generic-3'); + assert.throws(() => get.call(true), TypeError, 'non-generic-4'); + assert.throws(() => get.call(''), TypeError, 'non-generic-5'); + assert.throws(() => get.call({}), TypeError, 'non-generic-6'); + if (typeof SharedArrayBuffer == 'function') { + assert.throws(() => get.call(new SharedArrayBuffer(8)), TypeError, 'non-generic-7'); + } + } +}); diff --git a/tests/unit-global/esnext.array-buffer.transfer-to-fixed-length.js b/tests/unit-global/esnext.array-buffer.transfer-to-fixed-length.js new file mode 100644 index 000000000000..4fc9d895fe63 --- /dev/null +++ b/tests/unit-global/esnext.array-buffer.transfer-to-fixed-length.js @@ -0,0 +1,50 @@ +/* eslint-disable es/no-shared-array-buffer -- testing */ +import { GLOBAL } from '../helpers/constants'; +import { arrayToBuffer, bufferToArray } from '../helpers/helpers'; + +const transferToFixedLength = GLOBAL?.ArrayBuffer?.prototype?.transferToFixedLength; + +if (transferToFixedLength) QUnit.test('ArrayBuffer#transferToFixedLength', assert => { + assert.isFunction(transferToFixedLength); + assert.arity(transferToFixedLength, 0); + assert.name(transferToFixedLength, 'transferToFixedLength'); + assert.looksNative(transferToFixedLength); + assert.nonEnumerable(ArrayBuffer.prototype, 'transferToFixedLength'); + + const DETACHED = 'detached' in ArrayBuffer.prototype; + + const array = [1, 2, 3, 4, 5, 6, 7, 8]; + + let buffer = arrayToBuffer(array); + let transferred = buffer.transferToFixedLength(); + assert.notSame(transferred, buffer, 'returns new buffer 1'); + assert.true(transferred instanceof ArrayBuffer, 'returns ArrayBuffer 1'); + assert.same(buffer.byteLength, 0, 'original array length 1'); + if (DETACHED) assert.true(buffer.detached, 'original array detached 1'); + assert.same(transferred.byteLength, 8, 'proper transferred byteLength 1'); + assert.arrayEqual(bufferToArray(transferred), array, 'properly copied 1'); + + buffer = arrayToBuffer(array); + transferred = buffer.transferToFixedLength(5); + assert.notSame(transferred, buffer, 'returns new buffer 2'); + assert.true(transferred instanceof ArrayBuffer, 'returns ArrayBuffer 2'); + assert.same(buffer.byteLength, 0, 'original array length 2'); + if (DETACHED) assert.true(buffer.detached, 'original array detached 2'); + assert.same(transferred.byteLength, 5, 'proper transferred byteLength 2'); + assert.arrayEqual(bufferToArray(transferred), [1, 2, 3, 4, 5], 'properly copied 2'); + + buffer = arrayToBuffer(array); + transferred = buffer.transferToFixedLength(16.7); + assert.notSame(transferred, buffer, 'returns new buffer 3'); + assert.true(transferred instanceof ArrayBuffer, 'returns ArrayBuffer 3'); + assert.same(buffer.byteLength, 0, 'original array length 3'); + if (DETACHED) assert.true(buffer.detached, 'original array detached 3'); + assert.same(transferred.byteLength, 8, 'proper transferred byteLength 3'); + assert.arrayEqual(bufferToArray(transferred), array, 'properly copied 3'); + + assert.throws(() => arrayToBuffer(array).transferToFixedLength(-1), RangeError, 'negative length'); + assert.throws(() => transferToFixedLength.call({}), TypeError, 'non-generic-1'); + if (typeof SharedArrayBuffer == 'function') { + assert.throws(() => transferToFixedLength.call(new SharedArrayBuffer(8)), TypeError, 'non-generic-2'); + } +}); diff --git a/tests/unit-global/esnext.array-buffer.transfer.js b/tests/unit-global/esnext.array-buffer.transfer.js new file mode 100644 index 000000000000..45a8bda9a5ae --- /dev/null +++ b/tests/unit-global/esnext.array-buffer.transfer.js @@ -0,0 +1,50 @@ +/* eslint-disable es/no-shared-array-buffer -- testing */ +import { GLOBAL } from '../helpers/constants'; +import { arrayToBuffer, bufferToArray } from '../helpers/helpers'; + +const transfer = GLOBAL?.ArrayBuffer?.prototype?.transfer; + +if (transfer) QUnit.test('ArrayBuffer#transfer', assert => { + assert.isFunction(transfer); + assert.arity(transfer, 0); + assert.name(transfer, 'transfer'); + assert.looksNative(transfer); + assert.nonEnumerable(ArrayBuffer.prototype, 'transfer'); + + const DETACHED = 'detached' in ArrayBuffer.prototype; + + const array = [1, 2, 3, 4, 5, 6, 7, 8]; + + let buffer = arrayToBuffer(array); + let transferred = buffer.transfer(); + assert.notSame(transferred, buffer, 'returns new buffer 1'); + assert.true(transferred instanceof ArrayBuffer, 'returns ArrayBuffer 1'); + assert.same(buffer.byteLength, 0, 'original array length 1'); + if (DETACHED) assert.true(buffer.detached, 'original array detached 1'); + assert.same(transferred.byteLength, 8, 'proper transferred byteLength 1'); + assert.arrayEqual(bufferToArray(transferred), array, 'properly copied 1'); + + buffer = arrayToBuffer(array); + transferred = buffer.transfer(5); + assert.notSame(transferred, buffer, 'returns new buffer 2'); + assert.true(transferred instanceof ArrayBuffer, 'returns ArrayBuffer 2'); + assert.same(buffer.byteLength, 0, 'original array length 2'); + if (DETACHED) assert.true(buffer.detached, 'original array detached 2'); + assert.same(transferred.byteLength, 5, 'proper transferred byteLength 2'); + assert.arrayEqual(bufferToArray(transferred), [1, 2, 3, 4, 5], 'properly copied 2'); + + buffer = arrayToBuffer(array); + transferred = buffer.transfer(16.7); + assert.notSame(transferred, buffer, 'returns new buffer 3'); + assert.true(transferred instanceof ArrayBuffer, 'returns ArrayBuffer 3'); + assert.same(buffer.byteLength, 0, 'original array length 3'); + if (DETACHED) assert.true(buffer.detached, 'original array detached 3'); + assert.same(transferred.byteLength, 8, 'proper transferred byteLength 3'); + assert.arrayEqual(bufferToArray(transferred), array, 'properly copied 3'); + + assert.throws(() => arrayToBuffer(array).transfer(-1), RangeError, 'negative length'); + assert.throws(() => transfer.call({}), TypeError, 'non-generic-1'); + if (typeof SharedArrayBuffer == 'function') { + assert.throws(() => transfer.call(new SharedArrayBuffer(8)), TypeError, 'non-generic-2'); + } +});