-
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.
- Loading branch information
Showing
8 changed files
with
205 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
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,4 @@ | ||
import { globalThis } from './commons.js'; | ||
import { ImmutableArrayBuffer } from './immutable-array-buffer.js'; | ||
|
||
globalThis.ImmutableArrayBuffer = ImmutableArrayBuffer; |
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,105 @@ | ||
/* eslint-disable class-methods-use-this */ | ||
import { | ||
defineProperties, | ||
arrayBufferSlice, | ||
toStringTagSymbol, | ||
isView, | ||
speciesSymbol, | ||
} from './commons.js'; | ||
|
||
/** | ||
* `ImmutableArrayBuffer` is intended to be a peer of `ArrayBuffer` and | ||
* `SharedArrayBuffer`, but only with the non-mutating methods they have in | ||
* common. We're adding this to ses as if it was already part of the | ||
* language, and we consider this implementation to be a shim for an | ||
* upcoming tc39 proposal. | ||
* | ||
* As a proposal it would take additional steps that would the shim does not: | ||
* - to have `ImmutableArrayBuffer` be shared between | ||
* threads (in spec speak, "agent") in exactly the same way | ||
* `SharedArrayBuffer` is shared between agents. Unlike `SharedArrayBuffer`, | ||
* sharing an `ImmutableArrayBuffer` does not introduce any observable | ||
* concurrency. Unlike `ArrayBuffer`, sharing an `ImmutableArrayBuffer` | ||
* does not detach anything. | ||
* - when used as a backing store of a `TypedArray` or `DataView`, all the query | ||
* methods would work, but the mutating methods would throw. In this sense, | ||
* the wrapping `TypedArray` or `DataView` would also be immutable. | ||
* | ||
* Technically, this file is a ponyfill because it does not install this class | ||
* on `globalThis` or have any other effects on primordial state. It only | ||
* defines and exports a new class. | ||
* `immutable-array-buffer-shim.js` is the corresponding shim which | ||
* installs `ImmutableArrayBuffer` on `globalThis`. It is imported by | ||
* `lockdown`, so that `ImmutableArrayBuffer` can act as-if defined as | ||
* part of the language. | ||
* | ||
* Note that the class isn't immutable until hardened by lockdown. | ||
* Even then, the instances are not immutable until hardened. | ||
* This class does not harden its instances itself to preserve similarity | ||
* with `ArrayBuffer` and `SharedArrayBuffer`. | ||
*/ | ||
export class ImmutableArrayBuffer { | ||
/** @type {ArrayBuffer} */ | ||
#buffer; | ||
|
||
/** | ||
* @param {unknown} arg | ||
* @returns {arg is DataView} | ||
*/ | ||
static isView(arg) { | ||
// TODO should this just share/alias`isView` instead? | ||
return isView(arg); | ||
} | ||
|
||
// TODO how to type this? | ||
static get [speciesSymbol]() { | ||
return ImmutableArrayBuffer; | ||
} | ||
|
||
/** @param {ArrayBuffer} buffer */ | ||
constructor(buffer) { | ||
// This also enforces that `buffer` is a genuine `ArrayBuffer` | ||
this.#buffer = arrayBufferSlice(buffer, 0); | ||
} | ||
|
||
/** @type {number} */ | ||
get byteLength() { | ||
return this.#buffer.byteLength; | ||
} | ||
|
||
/** @type {boolean} */ | ||
get detached() { | ||
return false; | ||
} | ||
|
||
/** @type {number} */ | ||
get maxByteLength() { | ||
// Not underlying maxByteLength, which is irrelevant | ||
return this.#buffer.byteLength; | ||
} | ||
|
||
/** @type {boolean} */ | ||
get resizable() { | ||
return false; | ||
} | ||
|
||
/** | ||
* @param {number} begin | ||
* @param {number} [end] | ||
* @returns {ArrayBuffer} | ||
* Returns a genuine ArrayBuffer, not a SharedArrayBuffer | ||
*/ | ||
slice(begin, end = undefined) { | ||
return arrayBufferSlice(this.#buffer, begin, end); | ||
} | ||
|
||
/** @type {string} */ | ||
get [toStringTagSymbol]() { | ||
// Is remade into a data property by mutating the class prototype below. | ||
return 'ImmutableArrayBuffer'; | ||
} | ||
} | ||
|
||
defineProperties(ImmutableArrayBuffer.prototype, { | ||
[toStringTagSymbol]: { value: 'ImmutableArrayBuffer' }, | ||
}); |
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,65 @@ | ||
/* global ImmutableArrayBuffer */ | ||
// TODO make the above global declaration unnecessary. | ||
// TODO ensure ImmutableArrayBuffer is typed like ArrayBuffer is typed | ||
// Where are these configured? | ||
|
||
import test from 'ava'; | ||
import '../index.js'; | ||
|
||
const { isFrozen } = Object; | ||
|
||
lockdown(); | ||
|
||
test('ImmutableArrayBuffer installed and hardened', t => { | ||
t.true(isFrozen(ImmutableArrayBuffer)); | ||
t.true(isFrozen(ImmutableArrayBuffer.isView)); | ||
t.true(isFrozen(ImmutableArrayBuffer.prototype)); | ||
t.true(isFrozen(ImmutableArrayBuffer.prototype.slice)); | ||
}); | ||
|
||
test('ImmutableArrayBuffer ops', t => { | ||
const ab1 = new ArrayBuffer(2, { maxByteLength: 7 }); | ||
const ta1 = new Uint8Array(ab1); | ||
ta1[0] = 3; | ||
ta1[1] = 4; | ||
const iab = new ImmutableArrayBuffer(ab1); | ||
t.true(iab instanceof ImmutableArrayBuffer); | ||
t.false(iab instanceof ArrayBuffer); | ||
ta1[1] = 5; | ||
const ab2 = iab.slice(0); | ||
const ta2 = new Uint8Array(ab2); | ||
t.is(ta1[1], 5); | ||
t.is(ta2[1], 4); | ||
ta2[1] = 6; | ||
|
||
const ab3 = iab.slice(0); | ||
t.false(ab3 instanceof ImmutableArrayBuffer); | ||
t.true(ab3 instanceof ArrayBuffer); | ||
|
||
const ta3 = new Uint8Array(ab3); | ||
t.is(ta1[1], 5); | ||
t.is(ta2[1], 6); | ||
t.is(ta3[1], 4); | ||
|
||
t.false(ArrayBuffer.isView({})); | ||
t.false(ImmutableArrayBuffer.isView({})); | ||
const dv1 = new DataView(ab1); | ||
t.true(ArrayBuffer.isView(dv1)); | ||
t.true(ImmutableArrayBuffer.isView(dv1)); | ||
|
||
t.is(ImmutableArrayBuffer[Symbol.species], ImmutableArrayBuffer); | ||
|
||
t.is(ab1.byteLength, 2); | ||
t.is(iab.byteLength, 2); | ||
t.is(ab2.byteLength, 2); | ||
|
||
t.is(iab.maxByteLength, 2); | ||
if ('maxByteLength' in ab1) { | ||
// ArrayBuffer.p.maxByteLength absent from Node 18 | ||
t.is(ab1.maxByteLength, 7); | ||
t.is(ab2.maxByteLength, 2); | ||
} | ||
|
||
t.false(iab.detached); | ||
t.false(iab.resizable); | ||
}); |