Skip to content

Normative: Account for immutability in ArrayBuffer.prototype.{resize,slice} #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ The [OCapN](https://ocapn.org/) network protocol treats strings and byte-arrays

## Solution

This proposal introduces additional methods and read-only accessor properties to `ArrayBuffer.prototype` that fit naturally into those explained above. Just as a buffer can be resizable or not, or detached or not, this proposal enables buffers to be immutable or not. Just as `transferToFixedSize` moves the contents of a original buffer into a newly created non-resizable buffer, this proposal provides a transfer operation that moves the contents of an original original buffer into a newly created immutable buffer. Altogether, this proposal only adds to `ArrayBuffer.prototype` one method
This proposal introduces additional methods and read-only accessor properties to `ArrayBuffer.prototype` that fit naturally into those explained above. Just as a buffer can be resizable or not, and detached or not, this proposal enables buffers to be immutable or not. Just as `transferToFixedSize` moves the contents of a original buffer into a newly created non-resizable buffer, this proposal provides a transfer operation that moves the contents of an original original buffer into a newly created immutable buffer. Altogether, this proposal only adds to `ArrayBuffer.prototype` one method
- `transferToImmutable() :ArrayBuffer` -- move the contents of the original buffer into a new immutable buffer, detach the original buffer, and return the new buffer.

and one read-only accessor
- `immutable: boolean` -- is this buffer immutable, or can its contents be changed?

An immutable buffer cannot be detached or resized. Its `maxByteLength` is the same as its `byteLength`. A `DataView` or `TypedArray` using an immutable buffer as its backing store can be frozen and immutable. `ArrayBuffer`s, `DataView`s, and `TypedArray`s that are frozen and immutable could be placed in ROM without going beyond JavaScript's official semantics.
An immutable buffer cannot be detached, resized, or further transferred. Its `maxByteLength` is the same as its `byteLength`. A `DataView` or `TypedArray` using an immutable buffer as its backing store can be frozen and immutable. `ArrayBuffer`s, `DataView`s, and `TypedArray`s that are frozen and immutable could be placed in ROM without going beyond JavaScript's official semantics.

The ArrayBuffer `slice` method and TypedArray methods that create new ArrayBuffers (`filter`, `map`, `slice`, `toReversed`, etc.) make no effort to preserve immutability, just like they make no effort to preserve resizability (although use of SpeciesConstructor in those methods means that _lack_ of resizability/immutability in the result cannot be guaranteed for the latter).

## Implementations

Expand Down
62 changes: 62 additions & 0 deletions spec.emu
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,68 @@ contributors: Mark S. Miller, Richard Gibson
</emu-clause>
</ins>

<emu-clause id="sec-arraybuffer.prototype.resize" number="6">
<h1>ArrayBuffer.prototype.resize ( _newLength_ )</h1>
<p>This method performs the following steps when called:</p>
<emu-alg>
1. Let _O_ be the *this* value.
1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferMaxByteLength]]).
1. If IsSharedArrayBuffer(_O_) is *true*, throw a *TypeError* exception.
1. Let _newByteLength_ be ? ToIndex(_newLength_).
1. If IsDetachedBuffer(_O_) is *true*, throw a *TypeError* exception.
1. <ins>If IsImmutableBuffer(_O_) is *true*, throw a *TypeError* exception.</ins>
1. If _newByteLength_ > _O_.[[ArrayBufferMaxByteLength]], throw a *RangeError* exception.
1. Let _hostHandled_ be ? HostResizeArrayBuffer(_O_, _newByteLength_).
1. If _hostHandled_ is ~handled~, return *undefined*.
1. Let _oldBlock_ be _O_.[[ArrayBufferData]].
1. Let _newBlock_ be ? CreateByteDataBlock(_newByteLength_).
1. Let _copyLength_ be min(_newByteLength_, _O_.[[ArrayBufferByteLength]]).
1. Perform CopyDataBlockBytes(_newBlock_, 0, _oldBlock_, 0, _copyLength_).
1. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations may implement this method as in-place growth or shrinkage.
1. Set _O_.[[ArrayBufferData]] to _newBlock_.
1. Set _O_.[[ArrayBufferByteLength]] to _newByteLength_.
1. Return *undefined*.
</emu-alg>
</emu-clause>

<emu-clause id="sec-arraybuffer.prototype.slice">
<h1>ArrayBuffer.prototype.slice ( _start_, _end_ )</h1>
<p>This method performs the following steps when called:</p>
<emu-alg>
1. Let _O_ be the *this* value.
1. Perform ? RequireInternalSlot(_O_, [[ArrayBufferData]]).
1. If IsSharedArrayBuffer(_O_) is *true*, throw a *TypeError* exception.
1. If IsDetachedBuffer(_O_) is *true*, throw a *TypeError* exception.
1. Let _len_ be _O_.[[ArrayBufferByteLength]].
1. Let _relativeStart_ be ? ToIntegerOrInfinity(_start_).
1. If _relativeStart_ = -∞, let _first_ be 0.
1. Else if _relativeStart_ &lt; 0, let _first_ be max(_len_ + _relativeStart_, 0).
1. Else, let _first_ be min(_relativeStart_, _len_).
1. If _end_ is *undefined*, let _relativeEnd_ be _len_; else let _relativeEnd_ be ? ToIntegerOrInfinity(_end_).
1. If _relativeEnd_ = -∞, let _final_ be 0.
1. Else if _relativeEnd_ &lt; 0, let _final_ be max(_len_ + _relativeEnd_, 0).
1. Else, let _final_ be min(_relativeEnd_, _len_).
1. Let _newLen_ be max(_final_ - _first_, 0).
1. Let _ctor_ be ? SpeciesConstructor(_O_, %ArrayBuffer%).
1. Let _new_ be ? Construct(_ctor_, « 𝔽(_newLen_) »).
1. Perform ? RequireInternalSlot(_new_, [[ArrayBufferData]]).
1. If IsSharedArrayBuffer(_new_) is *true*, throw a *TypeError* exception.
1. If IsDetachedBuffer(_new_) is *true*, throw a *TypeError* exception.
1. <ins>If IsImmutableBuffer(_new_) is *true*, throw a *TypeError* exception.</ins>
1. If SameValue(_new_, _O_) is *true*, throw a *TypeError* exception.
1. If _new_.[[ArrayBufferByteLength]] &lt; _newLen_, throw a *TypeError* exception.
1. NOTE: Side-effects of the above steps may have detached or resized _O_.
1. If IsDetachedBuffer(_O_) is *true*, throw a *TypeError* exception.
1. Let _fromBuf_ be _O_.[[ArrayBufferData]].
1. Let _toBuf_ be _new_.[[ArrayBufferData]].
1. Let _currentLen_ be _O_.[[ArrayBufferByteLength]].
1. If _first_ &lt; _currentLen_, then
1. Let _count_ be min(_newLen_, _currentLen_ - _first_).
1. Perform CopyDataBlockBytes(_toBuf_, 0, _fromBuf_, _first_, _count_).
1. Return _new_.
</emu-alg>
</emu-clause>

<ins class="block">

<emu-clause id="sec-arraybuffer.prototype.transfertoimmutable">
Expand Down
Loading