Skip to content

Commit

Permalink
add ArrayBuffer.prototype.transfer and friends Stage 3 proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
zloirock committed Feb 12, 2023
1 parent 081d6f8 commit 139abbf
Show file tree
Hide file tree
Showing 36 changed files with 364 additions and 32 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.**
Expand Down
6 changes: 6 additions & 0 deletions packages/core-js-compat/src/data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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': {
Expand Down
3 changes: 3 additions & 0 deletions packages/core-js-compat/src/modules-by-versions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// empty
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// empty
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// empty
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// empty
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// empty
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// empty
3 changes: 3 additions & 0 deletions packages/core-js/actual/array-buffer/constructor.js
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions packages/core-js/actual/array-buffer/detached.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require('../../stable/array-buffer');
require('../../modules/esnext.array-buffer.detached');
3 changes: 3 additions & 0 deletions packages/core-js/actual/array-buffer/index.js
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require('../../stable/array-buffer');
require('../../modules/esnext.array-buffer.transfer-to-fixed-length');
2 changes: 2 additions & 0 deletions packages/core-js/actual/array-buffer/transfer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require('../../stable/array-buffer');
require('../../modules/esnext.array-buffer.transfer');
3 changes: 3 additions & 0 deletions packages/core-js/full/array-buffer/detached.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var parent = require('../../actual/array-buffer/detached');

module.exports = parent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var parent = require('../../actual/array-buffer/transfer-to-fixed-length');

module.exports = parent;
3 changes: 3 additions & 0 deletions packages/core-js/full/array-buffer/transfer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var parent = require('../../actual/array-buffer/transfer');

module.exports = parent;
12 changes: 12 additions & 0 deletions packages/core-js/internals/array-buffer-byte-length.js
Original file line number Diff line number Diff line change
@@ -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;
};
14 changes: 14 additions & 0 deletions packages/core-js/internals/array-buffer-is-detached.js
Original file line number Diff line number Diff line change
@@ -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;
}
};
33 changes: 33 additions & 0 deletions packages/core-js/internals/array-buffer-transfer.js
Original file line number Diff line number Diff line change
@@ -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;
};
5 changes: 4 additions & 1 deletion packages/core-js/internals/array-buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
17 changes: 17 additions & 0 deletions packages/core-js/internals/structured-clone-proper-transfer.js
Original file line number Diff line number Diff line change
@@ -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;
});
15 changes: 15 additions & 0 deletions packages/core-js/modules/esnext.array-buffer.detached.js
Original file line number Diff line number Diff line change
@@ -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);
}
});
}
Original file line number Diff line number Diff line change
@@ -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);
}
});
11 changes: 11 additions & 0 deletions packages/core-js/modules/esnext.array-buffer.transfer.js
Original file line number Diff line number Diff line change
@@ -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);
}
});
17 changes: 4 additions & 13 deletions packages/core-js/modules/web.structured-clone.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');

Expand Down Expand Up @@ -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':
Expand Down
3 changes: 3 additions & 0 deletions packages/core-js/proposals/array-buffer-transfer.js
Original file line number Diff line number Diff line change
@@ -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');
1 change: 1 addition & 0 deletions packages/core-js/stage/3.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
9 changes: 9 additions & 0 deletions tests/compat/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
},
Expand Down
4 changes: 4 additions & 0 deletions tests/entries/unit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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');
Expand Down
19 changes: 19 additions & 0 deletions tests/helpers/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
20 changes: 2 additions & 18 deletions tests/unit-global/es.array-buffer.slice.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down
Loading

0 comments on commit 139abbf

Please sign in to comment.