Stage 3 Draft / August 26, 2021

Resizable ArrayBuffer and growable SharedArrayBuffer

We extend the ArrayBuffer and SharedArrayBuffer constructors to take an additional maximum byte length, which would construct dynamically resizable and growable array buffers, respectively. In addition, a transfer method is added to the ArrayBuffer prototype.

1 Modifications to ArrayBuffer Objects

1.1 Modifications to Abstract Operations for ArrayBuffer Objects

1.1.1 DetachArrayBuffer ( arrayBuffer [ , key ] )

The abstract operation DetachArrayBuffer takes argument arrayBuffer and optional argument key. It performs the following steps when called:

  1. Assert: Type(arrayBuffer) is Object and it has [[ArrayBufferData]], [[ArrayBufferByteLength]], and [[ArrayBufferDetachKey]] internal slots.
  2. Assert: IsSharedArrayBuffer(arrayBuffer) is false.
  3. If key is not present, set key to undefined.
  4. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception.
  5. Set arrayBuffer.[[ArrayBufferData]] to null.
  6. Set arrayBuffer.[[ArrayBufferByteLength]] to 0.
  7. Return NormalCompletion(null).
Note

Detaching an ArrayBuffer instance disassociates the Data Block used as its backing store from the instance and sets the byte length of the buffer to 0. No operations defined by this specification use the DetachArrayBuffer abstract operation. However, an ECMAScript host or implementation may define such operations.

1.1.2 AllocateArrayBuffer ( constructor, byteLength[ , maxByteLength ] )

The abstract operation AllocateArrayBuffer takes arguments constructor and, byteLength, and maxByteLength. It is used to create an ArrayBuffer object. It performs the following steps when called:

  1. Let slots be « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] ».
  2. If maxByteLength is present, append [[ArrayBufferMaxByteLength]] to slots.
  3. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »slots).
  4. Let block be ? CreateByteDataBlock(byteLength).
  5. Set obj.[[ArrayBufferData]] to block.
  6. Set obj.[[ArrayBufferByteLength]] to byteLength.
  7. If maxByteLength is present, then
    1. Assert: byteLengthmaxByteLength.
    2. If it is not possible to create a Data Block block consisting of maxByteLength bytes, throw a RangeError exception.
    3. NOTE: Resizable ArrayBuffers are designed to be implementable with in-place growth. Implementations reserve the right to throw if, for example, virtual memory cannot be reserved up front.
    4. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.
  8. Return obj.

1.1.3 ArrayBufferByteLength ( arrayBuffer, order )

The abstract operation ArrayBufferByteLength takes arguments arrayBuffer and order (either SeqCst or Unordered). It performs the following steps when called:

  1. Assert: Type(arrayBuffer) is Object and arrayBuffer has an [[ArrayBufferData]] internal slot.
  2. If IsSharedArrayBuffer(arrayBuffer) is true and arrayBuffer has an [[ArrayBufferByteLengthData]] internal slot, then
    1. Let bufferByteLengthBlock be O.[[ArrayBufferByteLengthData]].
    2. Return (GetValueFromBuffer(bufferByteLengthBlock, 0, BigUint64, true, order)).
  3. Assert: IsDetachedBuffer(arrayBuffer) is false.
  4. Return arrayBuffer.[[ArrayBufferByteLength]].

1.1.4 MakeIdempotentArrayBufferByteLengthGetter ( order )

The abstract operation MakeIdempotentArrayBufferByteLengthGetter takes argument order (either SeqCst or Unordered). The returned Abstract Closure helps ensure that there there is a single shared memory read event of the byte length data block in the calling operation. It performs the following steps when called:

  1. NOTE: The [[ArrayBuffer]] slot is used for editorial clarity only, that a getter should only be used with a single ArrayBuffer.
  2. Let lengthStorage be { [[ArrayBuffer]]: empty, [[ByteLength]]: empty }.
  3. Let getter be a new Abstract Closure with parameters (buffer) that captures lengthStorage and order and performs the following steps when called:
    1. If lengthStorage.[[ByteLength]] is empty, then
      1. Assert: lengthStorage.[[ArrayBuffer]] is empty.
      2. Set lengthStorage.[[ArrayBuffer]] to buffer.
      3. Set lengthStorage.[[ByteLength]] to ArrayBufferByteLength(buffer, order).
    2. Assert: SameValue(lengthStorage.[[ArrayBuffer]], buffer) is true.
    3. Return lengthStorage.[[ByteLength]].
  4. Return getter.

1.1.5 IsResizableArrayBuffer ( arrayBuffer )

The abstract operation IsResizableArrayBuffer takes argument arrayBuffer. It performs the following steps when called:

  1. Assert: Type(arrayBuffer) is Object and arrayBuffer has an [[ArrayBufferData]] internal slot.
  2. If buffer has an [[ArrayBufferMaxByteLength]] internal slot, return true.
  3. Return false.

1.1.6 GetArrayBufferMaxByteLengthOption ( options )

The abstract operation GetArrayBufferMaxByteLengthOption takes argument options. It performs the following steps when called:

  1. If Type(options) is not Object, return empty.
  2. Let maxByteLength be ? Get(options, "maxByteLength").
  3. If maxByteLength is undefined, return empty.
  4. Return ? ToIndex(maxByteLength).

1.1.7 HostResizeArrayBuffer ( buffer, newByteLength )

The host-defined abstract operation HostResizeArrayBuffer takes arguments buffer (an ArrayBuffer object) and newByteLength. It gives the host an opportunity to perform implementation-defined resizing of buffer. If the host chooses not to handle resizing of buffer, it may return unhandled for the default behavior.

The implementation of HostResizeArrayBuffer must conform to the following requirements:

  • The abstract operation must return either NormalCompletion(handled), NormalCompletion(unhandled), or an abrupt throw completion.
  • The abstract operation does not detach buffer.
  • If the abstract operation completes normally with handled, buffer.[[ArrayBufferByteLength]] is newByteLength.

The default implementation of HostResizeArrayBuffer is to return unhandled.

1.2 The ArrayBuffer Constructor

1.2.1 ArrayBuffer ( length[ , options ] )

When the ArrayBuffer function is called with argument length and optional argument options, the following steps are taken:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let byteLength be ? ToIndex(length).
  3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options).
  4. If requestedMaxByteLength is empty, then
    1. Return ? AllocateArrayBuffer(NewTarget, byteLength).
  5. If byteLength > requestedMaxByteLength, throw a RangeError exception.
  6. Return ? AllocateArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).

1.3 Modifications to the Properties of the ArrayBuffer Prototype Object

1.3.1 get ArrayBuffer [ @@species ]

ArrayBuffer[@@species] is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Return the this value.

The value of the "name" property of this function is "get [Symbol.species]".

Note

ArrayBuffer prototype methods normally use their this value's constructor to create a derived object. However, a subclass constructor may over-ride that default behaviour for the ArrayBuffer.prototype.slice ( start, end ) method by redefining its @@species property.

1.3.2 get ArrayBuffer.prototype.maxByteLength

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

  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, return +0𝔽.
  5. If IsResizableArrayBuffer(O) is true, then
    1. Let length be O.[[ArrayBufferMaxByteLength]].
  6. Else,
    1. Let length be O.[[ArrayBufferByteLength]].
  7. Return 𝔽(length).

1.3.3 get ArrayBuffer.prototype.resizable

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

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  4. Return IsResizableArrayBuffer(O).

1.3.4 ArrayBuffer.prototype.resize ( newLength )

The following steps are taken:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
  3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
  5. Let newByteLength be ? ToIntegerOrInfinity(newLength).
  6. If newByteLength < 0 or newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
  7. Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).
  8. If hostHandled is handled, return undefined.
  9. Let oldBlock be O.[[ArrayBufferData]].
  10. Let newBlock be ? CreateByteDataBlock(newByteLength).
  11. Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
  12. Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).
  13. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations reserve the right to implement this method as in-place growth or shrinkage.
  14. Set O.[[ArrayBufferData]] to newBlock.
  15. Set O.[[ArrayBufferByteLength]] to newLength.
  16. Return undefined.

1.3.5 ArrayBuffer.prototype.transfer ( [ newLength ] )

The following steps are taken:

  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. If newLength is undefined, let newByteLength be O.[[ArrayBufferByteLength]].
  6. Else, let newByteLength be ? ToIntegerOrInfinity(newLength).
  7. Let new be ? Construct(%ArrayBuffer%, « 𝔽(newByteLength) »).
  8. NOTE: This method returns a fixed-length ArrayBuffer.
  9. Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
  10. Let fromBlock be O.[[ArrayBufferData]].
  11. Let toBlock be new.[[ArrayBufferData]].
  12. Perform CopyDataBlockBytes(toBlock, 0, fromBlock, 0, copyLength).
  13. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations reserve the right to implement this method as a zero-copy move or a realloc.
  14. Perform ! DetachArrayBuffer(O).
  15. Return new.
Note

The transfer method intentionally does not honor the @@species property when creating derived objects.

2 Modifications to SharedArrayBuffer Objects

2.1 Modifications to Abstract Operations for SharedArrayBuffer Objects

2.1.1 AllocateSharedArrayBuffer ( constructor, byteLength[ , maxByteLength ] )

The abstract operation AllocateSharedArrayBuffer takes arguments constructor and, byteLength, and optional argument maxByteLength. It is used to create a SharedArrayBuffer object. It performs the following steps when called:

  1. Let slots be « [[ArrayBufferData]] ».
  2. If maxByteLength is present, append [[ArrayBufferByteLengthData]] and [[ArrayBufferMaxByteLength]] to slots.
  3. Else, append [[ArrayBufferByteLength]] to slots.
  4. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%SharedArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]] »slots).
  5. If maxByteLength is present, then let allocLength be maxByteLength.
  6. Else, let allocLength be byteLength
  7. Let block be ? CreateSharedByteDataBlock(byteLengthallocLength).
  8. NOTE: Growable SharedArrayBuffers must be implemented as in-place growable. Creation of a maxByteLength sized Data Block is a specification mechanism. It may be implemented as committing a byteLength sized buffer while reserving maxByteLength in virtual memory.
  9. Set obj.[[ArrayBufferData]] to block.
  10. If maxByteLength is present, then
    1. Assert: byteLengthmaxByteLength.
    2. Let byteLengthBlock be ? CreateSharedByteDataBlock(8).
    3. Perform SetValueInBuffer(byteLengthBlock, 0, BigUint64, ℤ(byteLength), true, SeqCst).
    4. Set obj.[[ArrayBufferByteLengthData]] to byteLengthBlock.
    5. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.
  11. Else,
    1. Set obj.[[ArrayBufferByteLength]] to byteLength.
  12. Return obj.

2.1.2 HostGrowSharedArrayBuffer ( buffer, newByteLength )

The host-defined abstract operation HostGrowSharedArrayBuffer takes arguments buffer (a growable SharedArrayBuffer object) and newByteLength. It gives the host an opportunity to perform implementation-defined growing of buffer. If the host chooses not to handle resizing of buffer, it may return unhandled for the default behavior.

The implementation of HostGrowSharedArrayBuffer must conform to the following requirements:

  • The abstract operation must return either NormalCompletion(handled), NormalCompletion(unhandled), or an abrupt throw completion.
  • If the abstract operation does not complete normally with unhandled, and newByteLength < the current byte length of the buffer or newByteLength > buffer.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
  • Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. If the abstract operation completes normally with handled, a WriteSharedMemory or ReadModifyWriteSharedMemory event whose [[Order]] is SeqCst, [[Payload]] is NumericToRawBytes(BigUint64, newByteLength, isLittleEndian), [[Block]] is buffer.[[ArrayBufferByteLengthData]], [[ByteIndex]] is 0, and [[ElementSize]] is 8 is added to the surrounding agent's candidate execution such that racing calls to SharedArrayBuffer.prototype.grow are not "lost", i.e. silently do nothing.

The default implementation of HostGrowSharedArrayBuffer is to return unhandled.

Note

The second requirement above is intentionally vague about how or when the current byte length of buffer is read. Because the byte length must be updated via an atomic read-modify-write operation on the underlying hardware, architectures that use load-link/store-conditional or load-exclusive/store-exclusive instruction pairs may wish to keep the paired instructions close in the instruction stream. As such, SharedArrayBuffer.prototype.grow itself does not perform bounds checking on newByteLength before calling HostGrowSharedArrayBuffer, nor is there a requirement on when the current byte length is read.

This is in contrast with HostResizeArrayBuffer, which is guaranteed that the value of newByteLength is ≥ 0 and ≤ buffer.[[ArrayBufferMaxByteLength]].

2.2 The SharedArrayBuffer Constructor

2.2.1 SharedArrayBuffer ( length[ , options ] )

When the SharedArrayBuffer function is called with argument length and optional argument options, the following steps are taken:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Let byteLength be ? ToIndex(length).
  3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options).
  4. If requestedMaxByteLength is empty, then
    1. Return ? AllocateSharedArrayBuffer(NewTarget, byteLength).
  5. If byteLength > requestedMaxByteLength, throw a RangeError exception.
  6. Return ? AllocateSharedArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).

2.3 Modifications to the Properties of the SharedArrayBuffer Prototype Object

2.3.1 get SharedArrayBuffer.prototype.byteLength

SharedArrayBuffer.prototype.byteLength is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. Let length be O.[[ArrayBufferByteLength]].
  5. Let length be ArrayBufferByteLength(O, SeqCst).
  6. Return 𝔽(length).

2.3.2 get SharedArrayBuffer.prototype.growable

SharedArrayBuffer.prototype.growable is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. Return IsResizableArrayBuffer(O).

2.3.3 get SharedArrayBuffer.prototype.maxByteLength

SharedArrayBuffer.prototype.maxByteLength is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. If IsResizableArrayBuffer(O) is true, then
    1. Let length be O.[[ArrayBufferMaxByteLength]].
  5. Else,
    1. Let length be O.[[ArrayBufferByteLength]].
  6. Return 𝔽(length).

2.3.4 SharedArrayBuffer.prototype.grow ( newLength )

The following steps are taken:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
  3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
  4. Let newByteLength to ? ToIntegerOrInfinity(newLength).
  5. Let hostHandled be ? HostGrowSharedArrayBuffer(O, newByteLength).
  6. If hostHandled is handled, return undefined.
  7. Let rawCurrentByteLengthBytesRead be a List of length 8 whose elements are nondeterministically chosen byte values.
  8. NOTE: In implementations, rawCurrentByteLengthBytesRead 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.
  9. Let byteLengthBlock be O.[[ArrayBufferByteLengthData]].
  10. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
  11. Let currentByteLength be RawBytesToNumeric(BigUint64, rawCurrentByteLengthBytesRead, isLittleEndian).
  12. Let growFailed be false.
  13. If newByteLength < currentByteLength or newByteLength > O.[[ArrayBufferMaxByteLength]], set growFailed to true.
  14. Let byteLengthDelta be newByteLength - currentByteLength.
  15. If it is impossible to create a new Shared Data Block value consisting of byteLengthDelta bytes, set growFailed to true.
  16. NOTE: No new Shared Data Block is constructed and used here. The observable behaviour of growable SharedArrayBuffers is specified by allocating a max-sized Shared Data Block at construction time, and this step is intended to capture the requirement that implementations that run out of memory must throw a RangeError.
  17. NOTE: The above checks help ensure that concurrent calls to SharedArrayBuffer.prototype.grow are totally ordered. For example, consider two racing calls: sab.grow(10) and sab.grow(20). One of the two calls is guaranteed to win the race. The call to sab.grow(10) will never shrink sab even if sab.grow(20) happened first.
  18. If growFailed is false and newByteLengthcurrentByteLength, then
    1. NOTE: Resizes to the same length explicitly do nothing to avoid gratuitous synchronization.
    2. Let second be a new read-modify-write modification function with parameters (oldBytes, newBytes) that captures nothing and performs the following steps atomically when called:
      1. Return newBytes.
    3. Let newByteLengthBytes be NumericToRawBytes(BigUint64, ℤ(newByteLength), isLittleEndian).
    4. Let event be ReadModifyWriteSharedMemory { [[Order]]: SeqCst, [[NoTear]]: true, [[Block]]: byteLengthBlock, [[ByteIndex]]: 0, [[ElementSize]]: 8, [[Payload]]: newByteLengthBytes, [[ModifyOp]]: second }.
    5. NOTE: The new memory is already zeroed, as a O.[[ArrayBufferMaxByteLength]] sized Data Block is already allocated. This is a specification mechanism, an implementation is not required to reserve O.[[ArrayBufferMaxByteLength]] bytes of physical memory.
  19. Else,
    1. Let event be ReadSharedMemory { [[Order]]: SeqCst, [[NoTear]]: true, [[Block]]: byteLengthBlock, [[ByteIndex]]: 0, [[ElementSize]]: 8 }.
  20. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
  21. Let eventList be the [[EventList]] field of the element in execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
  22. Append event to eventList.
  23. Append Chosen Value Record { [[Event]]: event, [[ChosenValue]]: rawCurrentBytesLengthBytesRead } to execution.[[ChosenValues]].
  24. If growFailed is true, throw a RangeError exception.
  25. Return undefined.
Note

Spurious failures of the compare-exchange to update the length are prohibited. If the bounds checking for the new length passes and the implementation is not out of memory, a ReadModifyWriteSharedMemory event (i.e. a successful compare-exchange) is always added into the candidate execution.

Many of the above steps are shared with the algorithm steps of Atomics.compareExchange and should be refactored when merged into the full specification.

3 Modifications to Integer-Indexed Exotic Objects

3.1 [[OwnPropertyKeys]] ( )

The [[OwnPropertyKeys]] internal method of an Integer-Indexed exotic object O takes no arguments. It performs the following steps when called:

  1. Let keys be a new empty List.
  2. Assert: O is an Integer-Indexed exotic object.
  3. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is false, then
    1. For each integer i starting with 0 such that i < O.[[ArrayLength]], in ascending order, do
      1. Add ! ToString(𝔽(i)) as the last element of keys.
  4. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  5. Let len be IntegerIndexedObjectLength(O, getBufferByteLength).
  6. If len is not out-of-bounds, then
    1. For each integer i starting with 0 such that i < len, in ascending order, do
      1. Add ! ToString(𝔽(i)) as the last element of keys.
  7. For each own property key P of O such that Type(P) is String and P is not an integer index, in ascending chronological order of property creation, do
    1. Add P as the last element of keys.
  8. For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, do
    1. Add P as the last element of keys.
  9. Return keys.

3.2 IsValidIntegerIndex ( O, index )

The abstract operation IsValidIntegerIndex takes arguments O, and index. It performs the following steps when called:

  1. Assert: O is an Integer-Indexed exotic object.
  2. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
  3. If ! IsIntegralNumber(index) is false, return false.
  4. If index is -0𝔽, return false.
  5. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(Unordered).
  6. NOTE: Bounds checking is not a synchronizing operation when O's backing buffer is a growable SharedArrayBuffer.
  7. Let length be IntegerIndexedObjectLength(O, getBufferByteLength).
  8. If length is out-of-bounds or (index) < 0 or (index) ≥ O.[[ArrayLength]]length, return false.
  9. Return true.

3.3 IntegerIndexedObjectByteLength ( O, getBufferByteLength )

The abstract operation IntegerIndexedObjectByteLength takes arguments O and getBufferByteLength. It performs the following steps when called:

  1. Assert: O is an Integer-Indexed exotic object.
  2. Let length be IntegerIndexedObjectLength(O, getBufferByteLength).
  3. If length is out-of-bounds or length = 0, return 0.
  4. If O.[[ByteLength]] is not auto, return O.[[ByteLength]].
  5. Let elementSize be the Element Size value specified in Table 63 for O.[[TypedArrayName]].
  6. Return length × elementSize.

3.4 IntegerIndexedObjectLength ( O, getBufferByteLength )

The abstract operation IntegerIndexedObjectLength takes arguments O and getBufferByteLength. It performs the following steps when called:

  1. Assert: O is an Integer-Indexed exotic object.
  2. If IsIntegerIndexedObjectOutOfBounds(O, getBufferByteLength) is true, return out-of-bounds.
  3. If O.[[ArrayLength]] is not auto, return O.[[ArrayLength]].
  4. Let buffer be O.[[ViewedArrayBuffer]].
  5. Let bufferByteLength be getBufferByteLength(buffer).
  6. Assert: IsResizableArrayBuffer(buffer) is true.
  7. Let byteOffset be O.[[ByteOffset]].
  8. Let elementSize be the Element Size value specified in Table 63 for O.[[TypedArrayName]].
  9. Let length be floor((bufferByteLength - byteOffset) / elementSize).
  10. Return length.

3.5 IsIntegerIndexedObjectOutOfBounds ( O, getBufferByteLength )

The abstract operation IsIntegerIndexedObjectOutOfBounds takes arguments O and getBufferByteLength. It checks if any part of the underlying viewed buffer is out of bounds. It performs the following steps when called:

  1. Assert: O is an Integer-Indexed exotic object.
  2. Let buffer be O.[[ViewedArrayBuffer]].
  3. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return true.
  4. Let bufferByteLength be getBufferByteLength(buffer).
  5. Let byteOffsetStart be O.[[ByteOffset]].
  6. If O.[[ArrayLength]] is auto, then
    1. Let byteOffsetEnd be bufferByteLength.
  7. Else,
    1. Let elementSize be the Element Size value specified in Table 63 for O.[[TypedArrayName]].
    2. Let byteOffsetEnd be byteOffsetStart + O.[[ArrayLength]] × elementSize.
  8. If byteOffsetStart > bufferByteLength or byteOffsetEnd > bufferByteLength, then return true.
  9. NOTE: 0-length TypedArrays are not considered out-of-bounds.
  10. Return false.

4 Modifications to TypedArray Objects

4.1 Modifications to Properties of the %TypedArray.prototype% Object

4.1.1 Runtime Semantics: ValidateTypedArray ( O )

The abstract operation ValidateTypedArray takes argument O. It performs the following steps when called:

  1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
  2. Assert: O has a [[ViewedArrayBuffer]] internal slot.
  3. Let buffer be O.[[ViewedArrayBuffer]].
  4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  5. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  6. If IsIntegerIndexedObjectOutOfBounds(O, getBufferByteLength) is true, throw a TypeError exception.
  7. Return buffer.

4.1.2 get %TypedArray%.prototype.byteLength

%TypedArray%.prototype.byteLength is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
  3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
  4. Let buffer be O.[[ViewedArrayBuffer]].
  5. If IsDetachedBuffer(buffer) is true, return +0𝔽.
  6. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  7. Let size be O.[[ByteLength]]IntegerIndexedObjectByteLength(O, getBufferByteLength).
  8. Return 𝔽(size).

4.1.3 get %TypedArray%.prototype.byteOffset

%TypedArray%.prototype.byteOffset is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
  3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
  4. Let buffer be O.[[ViewedArrayBuffer]].
  5. If IsDetachedBuffer(buffer) is true, return +0𝔽.
  6. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  7. If IsIntegerIndexedObjectOutOfBounds(O, getBufferByteLength) is true, return +0𝔽.
  8. Let offset be O.[[ByteOffset]].
  9. Return 𝔽(offset).

4.1.4 get %TypedArray%.prototype.length

%TypedArray%.prototype.length is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
  3. Assert: O has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots.
  4. Let buffer be O.[[ViewedArrayBuffer]].
  5. If IsDetachedBuffer(buffer) is true, return +0𝔽.
  6. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  7. Let length be O.[[ArrayLength]]IntegerIndexedObjectLength(O, getBufferByteLength).
  8. If length is out-of-bounds, set length to 0.
  9. Return 𝔽(length).

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

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

The interpretation and use of the arguments of %TypedArray%.prototype.fill are the same as for Array.prototype.fill as defined in 22.1.3.6.

When the fill method is called, the following steps are taken:

  1. Let O be the this value.
  2. Perform ? ValidateTypedArray(O).
  3. Let len be O.[[ArrayLength]].
  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 is -∞, let k be 0.
  8. Else if relativeStart < 0, let k be max(len + relativeStart, 0).
  9. Else, let k be min(relativeStart, len).
  10. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
  11. If relativeEnd is -∞, 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. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
  15. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  16. If IsIntegerIndexedObjectOutOfBounds(O, getBufferByteLength) is true, throw a TypeError exception.
  17. Repeat, while k < final,
    1. Let Pk be ! ToString(𝔽(k)).
    2. Perform ! Set(O, Pk, value, true).
    3. Set k to k + 1.
  18. Return O.

4.1.6 %TypedArray%.prototype.slice ( start, end )

The interpretation and use of the arguments of %TypedArray%.prototype.slice are the same as for Array.prototype.slice as defined in 22.1.3.25. The following steps are taken:

When the slice method is called, the following steps are taken:

  1. Let O be the this value.
  2. Perform ? ValidateTypedArray(O).
  3. Let len be O.[[ArrayLength]].
  4. Let relativeStart be ? ToIntegerOrInfinity(start).
  5. If relativeStart is -∞, let k be 0.
  6. Else if relativeStart < 0, let k be max(len + relativeStart, 0).
  7. Else, let k be min(relativeStart, len).
  8. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
  9. If relativeEnd is -∞, let final be 0.
  10. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).
  11. Else, let final be min(relativeEnd, len).
  12. Let count be max(final - k, 0).
  13. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(count) »).
  14. If count > 0, then
    1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
    2. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
    3. If IsIntegerIndexedObjectOutOfBounds(O, getBufferByteLength) is true, throw a TypeError exception.
    4. Let srcName be the String value of O.[[TypedArrayName]].
    5. Let srcType be the Element Type value in Table 63 for srcName.
    6. Let targetName be the String value of A.[[TypedArrayName]].
    7. Let targetType be the Element Type value in Table 63 for targetName.
    8. If srcType is different from targetType, then
      1. Let n be 0.
      2. Repeat, while k < final,
        1. Let Pk be ! ToString(𝔽(k)).
        2. Let kValue be ! Get(O, Pk).
        3. Perform ! Set(A, ! ToString(𝔽(n)), kValue, true).
        4. Set k to k + 1.
        5. Set n to n + 1.
    9. Else,
      1. Let srcBuffer be O.[[ViewedArrayBuffer]].
      2. Let targetBuffer be A.[[ViewedArrayBuffer]].
      3. Let elementSize be the Element Size value specified in Table 63 for Element Type srcType.
      4. NOTE: If srcType and targetType are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data.
      5. Let srcByteOffset be O.[[ByteOffset]].
      6. Let targetByteIndex be A.[[ByteOffset]].
      7. Let srcByteIndex be (k × elementSize) + srcByteOffset.
      8. Let limit be targetByteIndex + count × elementSize.
      9. 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.
  15. Return A.

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

4.1.7 %TypedArray%.prototype.sort ( comparefn )

%TypedArray%.prototype.sort is a distinct function that, except as described below, implements the same requirements as those of Array.prototype.sort as defined in 22.1.3.27. The implementation of the %TypedArray%.prototype.sort specification 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 function is not generic. The this value must be an object with a [[TypedArrayName]] internal slot.

Upon entry, the following steps are performed to initialize evaluation of the sort function. These steps are used instead of steps in 22.1.3.27:

  1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
  2. Let obj be the this value.
  3. Perform ? ValidateTypedArray(obj).
  4. Let buffer be obj.[[ViewedArrayBuffer]].
  5. Let len be obj.[[ArrayLength]].

The following version of SortCompare is used by %TypedArray%.prototype.sort. It performs a numeric comparison rather than the string comparison used in 22.1.3.27.

The abstract operation TypedArraySortCompare takes arguments x and y. It also has access to the obj, comparefn, and buffer values of the current invocation of the sort method. It performs the following steps when called:

  1. Assert: Both Type(x) and Type(y) are Number or both are BigInt.
  2. If comparefn is not undefined, then
    1. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)).
    2. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
    3. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
    4. If IsIntegerIndexedObjectOutOfBounds(obj, getBufferByteLength) is true, throw a TypeError exception.
    5. If v is NaN, return +0𝔽.
    6. Return v.
  3. If x and y are both NaN, return +0𝔽.
  4. If x is NaN, return 1𝔽.
  5. If y is NaN, return -1𝔽.
  6. If x < y, return -1𝔽.
  7. If x > y, return 1𝔽.
  8. If x is -0𝔽 and y is +0𝔽, return -1𝔽.
  9. If x is +0𝔽 and y is -0𝔽, return 1𝔽.
  10. Return +0𝔽.
Note

Because NaN always compares greater than any other value, NaN property values always sort to the end of the result when comparefn is not provided.

4.1.8 SetTypedArrayFromTypedArray ( target, targetOffset, source )

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

  1. Assert: source is an Object that has a [[TypedArrayName]] internal slot.
  2. Let targetBuffer be target.[[ViewedArrayBuffer]].
  3. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
  4. Let getSrcBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  5. Let targetLength be target.[[ArrayLength]]IntegerIndexedObjectLength(target, getBufferByteLength).
  6. If targetLength is out-of-bounds, set targetLength to 0.
  7. Let srcBuffer be source.[[ViewedArrayBuffer]].
  8. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
  9. If IsIntegerIndexedObjectOutOfBounds(target, getSrcBufferByteLength) is true, throw a TypeError exception.
  10. Let targetName be the String value of target.[[TypedArrayName]].
  11. Let targetType be the Element Type value in Table 63 for targetName.
  12. Let targetElementSize be the Element Size value specified in Table 63 for targetName.
  13. Let targetByteOffset be target.[[ByteOffset]].
  14. Let srcName be the String value of source.[[TypedArrayName]].
  15. Let srcType be the Element Type value in Table 63 for srcName.
  16. Let srcElementSize be the Element Size value specified in Table 63 for srcName.
  17. Let srcLength be source.[[ArrayLength]].
  18. Let srcByteOffset be source.[[ByteOffset]].
  19. If targetOffset is +∞, throw a RangeError exception.
  20. If srcLength + targetOffset > targetLength, throw a RangeError exception.
  21. If target.[[ContentType]] ≠ source.[[ContentType]], throw a TypeError exception.
  22. If both IsSharedArrayBuffer(srcBuffer) and IsSharedArrayBuffer(targetBuffer) are true, then
    1. If srcBuffer.[[ArrayBufferData]] and targetBuffer.[[ArrayBufferData]] are the same Shared Data Block values, let same be true; else let same be false.
  23. Else, let same be SameValue(srcBuffer, targetBuffer).
  24. If same is true, then
    1. Let srcByteLength be source.[[ByteLength]]IntegerIndexedObjectByteLength(source, getSrcBufferByteLength).
    2. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%).
    3. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects.
    4. Let srcByteIndex be 0.
  25. Else, let srcByteIndex be srcByteOffset.
  26. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
  27. Let limit be targetByteIndex + targetElementSize × srcLength.
  28. If srcType is the same as targetType, then
    1. NOTE: If srcType and targetType are the same, 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.
  29. 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.

4.2 Modifications to the TypedArray Constructors

4.2.1 InitializeTypedArrayFromTypedArray ( O, srcArray )

The abstract operation InitializeTypedArrayFromTypedArray takes arguments O (a TypedArray object) and srcArray (a TypedArray object). It performs the following steps when called:

  1. Assert: O is an Object that has a [[TypedArrayName]] internal slot.
  2. Assert: srcArray is an Object that has a [[TypedArrayName]] internal slot.
  3. Let srcData be srcArray.[[ViewedArrayBuffer]].
  4. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
  5. Let constructorName be the String value of O.[[TypedArrayName]].
  6. Let elementType be the Element Type value in Table 63 for constructorName.
  7. Let getSrcBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  8. Let elementLength be srcArray.[[ArrayLength]]IntegerIndexedObjectLength(srcArray, getSrcBufferByteLength).
  9. If elementLength is out-of-bounds, set elementLength to 0.
  10. Let srcName be the String value of srcArray.[[TypedArrayName]].
  11. Let srcType be the Element Type value in Table 63 for srcName.
  12. Let srcElementSize be the Element Size value specified in Table 63 for srcName.
  13. Let srcByteOffset be srcArray.[[ByteOffset]].
  14. Let elementSize be the Element Size value specified in Table 63 for constructorName.
  15. Let byteLength be elementSize × elementLength.
  16. If IsSharedArrayBuffer(srcData) is false, then
    1. Let bufferConstructor be ? SpeciesConstructor(srcData, %ArrayBuffer%).
  17. Else,
    1. Let bufferConstructor be %ArrayBuffer%.
  18. If elementType is the same as srcType, then
    1. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor).
  19. Else,
    1. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength).
    2. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
    3. If IsIntegerIndexedObjectOutOfBounds(srcArray, getSrcBufferByteLength) is true, throw a TypeError exception.
    4. If srcArray.[[ContentType]] ≠ O.[[ContentType]], throw a TypeError exception.
    5. Let srcByteIndex be srcByteOffset.
    6. Let targetByteIndex be 0.
    7. Let count be elementLength.
    8. Repeat, while count > 0,
      1. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, Unordered).
      2. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, Unordered).
      3. Set srcByteIndex to srcByteIndex + srcElementSize.
      4. Set targetByteIndex to targetByteIndex + elementSize.
      5. Set count to count - 1.
  20. Set O.[[ViewedArrayBuffer]] to data.
  21. Set O.[[ByteLength]] to byteLength.
  22. Set O.[[ByteOffset]] to 0.
  23. Set O.[[ArrayLength]] to elementLength.

4.2.2 InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset, length )

The abstract operation InitializeTypedArrayFromArrayBuffer takes arguments O (a TypedArray object), buffer (an ArrayBuffer object), byteOffset (an ECMAScript language value), and length (an ECMAScript language value). It performs the following steps when called:

  1. Assert: O is an Object that has a [[TypedArrayName]] internal slot.
  2. Assert: buffer is an Object that has an [[ArrayBufferData]] internal slot.
  3. Let constructorName be the String value of O.[[TypedArrayName]].
  4. Let elementSize be the Element Size value specified in Table 63 for constructorName.
  5. Let offset be ? ToIndex(byteOffset).
  6. If offset modulo elementSize ≠ 0, throw a RangeError exception
  7. Let bufferIsResizable be IsResizableArrayBuffer(buffer)..
  8. If length is not undefined, then
    1. Let newLength be ? ToIndex(length).
  9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]ArrayBufferByteLength(buffer, SeqCst)..
  11. If length is undefined and bufferIsResizable is true, then
    1. If offset > bufferByteLength, throw a RangeError exception.
    2. Set O.[[ByteLength]] to auto.
    3. Set O.[[ArrayLength]] to auto.
  12. Else,
    1. If length is undefined, then
      1. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception.
      2. Let newByteLength be bufferByteLength - offset.
      3. If newByteLength < 0, throw a RangeError exception.
    2. Else,
      1. Let newByteLength be newLength × elementSize.
      2. If offset + newByteLength > bufferByteLength, throw a RangeError exception.
    3. Set O.[[ViewedArrayBuffer]] to buffer.
    4. Set O.[[ByteLength]] to newByteLength.
    5. Set O.[[ByteOffset]] to offset.
    6. Set O.[[ArrayLength]] to newByteLength / elementSize.
  13. Set O.[[ViewedArrayBuffer]] to buffer.
  14. Set O.[[ByteOffset]] to offset.

5 Modifications to DataView Objects

5.1 Modifications to Abstract Operations For DataView Objects

5.1.1 GetViewByteLength ( view, getBufferByteLength )

The abstract operation GetViewByteLength takes arguments view and getBufferByteLength. It performs the following steps when called:

  1. Assert: Type(view) is Object and view has a [[DataView]] internal slot.
  2. If view.[[ByteLength]] is not auto, then return view.[[ByteLength]].
  3. Let buffer be view.[[ViewedArrayBuffer]].
  4. Return getBufferByteLength(buffer).

5.1.2 IsViewOutOfBounds ( view, getBufferByteLength )

The abstract operation IsViewOutOfBounds takes arguments view and getBufferByteLength. It performs the following steps when called:

  1. Assert: Type(view) is Object and view has a [[DataView]] internal slot.
  2. Let byteLength be GetViewByteLength(view, getBufferByteLength).
  3. Let buffer be view.[[ViewedArrayBuffer]].
  4. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return true.
  5. Let bufferByteLength be getBufferByteLength(buffer).
  6. If view.[[ByteOffset]] + byteLength > bufferByteLength, then return true.
  7. Return false.

5.1.3 GetViewValue ( view, requestIndex, isLittleEndian, type )

The abstract operation GetViewValue takes arguments view, requestIndex, isLittleEndian, and type. It is used by functions on DataView instances to retrieve values from 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. Set isLittleEndian to ! ToBoolean(isLittleEndian).
  5. Let buffer be view.[[ViewedArrayBuffer]].
  6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  7. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(Unordered).
  8. NOTE: Bounds checking is not a synchronizing operation when view's backing buffer is a growable SharedArrayBuffer.
  9. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a TypeError exception.
  10. Let viewOffset be view.[[ByteOffset]].
  11. Let viewSize be view.[[ByteLength]]GetViewByteLength(view, getBufferByteLength).
  12. Let elementSize be the Element Size value specified in Table 63 for Element Type type.
  13. If getIndex + elementSize > viewSize, throw a RangeError exception.
  14. Let bufferIndex be getIndex + viewOffset.
  15. Return GetValueFromBuffer(buffer, bufferIndex, type, false, Unordered, isLittleEndian).

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

The abstract operation SetViewValue takes arguments view, requestIndex, isLittleEndian, type, and value. 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 buffer be view.[[ViewedArrayBuffer]].
  8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  9. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(Unordered).
  10. NOTE: Bounds checking is not a synchronizing operation when view's backing buffer is a growable SharedArrayBuffer.
  11. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a TypeError exception.
  12. Let viewOffset be view.[[ByteOffset]].
  13. Let viewSize be view.[[ByteLength]]GetViewByteLength(view, getBufferByteLength).
  14. Let elementSize be the Element Size value specified in Table 63 for Element Type type.
  15. If getIndex + elementSize > viewSize, throw a RangeError exception.
  16. Let bufferIndex be getIndex + viewOffset.
  17. Return SetValueInBuffer(buffer, bufferIndex, type, numberValue, false, Unordered, isLittleEndian).

5.2 Modifications to the DataView Constructor

5.2.1 DataView ( buffer [ , byteOffset [ , byteLength ] ] )

When the DataView function is called with at least one argument buffer, the following steps are taken:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]).
  3. Let offset be ? ToIndex(byteOffset).
  4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  5. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]ArrayBufferByteLength(buffer, SeqCst)..
  6. If offset > bufferByteLength, throw a RangeError exception.
  7. Let bufferIsResizable be IsResizableArrayBuffer(buffer).
  8. If bufferIsResizable is true and byteLength is undefined, then
    1. Let viewByteLength be auto.
  9. IElse if byteLength is undefined, then
    1. Let viewByteLength be bufferByteLength - offset.
  10. Else,
    1. Let viewByteLength be ? ToIndex(byteLength).
    2. If offset + viewByteLength > bufferByteLength, throw a RangeError exception.
  11. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%DataView.prototype%", « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »).
  12. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  13. Set O.[[ViewedArrayBuffer]] to buffer.
  14. Set O.[[ByteLength]] to viewByteLength.
  15. Set O.[[ByteOffset]] to offset.
  16. Return O.

5.3 Modifications to Properties of the DataView Prototype Object

5.3.1 get DataView.prototype.byteLength

DataView.prototype.byteLength is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[DataView]]).
  3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
  4. Let buffer be O.[[ViewedArrayBuffer]].
  5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  6. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  7. If IsViewOutOfBounds(O, getBufferByteLength) is true, throw a TypeError exception.
  8. Let size be O.[[ByteLength]]GetViewByteLength(O, getBufferByteLength).
  9. Return 𝔽(size).

5.3.2 get DataView.prototype.byteOffset

DataView.prototype.byteOffset is an accessor property whose set accessor function is undefined. Its get accessor function performs the following steps:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[DataView]]).
  3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
  4. Let buffer be O.[[ViewedArrayBuffer]].
  5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  6. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
  7. If IsViewOutOfBounds(O, getBufferByteLength) is true, throw a TypeError exception.
  8. Let offset be O.[[ByteOffset]].
  9. Return 𝔽(offset).

6 Modifications to Abstract Operations for Atomics

6.1 ValidateAtomicAccess ( typedArray, requestIndex )

The abstract operation ValidateAtomicAccess takes arguments typedArray and requestIndex. It performs the following steps when called:

  1. Assert: typedArray is an Object that has a [[ViewedArrayBuffer]] internal slot.
  2. Let getBufferByteLength be MakeIdempotentArrayBufferByteLengthGetter(Unordered).
  3. Let length be typedArray.[[ArrayLength]]IntegerIndexedObjectLength(typedArray, getBufferByteLength).
  4. If length is out-of-bounds, set length to 0.
  5. Let accessIndex be ? ToIndex(requestIndex).
  6. Assert: accessIndex ≥ 0.
  7. If accessIndexlength, throw a RangeError exception.
  8. Let arrayTypeName be typedArray.[[TypedArrayName]].
  9. Let elementSize be the Element Size value specified in Table 63 for arrayTypeName.
  10. Let offset be typedArray.[[ByteOffset]].
  11. Return (accessIndex × elementSize) + offset.

7 Resizable ArrayBuffer and growable SharedArrayBuffer Guidelines

Note 1

The following are guidelines for ECMAScript programmers working with resizable ArrayBuffer and growable SharedArrayBuffer.

We recommend that programs be tested in their deployment environments where possible. The amount of available physical memory differ greatly between hardware devices. Similarly, virtual memory subsystems also differ greatly between hardware devices as well as operating systems. An application that runs without out-of-memory errors on a 64-bit desktop web browser could run out of memory on a 32-bit mobile web browser.

When choosing a value for the "maxByteLength" option for resizable ArrayBuffer and growable SharedArrayBuffer, we recommend that the smallest possible size for the application be chosen. We recommend that "maxByteLength" does not exceed 1073741824, or 1GiB.

Please note that successfully constructing a ResizableArrayBuffer or a growable SharedArrayBuffer for a particular maximum size does not guarantee that future resizes and grows will succeed.

Note 2

The following are guidelines for ECMAScript implementers implementing resizable ArrayBuffer.

Resizable ArrayBuffer can be implemented as copying upon resize, as in-place growth via reserving virtual memory up front, or as a combination of both for different values of the constructor's "maxByteLength" option.

If a host is multi-tenanted (i.e. it runs many JavaScript applications simultaneously), such as a web browser, and its implementations choose to implement in-place growth by reserving virtual memory, we recommend that both 32-bit and 64-bit implementations throw for values of "maxByteLength" ≥ 1GiB to 1.5GiB. This is to reduce the likelihood a single application can exhaust the virtual memory address space and to reduce interoperability risks.

If a host does not have virtual memory, such as those running on embedded devices without an MMU, or if a host only implements resizing by copying, it may accept any Number value for the "maxByteLength" option. However, we recommend a RangeError be thrown if a memory block of the requested size can never be allocated. For example, if the requested size is greater than the maximium amount of usable memory on the device.

Note 3

The following are guidelines for ECMAScript implementers implementing growable SharedArrayBuffer.

We recommend growable SharedArrayBuffer be implemented as in-place growth via reserving virtual memory up front.

Because grow operations can be concurrent with memory accesses on a growable SharedArrayBuffer, the constraints of the memory model require that even unordered accesses do not "tear" (bits of their values will not be mixed). In practice, this means the underlying data block of a growable SharedArrayBuffer cannot be grown by being copied without stopping the world. We do not recommend stopping the world as an implementation strategy because it introduces a serialization point and is slow.

Grown memory must appear zeroed from the moment of its creation, including to any racy concurrent accesses. This can be accomplished via zero-filled-on-demand virtual memory pages, or careful synchronization if manually zeroing memory.

In practice it is difficult to implement growable SharedArrayBuffer by copying on hosts that do not have virtual memory, such as those running on embedded devices without an MMU. Memory usage behaviour of growable SharedArrayBuffers on such hosts may significantly differ from that of hosts with virtual memory. Such hosts should clearly communicate memory usage expectations to users.

8 Mechanical Changes Omitted for Brevity