Proposal proposal-immutable-arraybuffer

Stage 0 Draft / December 5, 2024

Immutable ArrayBuffers

10 Ordinary and Exotic Objects Behaviours

10.4 Built-in Exotic Object Internal Methods and Slots

10.4.5 TypedArray Exotic Objects

10.4.5.1 [[GetOwnProperty]] ( P )

The [[GetOwnProperty]] internal method of a TypedArray O takes argument P (a property key) and returns a normal completion containing either a Property Descriptor or undefined. It performs the following steps when called:

  1. If P is a String, then
    1. Let numericIndex be CanonicalNumericIndexString(P).
    2. If numericIndex is not undefined, then
      1. Let value be TypedArrayGetElement(O, numericIndex).
      2. If value is undefined, return undefined.
      3. Let mutable be true.
      4. If IsImmutableBuffer(O.[[ViewedArrayBuffer]]) is true, set mutable to false.
      5. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true mutable, [[Enumerable]]: true, [[Configurable]]: true mutable }.
  2. Return OrdinaryGetOwnProperty(O, P).

10.4.5.3 [[DefineOwnProperty]] ( P, Desc )

The [[DefineOwnProperty]] internal method of a TypedArray O takes arguments P (a property key) and Desc (a Property Descriptor) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. If P is a String, then
    1. Let numericIndex be CanonicalNumericIndexString(P).
    2. If numericIndex is not undefined, then
      1. If IsValidIntegerIndex(O, numericIndex) is false, return false.
      2. Let mutable be true.
      3. If IsImmutableBuffer(O.[[ViewedArrayBuffer]]) is true, set mutable to false.
      4. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is false not mutable, return false.
      5. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.
      6. If IsAccessorDescriptor(Desc) is true, return false.
      7. If Desc has a [[Writable]] field and Desc.[[Writable]] is false not mutable, return false.
      8. If Desc has a [[Value]] field and mutable is false and SameValue(Desc.[[Value]], TypedArrayGetElement(O, numericIndex)) is false, return false.
      9. If Desc has a [[Value]] field and mutable is true, perform ? TypedArraySetElement(O, numericIndex, Desc.[[Value]]).
      10. Return true.
  2. Return ! OrdinaryDefineOwnProperty(O, P, Desc).

10.4.5.5 [[Set]] ( P, V, Receiver )

The [[Set]] internal method of a TypedArray O takes arguments P (a property key), V (an ECMAScript language value), and Receiver (an ECMAScript language value) and returns either a normal completion containing a Boolean or a throw completion. It performs the following steps when called:

  1. If P is a String, then
    1. Let numericIndex be CanonicalNumericIndexString(P).
    2. If numericIndex is not undefined, then
      1. If SameValue(O, Receiver) is true, then
        1. IsValidIntegerIndex(O, numericIndex) is true and IsImmutableBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
        2. Perform ? TypedArraySetElement(O, numericIndex, V).
        3. Return true.
      2. If IsValidIntegerIndex(O, numericIndex) is false, return true.
  2. Return ? OrdinarySet(O, P, V, Receiver).

10.4.5.16 TypedArraySetElement ( O, index, value )

The abstract operation TypedArraySetElement takes arguments O (a TypedArray), index (a Number), and value (an ECMAScript language value) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called:

  1. If O.[[ContentType]] is bigint, let numValue be ? ToBigInt(value).
  2. Otherwise, let numValue be ? ToNumber(value).
  3. If IsValidIntegerIndex(O, index) is true and IsImmutableBuffer(O.[[ViewedArrayBuffer]]) is false, then
    1. Let offset be O.[[ByteOffset]].
    2. Let elementSize be TypedArrayElementSize(O).
    3. Let byteIndexInBuffer be ((index) × elementSize) + offset.
    4. Let elementType be TypedArrayElementType(O).
    5. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, elementType, numValue, true, unordered).
  4. Return unused.
Note

This operation always appears to succeed, but it has no effect when attempting to write past the end of a TypedArray or to a TypedArray which is backed by a detached or immutable ArrayBuffer.

23 Indexed Collections

23.2 TypedArray Objects

23.2.3 Properties of the %TypedArray% Prototype Object

23.2.3.6 %TypedArray%.prototype.copyWithin ( target, start [ , end ] )

The interpretation and use of the arguments of this method are the same as for Array.prototype.copyWithin as defined in 23.1.3.4.

This method performs the following steps when called:

  1. Let O be the this value.
  2. Let taRecord be ? ValidateTypedArray(O, seq-cst, write).
  3. Let len be TypedArrayLength(taRecord).
  4. Let relativeTarget be ? ToIntegerOrInfinity(target).
  5. If relativeTarget = -∞, let targetIndex be 0.
  6. Else if relativeTarget < 0, let targetIndex be max(len + relativeTarget, 0).
  7. Else, let targetIndex be min(relativeTarget, len).
  8. Let relativeStart be ? ToIntegerOrInfinity(start).
  9. If relativeStart = -∞, let startIndex be 0.
  10. Else if relativeStart < 0, let startIndex be max(len + relativeStart, 0).
  11. Else, let startIndex be min(relativeStart, len).
  12. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
  13. If relativeEnd = -∞, let endIndex be 0.
  14. Else if relativeEnd < 0, let endIndex be max(len + relativeEnd, 0).
  15. Else, let endIndex be min(relativeEnd, len).
  16. Let count be min(endIndex - startIndex, len - targetIndex).
  17. If count > 0, then
    1. NOTE: The copying must be performed in a manner that preserves the bit-level encoding of the source data.
    2. Let buffer be O.[[ViewedArrayBuffer]].
    3. Set taRecord to MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).
    4. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
    5. Set len to TypedArrayLength(taRecord).
    6. Let elementSize be TypedArrayElementSize(O).
    7. Let byteOffset be O.[[ByteOffset]].
    8. Let bufferByteLimit be (len × elementSize) + byteOffset.
    9. Let toByteIndex be (targetIndex × elementSize) + byteOffset.
    10. Let fromByteIndex be (startIndex × elementSize) + byteOffset.
    11. Let countBytes be count × elementSize.
    12. If fromByteIndex < toByteIndex and toByteIndex < fromByteIndex + countBytes, then
      1. Let direction be -1.
      2. Set fromByteIndex to fromByteIndex + countBytes - 1.
      3. Set toByteIndex to toByteIndex + countBytes - 1.
    13. Else,
      1. Let direction be 1.
    14. Repeat, while countBytes > 0,
      1. If fromByteIndex < bufferByteLimit and toByteIndex < bufferByteLimit, then
        1. Let value be GetValueFromBuffer(buffer, fromByteIndex, uint8, true, unordered).
        2. Perform SetValueInBuffer(buffer, toByteIndex, uint8, value, true, unordered).
        3. Set fromByteIndex to fromByteIndex + direction.
        4. Set toByteIndex to toByteIndex + direction.
        5. Set countBytes to countBytes - 1.
      2. Else,
        1. Set countBytes to 0.
  18. Return O.

23.2.3.9 %TypedArray%.prototype.fill ( value [ , start [ , end ] ] )

The interpretation and use of the arguments of this method are the same as for Array.prototype.fill as defined in 23.1.3.7.

This method performs the following steps when called:

  1. Let O be the this value.
  2. Let taRecord be ? ValidateTypedArray(O, seq-cst, write).
  3. Let len be TypedArrayLength(taRecord).
  4. If O.[[ContentType]] is bigint, set value to ? ToBigInt(value).
  5. Otherwise, set value to ? ToNumber(value).
  6. Let relativeStart be ? ToIntegerOrInfinity(start).
  7. If relativeStart = -∞, let startIndex be 0.
  8. Else if relativeStart < 0, let startIndex be max(len + relativeStart, 0).
  9. Else, let startIndex be min(relativeStart, len).
  10. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
  11. If relativeEnd = -∞, let endIndex be 0.
  12. Else if relativeEnd < 0, let endIndex be max(len + relativeEnd, 0).
  13. Else, let endIndex be min(relativeEnd, len).
  14. Set taRecord to MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).
  15. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
  16. Set len to TypedArrayLength(taRecord).
  17. Set endIndex to min(endIndex, len).
  18. Let k be startIndex.
  19. Repeat, while k < endIndex,
    1. Let Pk be ! ToString(𝔽(k)).
    2. Perform ! Set(O, Pk, value, true).
    3. Set k to k + 1.
  20. Return O.

23.2.3.25 %TypedArray%.prototype.reverse ( )

The interpretation and use of the arguments of this method are the same as for Array.prototype.reverse as defined in 23.1.3.26.

This method performs the following steps when called:

  1. Let O be the this value.
  2. Let taRecord be ? ValidateTypedArray(O, seq-cst, write).
  3. Let len be TypedArrayLength(taRecord).
  4. Let middle be floor(len / 2).
  5. Let lower be 0.
  6. Repeat, while lowermiddle,
    1. Let upper be len - lower - 1.
    2. Let upperP be ! ToString(𝔽(upper)).
    3. Let lowerP be ! ToString(𝔽(lower)).
    4. Let lowerValue be ! Get(O, lowerP).
    5. Let upperValue be ! Get(O, upperP).
    6. Perform ! Set(O, lowerP, upperValue, true).
    7. Perform ! Set(O, upperP, lowerValue, true).
    8. Set lower to lower + 1.
  7. Return O.

This method is not generic. The this value must be an object with a [[TypedArrayName]] internal slot.

23.2.3.26 %TypedArray%.prototype.set ( source [ , offset ] )

23.2.3.26.1 SetTypedArrayFromTypedArray ( target, targetOffset, source )

The abstract operation SetTypedArrayFromTypedArray takes arguments target (a TypedArray), targetOffset (a non-negative integer or +∞), and source (a TypedArray) and returns either a normal completion containing unused or a throw completion. It sets multiple values in target, starting at index targetOffset, reading the values from source. It performs the following steps when called:

  1. Let targetBuffer be target.[[ViewedArrayBuffer]].
  2. Let targetRecord be MakeTypedArrayWithBufferWitnessRecord(target, seq-cst).
  3. If IsTypedArrayOutOfBounds(targetRecord) is true, throw a TypeError exception.
  4. If IsImmutableBuffer(target.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
  5. Let targetLength be TypedArrayLength(targetRecord).
  6. Let srcBuffer be source.[[ViewedArrayBuffer]].
  7. Let srcRecord be MakeTypedArrayWithBufferWitnessRecord(source, seq-cst).
  8. If IsTypedArrayOutOfBounds(srcRecord) is true, throw a TypeError exception.
  9. Let srcLength be TypedArrayLength(srcRecord).
  10. Let targetType be TypedArrayElementType(target).
  11. Let targetElementSize be TypedArrayElementSize(target).
  12. Let targetByteOffset be target.[[ByteOffset]].
  13. Let srcType be TypedArrayElementType(source).
  14. Let srcElementSize be TypedArrayElementSize(source).
  15. Let srcByteOffset be source.[[ByteOffset]].
  16. If targetOffset = +∞, throw a RangeError exception.
  17. If srcLength + targetOffset > targetLength, throw a RangeError exception.
  18. If target.[[ContentType]] is not source.[[ContentType]], throw a TypeError exception.
  19. If IsSharedArrayBuffer(srcBuffer) is true, IsSharedArrayBuffer(targetBuffer) is true, and srcBuffer.[[ArrayBufferData]] is targetBuffer.[[ArrayBufferData]], let sameSharedArrayBuffer be true; otherwise, let sameSharedArrayBuffer be false.
  20. If SameValue(srcBuffer, targetBuffer) is true or sameSharedArrayBuffer is true, then
    1. Let srcByteLength be TypedArrayByteLength(srcRecord).
    2. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength).
    3. Let srcByteIndex be 0.
  21. Else,
    1. Let srcByteIndex be srcByteOffset.
  22. Let targetByteIndex be (targetOffset × targetElementSize) + targetByteOffset.
  23. Let limit be targetByteIndex + (targetElementSize × srcLength).
  24. If srcType is targetType, then
    1. NOTE: The transfer must be performed in a manner that preserves the bit-level encoding of the source data.
    2. Repeat, while targetByteIndex < limit,
      1. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, uint8, true, unordered).
      2. Perform SetValueInBuffer(targetBuffer, targetByteIndex, uint8, value, true, unordered).
      3. Set srcByteIndex to srcByteIndex + 1.
      4. Set targetByteIndex to targetByteIndex + 1.
  25. Else,
    1. Repeat, while targetByteIndex < limit,
      1. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, unordered).
      2. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, unordered).
      3. Set srcByteIndex to srcByteIndex + srcElementSize.
      4. Set targetByteIndex to targetByteIndex + targetElementSize.
  26. Return unused.

23.2.3.26.2 SetTypedArrayFromArrayLike ( target, targetOffset, source )

The abstract operation SetTypedArrayFromArrayLike takes arguments target (a TypedArray), targetOffset (a non-negative integer or +∞), and source (an ECMAScript language value, but not a TypedArray) and returns either a normal completion containing unused or a throw completion. It sets multiple values in target, starting at index targetOffset, reading the values from source. It performs the following steps when called:

  1. Let targetRecord be MakeTypedArrayWithBufferWitnessRecord(target, seq-cst).
  2. If IsTypedArrayOutOfBounds(targetRecord) is true, throw a TypeError exception.
  3. If IsImmutableBuffer(target.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
  4. Let targetLength be TypedArrayLength(targetRecord).
  5. Let src be ? ToObject(source).
  6. Let srcLength be ? LengthOfArrayLike(src).
  7. If targetOffset = +∞, throw a RangeError exception.
  8. If srcLength + targetOffset > targetLength, throw a RangeError exception.
  9. Let k be 0.
  10. Repeat, while k < srcLength,
    1. Let Pk be ! ToString(𝔽(k)).
    2. Let value be ? Get(src, Pk).
    3. Let targetIndex be 𝔽(targetOffset + k).
    4. Perform ? TypedArraySetElement(target, targetIndex, value).
    5. Set k to k + 1.
  11. Return unused.

23.2.3.29 %TypedArray%.prototype.sort ( comparator )

This is a distinct method that, except as described below, implements the same requirements as those of Array.prototype.sort as defined in 23.1.3.30. The implementation of this method may be optimized with the knowledge that the this value is an object that has a fixed length and whose integer-indexed properties are not sparse.

This method is not generic. The this value must be an object with a [[TypedArrayName]] internal slot.

It performs the following steps when called:

  1. If comparator is not undefined and IsCallable(comparator) is false, throw a TypeError exception.
  2. Let obj be the this value.
  3. Let taRecord be ? ValidateTypedArray(obj, seq-cst, write).
  4. Let len be TypedArrayLength(taRecord).
  5. NOTE: The following closure performs a numeric comparison rather than the string comparison used in 23.1.3.30.
  6. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparator and performs the following steps when called:
    1. Return ? CompareTypedArrayElements(x, y, comparator).
  7. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, read-through-holes).
  8. Let j be 0.
  9. Repeat, while j < len,
    1. Perform ! Set(obj, ! ToString(𝔽(j)), sortedList[j], true).
    2. Set j to j + 1.
  10. Return obj.
Note

Because NaN always compares greater than any other value (see CompareTypedArrayElements), NaN property values always sort to the end of the result when comparator is not provided.

23.2.4 Abstract Operations for TypedArray Objects

23.2.4.4 ValidateTypedArray ( O, order [ , use ] )

The abstract operation ValidateTypedArray takes arguments O (an ECMAScript language value) and order (seq-cst or unordered) and optional argument use (read or write) and returns either a normal completion containing a TypedArray With Buffer Witness Record or a throw completion. It performs the following steps when called:

  1. If use is not present, set use to read.
  2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
  3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
  4. If use is write and IsImmutableBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
  5. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, order).
  6. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
  7. Return taRecord.

25 Structured Data

25.1 ArrayBuffer Objects

25.1.3 Abstract Operations For ArrayBuffer Objects

25.1.3.1 AllocateArrayBuffer ( constructor, byteLength [ , maxByteLength ] )

The abstract operation AllocateArrayBuffer takes arguments constructor (a constructor) and byteLength (a non-negative integer) and optional argument maxByteLength (a non-negative integer, or either empty or immutable) and returns either a normal completion containing an ArrayBuffer or a throw completion. It is used to create an ArrayBuffer. It performs the following steps when called:

  1. Let slots be « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] ».
  2. If maxByteLength is present and maxByteLength is not empty an integer, let allocatingResizableBuffer be true; otherwise let allocatingResizableBuffer be false.
  3. If allocatingResizableBuffer is true, then
    1. If byteLength > maxByteLength, throw a RangeError exception.
    2. Append [[ArrayBufferMaxByteLength]] to slots.
  4. Else if maxByteLength is immutable, then
    1. Append [[ArrayBufferIsImmutable]] to slots.
  5. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", slots).
  6. Let block be ? CreateByteDataBlock(byteLength).
  7. Set obj.[[ArrayBufferData]] to block.
  8. Set obj.[[ArrayBufferByteLength]] to byteLength.
  9. If allocatingResizableBuffer is true, then
    1. If it is not possible to create a Data Block block consisting of maxByteLength bytes, throw a RangeError exception.
    2. NOTE: Resizable ArrayBuffers are designed to be implementable with in-place growth. Implementations may throw if, for example, virtual memory cannot be reserved up front.
    3. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.
  10. Return obj.

25.1.3.3 ArrayBufferCopyAndDetach ( arrayBuffer, newLength, preserveResizability )

The abstract operation ArrayBufferCopyAndDetach takes arguments arrayBuffer (an ECMAScript language value), newLength (an ECMAScript language value), and preserveResizability (preserve-resizability, fixed-length, or immutable) and returns either a normal completion containing an ArrayBuffer or a throw completion. It performs the following steps when called:

  1. Perform ? RequireInternalSlot(arrayBuffer, [[ArrayBufferData]]).
  2. If IsSharedArrayBuffer(arrayBuffer) is true, throw a TypeError exception.
  3. If newLength is undefined, then
    1. Let newByteLength be arrayBuffer.[[ArrayBufferByteLength]].
  4. Else,
    1. Let newByteLength be ? ToIndex(newLength).
  5. If IsDetachedBuffer(arrayBuffer) is true, throw a TypeError exception.
  6. If IsImmutableBuffer(arrayBuffer) is true, throw a TypeError exception.
  7. If preserveResizability is preserve-resizability and IsFixedLengthArrayBuffer(arrayBuffer) is false, then
    1. Let newMaxByteLength be arrayBuffer.[[ArrayBufferMaxByteLength]].
  8. Else if preserveResizability is immutable, then
    1. Let newMaxByteLength be immutable.
  9. Else,
    1. Let newMaxByteLength be empty.
  10. If arrayBuffer.[[ArrayBufferDetachKey]] is not undefined, throw a TypeError exception.
  11. Let newBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, newByteLength, newMaxByteLength).
  12. Let copyLength be min(newByteLength, arrayBuffer.[[ArrayBufferByteLength]]).
  13. Let fromBlock be arrayBuffer.[[ArrayBufferData]].
  14. Let toBlock be newBuffer.[[ArrayBufferData]].
  15. NOTE: This is the only step that can write into the Data Block of an immutable ArrayBuffer.
  16. Perform CopyDataBlockBytes(toBlock, 0, fromBlock, 0, copyLength).
  17. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations may implement this method as a zero-copy move or a realloc.
  18. Perform ! DetachArrayBuffer(arrayBuffer).
  19. Return newBuffer.

25.1.3.4 IsImmutableBuffer ( arrayBuffer )

The abstract operation IsImmutableBuffer takes argument arrayBuffer (an ArrayBuffer or a SharedArrayBuffer) and returns a Boolean. It performs the following steps when called:

  1. If arrayBuffer has an [[ArrayBufferIsImmutable]] internal slot, return true.
  2. Return false.

25.1.3.18 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] )

The abstract operation SetValueInBuffer takes arguments arrayBuffer (an ArrayBuffer or SharedArrayBuffer), byteIndex (a non-negative integer), type (a TypedArray element type), value (a Number or a BigInt), isTypedArray (a Boolean), and order (seq-cst, unordered, or init) and optional argument isLittleEndian (a Boolean) and returns unused. It performs the following steps when called:

  1. Assert: IsDetachedBuffer(arrayBuffer) is false.
  2. Assert: IsImmutableBuffer(arrayBuffer) is false.
  3. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
  4. Assert: value is a BigInt if IsBigIntElementType(type) is true; otherwise, value is a Number.
  5. Let block be arrayBuffer.[[ArrayBufferData]].
  6. Let elementSize be the Element Size value specified in Table 69 for Element Type type.
  7. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
  8. Let rawBytes be NumericToRawBytes(type, value, isLittleEndian).
  9. If IsSharedArrayBuffer(arrayBuffer) is true, then
    1. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
    2. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
    3. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, let noTear be true; otherwise let noTear be false.
    4. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } to eventsRecord.[[EventList]].
  10. Else,
    1. Store the individual bytes of rawBytes into block, starting at block[byteIndex].
  11. Return unused.

25.1.3.19 GetModifySetValueInBuffer ( arrayBuffer, byteIndex, type, value, op )

The abstract operation GetModifySetValueInBuffer takes arguments arrayBuffer (an ArrayBuffer or a SharedArrayBuffer), byteIndex (a non-negative integer), type (a TypedArray element type), value (a Number or a BigInt), and op (a read-modify-write modification function) and returns a Number or a BigInt. It performs the following steps when called:

  1. Assert: IsDetachedBuffer(arrayBuffer) is false.
  2. Assert: IsImmutableBuffer(arrayBuffer) is false.
  3. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
  4. Assert: value is a BigInt if IsBigIntElementType(type) is true; otherwise, value is a Number.
  5. Let block be arrayBuffer.[[ArrayBufferData]].
  6. Let elementSize be the Element Size value specified in Table 69 for Element Type type.
  7. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
  8. Let rawBytes be NumericToRawBytes(type, value, isLittleEndian).
  9. If IsSharedArrayBuffer(arrayBuffer) is true, then
    1. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
    2. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
    3. Let rawBytesRead be a List of length elementSize whose elements are nondeterministically chosen byte values.
    4. NOTE: In implementations, rawBytesRead is the result of a load-link, of a load-exclusive, or of an operand of a read-modify-write instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
    5. Let rmwEvent be ReadModifyWriteSharedMemory { [[Order]]: seq-cst, [[NoTear]]: true, [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes, [[ModifyOp]]: op }.
    6. Append rmwEvent to eventsRecord.[[EventList]].
    7. Append Chosen Value Record { [[Event]]: rmwEvent, [[ChosenValue]]: rawBytesRead } to execution.[[ChosenValues]].
  10. Else,
    1. Let rawBytesRead be a List of length elementSize whose elements are the sequence of elementSize bytes starting with block[byteIndex].
    2. Let rawBytesModified be op(rawBytesRead, rawBytes).
    3. Store the individual bytes of rawBytesModified into block, starting at block[byteIndex].
  11. Return RawBytesToNumeric(type, rawBytesRead, isLittleEndian).

25.1.6 Properties of the ArrayBuffer Prototype Object

25.1.6.1 get ArrayBuffer.prototype.immutable

ArrayBuffer.prototype.immutable is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. Return IsImmutableBuffer(O).

25.1.6.6 ArrayBuffer.prototype.resize ( newLength )

This method performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
  3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  4. Let newByteLength be ? ToIndex(newLength).
  5. If IsDetachedBuffer(O) is true, throw a TypeError exception.
  6. If IsImmutableBuffer(O) is true, throw a TypeError exception.
  7. If newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
  8. Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).
  9. If hostHandled is handled, return undefined.
  10. Let oldBlock be O.[[ArrayBufferData]].
  11. Let newBlock be ? CreateByteDataBlock(newByteLength).
  12. Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
  13. Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).
  14. 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.
  15. Set O.[[ArrayBufferData]] to newBlock.
  16. Set O.[[ArrayBufferByteLength]] to newByteLength.
  17. Return undefined.

25.1.6.7 ArrayBuffer.prototype.slice ( start, end )

This method performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
  5. Let len be O.[[ArrayBufferByteLength]].
  6. Let relativeStart be ? ToIntegerOrInfinity(start).
  7. If relativeStart = -∞, let first be 0.
  8. Else if relativeStart < 0, let first be max(len + relativeStart, 0).
  9. Else, let first be min(relativeStart, len).
  10. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
  11. If relativeEnd = -∞, let final be 0.
  12. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).
  13. Else, let final be min(relativeEnd, len).
  14. Let newLen be max(final - first, 0).
  15. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
  16. Let new be ? Construct(ctor, « 𝔽(newLen) »).
  17. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
  18. If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
  19. If IsDetachedBuffer(new) is true, throw a TypeError exception.
  20. If IsImmutableBuffer(new) is true, throw a TypeError exception.
  21. If SameValue(new, O) is true, throw a TypeError exception.
  22. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
  23. NOTE: Side-effects of the above steps may have detached or resized O.
  24. If IsDetachedBuffer(O) is true, throw a TypeError exception.
  25. Let fromBuf be O.[[ArrayBufferData]].
  26. Let toBuf be new.[[ArrayBufferData]].
  27. Let currentLen be O.[[ArrayBufferByteLength]].
  28. If first < currentLen, then
    1. Let count be min(newLen, currentLen - first).
    2. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, count).
  29. Return new.

25.1.6.8 ArrayBuffer.prototype.transferToImmutable ( [ newLength ] )

This method performs the following steps when called:

  1. Let O be the this value.
  2. Return ? ArrayBufferCopyAndDetach(O, newLength, immutable).

25.1.7 Properties of ArrayBuffer Instances

ArrayBuffer instances inherit properties from the ArrayBuffer prototype object. ArrayBuffer instances each have an [[ArrayBufferData]] internal slot, an [[ArrayBufferByteLength]] internal slot, and an [[ArrayBufferDetachKey]] internal slot. ArrayBuffer instances which are resizable each have an [[ArrayBufferMaxByteLength]] internal slot, and ArrayBuffer instances which are immutable each have an [[ArrayBufferIsImmutable]] internal slot.

ArrayBuffer instances whose [[ArrayBufferData]] is null are considered to be detached and all operators to access or modify data contained in the ArrayBuffer instance will fail.

ArrayBuffer instances whose [[ArrayBufferDetachKey]] is set to a value other than undefined need to have all DetachArrayBuffer calls passing that same "detach key" as an argument, otherwise a TypeError will result. This internal slot is only ever set by certain embedding environments, not by algorithms in this specification.

25.3 DataView Objects

25.3.1 Abstract Operations For DataView Objects

25.3.1.6 SetViewValue ( view, requestIndex, isLittleEndian, type, value )

The abstract operation SetViewValue takes arguments view (an ECMAScript language value), requestIndex (an ECMAScript language value), isLittleEndian (an ECMAScript language value), type (a TypedArray element type), and value (an ECMAScript language value) and returns either a normal completion containing undefined or a throw completion. It is used by functions on DataView instances to store values into the view's buffer. It performs the following steps when called:

  1. Perform ? RequireInternalSlot(view, [[DataView]]).
  2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
  3. Let getIndex be ? ToIndex(requestIndex).
  4. If IsBigIntElementType(type) is true, let numberValue be ? ToBigInt(value).
  5. Otherwise, let numberValue be ? ToNumber(value).
  6. Set isLittleEndian to ToBoolean(isLittleEndian).
  7. Let viewOffset be view.[[ByteOffset]].
  8. Let viewRecord be MakeDataViewWithBufferWitnessRecord(view, unordered).
  9. NOTE: Bounds checking is not a synchronizing operation when view's backing buffer is a growable SharedArrayBuffer.
  10. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception.
  11. If IsImmutableBuffer(view.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
  12. Let viewSize be GetViewByteLength(viewRecord).
  13. Let elementSize be the Element Size value specified in Table 69 for Element Type type.
  14. If getIndex + elementSize > viewSize, throw a RangeError exception.
  15. Let bufferIndex be getIndex + viewOffset.
  16. Perform SetValueInBuffer(view.[[ViewedArrayBuffer]], bufferIndex, type, numberValue, false, unordered, isLittleEndian).
  17. Return undefined.

25.4 The Atomics Object

25.4.3 Abstract Operations for Atomics

25.4.3.1 ValidateIntegerTypedArray ( typedArray, waitable [ , use ] )

The abstract operation ValidateIntegerTypedArray takes arguments typedArray (an ECMAScript language value) and waitable (a Boolean) and optional argument use (read or write) and returns either a normal completion containing a TypedArray With Buffer Witness Record, or a throw completion. It performs the following steps when called:

  1. If use is not present, set use to read.
  2. Let taRecord be ? ValidateTypedArray(typedArray, unordered, use).
  3. NOTE: Bounds checking is not a synchronizing operation when typedArray's backing buffer is a growable SharedArrayBuffer.
  4. If waitable is true, then
    1. If typedArray.[[TypedArrayName]] is neither "Int32Array" nor "BigInt64Array", throw a TypeError exception.
  5. Else,
    1. Let type be TypedArrayElementType(typedArray).
    2. If IsUnclampedIntegerElementType(type) is false and IsBigIntElementType(type) is false, throw a TypeError exception.
  6. Return taRecord.

25.4.3.3 ValidateAtomicAccessOnIntegerTypedArray ( typedArray, requestIndex [ , waitable [ , use ] ] )

The abstract operation ValidateAtomicAccessOnIntegerTypedArray takes arguments typedArray (an ECMAScript language value) and requestIndex (an ECMAScript language value) and optional arguments waitable (a Boolean) and use (read or write) and returns either a normal completion containing an integer or a throw completion. It performs the following steps when called:

  1. If waitable is not present, set waitable to false.
  2. If use is not present, set use to read.
  3. Let taRecord be ? ValidateIntegerTypedArray(typedArray, waitable, use).
  4. Return ? ValidateAtomicAccess(taRecord, requestIndex).

25.4.3.17 AtomicReadModifyWrite ( typedArray, index, value, op )

The abstract operation AtomicReadModifyWrite takes arguments typedArray (an ECMAScript language value), index (an ECMAScript language value), value (an ECMAScript language value), and op (a read-modify-write modification function) and returns either a normal completion containing either a Number or a BigInt, or a throw completion. op takes two List of byte values arguments and returns a List of byte values. This operation atomically loads a value, combines it with another value, and stores the combination. It returns the loaded value. It performs the following steps when called:

  1. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index, false, write).
  2. If typedArray.[[ContentType]] is bigint, let v be ? ToBigInt(value).
  3. Otherwise, let v be 𝔽(? ToIntegerOrInfinity(value)).
  4. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer).
  5. Let buffer be typedArray.[[ViewedArrayBuffer]].
  6. Let elementType be TypedArrayElementType(typedArray).
  7. Return GetModifySetValueInBuffer(buffer, byteIndexInBuffer, elementType, v, op).

25.4.6 Atomics.compareExchange ( typedArray, index, expectedValue, replacementValue )

This function performs the following steps when called:

  1. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index, false, write).
  2. Let buffer be typedArray.[[ViewedArrayBuffer]].
  3. Let block be buffer.[[ArrayBufferData]].
  4. If typedArray.[[ContentType]] is bigint, then
    1. Let expected be ? ToBigInt(expectedValue).
    2. Let replacement be ? ToBigInt(replacementValue).
  5. Else,
    1. Let expected be 𝔽(? ToIntegerOrInfinity(expectedValue)).
    2. Let replacement be 𝔽(? ToIntegerOrInfinity(replacementValue)).
  6. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer).
  7. Let elementType be TypedArrayElementType(typedArray).
  8. Let elementSize be TypedArrayElementSize(typedArray).
  9. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
  10. Let expectedBytes be NumericToRawBytes(elementType, expected, isLittleEndian).
  11. Let replacementBytes be NumericToRawBytes(elementType, replacement, isLittleEndian).
  12. If IsSharedArrayBuffer(buffer) is true, then
    1. Let rawBytesRead be AtomicCompareExchangeInSharedBlock(block, byteIndexInBuffer, elementSize, expectedBytes, replacementBytes).
  13. Else,
    1. Let rawBytesRead be a List of length elementSize whose elements are the sequence of elementSize bytes starting with block[byteIndexInBuffer].
    2. If ByteListEqual(rawBytesRead, expectedBytes) is true, then
      1. Store the individual bytes of replacementBytes into block, starting at block[byteIndexInBuffer].
  14. Return RawBytesToNumeric(elementType, rawBytesRead, isLittleEndian).

25.4.11 Atomics.store ( typedArray, index, value )

This function performs the following steps when called:

  1. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index, false, write).
  2. If typedArray.[[ContentType]] is bigint, let v be ? ToBigInt(value).
  3. Otherwise, let v be 𝔽(? ToIntegerOrInfinity(value)).
  4. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer).
  5. Let buffer be typedArray.[[ViewedArrayBuffer]].
  6. Let elementType be TypedArrayElementType(typedArray).
  7. Perform SetValueInBuffer(buffer, byteIndexInBuffer, elementType, v, true, seq-cst).
  8. Return v.

A Copyright & Software License

Copyright Notice

© 2024 Mark S. Miller, Richard Gibson

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.