-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ses): shim ArrayBuffer.p.transfer
- Loading branch information
Showing
4 changed files
with
109 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { | ||
arrayBufferPrototype, | ||
arrayBufferSlice, | ||
globalThis, | ||
TypeError, | ||
defineProperty, | ||
} from './commons.js'; | ||
|
||
export const shimArrayBufferTransfer = () => { | ||
// @ts-expect-error TODO extend ArrayBuffer type to include transfer, etc. | ||
if (typeof arrayBufferPrototype.transfer === 'function') { | ||
// Assume already exists so does not need to be shimmed. | ||
// Such conditional shimming is ok in this case since ArrayBuffer.p.transfer | ||
// is already officially part of JS. | ||
return; | ||
} | ||
const clone = globalThis.structuredClone; | ||
if (typeof clone !== 'function') { | ||
// Indeed, Node <= 16 has neither. | ||
throw TypeError( | ||
`Can only shim missing ArrayBuffer.prototype.transfer on a platform with "structuredClone"`, | ||
); | ||
} | ||
|
||
/** | ||
* @type {ThisType<ArrayBuffer>} | ||
*/ | ||
const methods = { | ||
/** | ||
* @param {number} [newLength] | ||
*/ | ||
transfer(newLength = undefined) { | ||
// Hopefully, a zero-length slice is cheap, but still enforces that | ||
// `this` is a genuine `ArrayBuffer` exotic object. | ||
arrayBufferSlice(this, 0, 0); | ||
const oldLength = this.byteLength; | ||
if (newLength === undefined || newLength === oldLength) { | ||
return clone(this, { transfer: [this] }); | ||
} | ||
if (typeof newLength !== 'number') { | ||
throw new TypeError(`transfer newLength if provided must be a number`); | ||
} | ||
if (newLength > oldLength) { | ||
// TODO support this case somehow | ||
throw new TypeError( | ||
`Cannot yet emulate transfer to larger ArrayBuffer ${newLength}`, | ||
); | ||
} | ||
const tmp = clone(this, { transfer: [this] }); | ||
return arrayBufferSlice(tmp, 0, newLength); | ||
}, | ||
}; | ||
|
||
defineProperty(arrayBufferPrototype, 'transfer', { | ||
// @ts-expect-error | ||
value: methods.transfer, | ||
writable: true, | ||
enumerable: false, | ||
configurable: true, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import test from 'ava'; | ||
import '../index.js'; | ||
|
||
lockdown(); | ||
|
||
// The purpose of this test is to see if Array.prototype.transfer works | ||
// correctly enough on platforms like Node 18 or Node 20 that don't yet have | ||
// it natively, and so are testing the shim on those. On platforms where | ||
// Array.prototype.transfer is present, like Node 22, | ||
// we also run the same tests.Thus, | ||
// this test only tests the intersection behavior of the standard and | ||
// the shim. The shim does not yet support a `newLength` argument | ||
// larger than the original. | ||
// | ||
// TODO once the shim supports transfering to a larger length, we must | ||
// test that as well. | ||
|
||
test('ArrayBuffer.p.transfer', t => { | ||
const abX = new ArrayBuffer(3); | ||
t.is(abX.byteLength, 3); | ||
const taX = new Uint8Array(abX); | ||
t.is(taX[2], 0); | ||
t.is(taX[3], undefined); | ||
|
||
// because this test must run on platforms prior to | ||
// ArrayBuffer.prototype.detached, we test detachment by other means. | ||
|
||
const abY = abX.transfer(); | ||
t.is(abY.byteLength, 3); | ||
t.is(abX.byteLength, 0); | ||
const taY = new Uint8Array(abY); | ||
t.is(taX[2], undefined); | ||
t.is(taY[2], 0); | ||
|
||
const abZ = abY.transfer(2); | ||
t.is(abY.byteLength, 0); | ||
t.is(abZ.byteLength, 2); | ||
const taZ = new Uint8Array(abZ); | ||
t.is(taY[2], undefined); | ||
t.is(taZ[2], undefined); | ||
t.is(taZ[1], 0); | ||
}); |