2020-12-02 20:49:31 +00:00
/*
2023-04-12 22:47:15 +00:00
* Copyright ( c ) 2020 - 2023 , Linus Groh < linusg @ serenityos . org >
2020-12-02 20:49:31 +00:00
*
2021-04-22 08:24:48 +00:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-12-02 20:49:31 +00:00
*/
2021-10-09 11:31:13 +00:00
# include <LibJS/Runtime/AbstractOperations.h>
2020-12-02 20:49:31 +00:00
# include <LibJS/Runtime/ArrayBuffer.h>
2022-04-15 15:39:17 +00:00
# include <LibJS/Runtime/ArrayBufferConstructor.h>
2020-12-02 20:49:31 +00:00
# include <LibJS/Runtime/GlobalObject.h>
namespace JS {
2024-11-14 15:01:23 +00:00
GC_DEFINE_ALLOCATOR ( ArrayBuffer ) ;
2023-11-19 08:45:05 +00:00
2024-11-14 15:01:23 +00:00
ThrowCompletionOr < GC : : Ref < ArrayBuffer > > ArrayBuffer : : create ( Realm & realm , size_t byte_length )
2020-12-02 20:49:31 +00:00
{
2021-10-09 11:31:13 +00:00
auto buffer = ByteBuffer : : create_zeroed ( byte_length ) ;
2022-02-07 12:34:32 +00:00
if ( buffer . is_error ( ) )
2022-08-16 19:33:17 +00:00
return realm . vm ( ) . throw_completion < RangeError > ( ErrorType : : NotEnoughMemoryToAllocate , byte_length ) ;
2022-02-07 12:34:32 +00:00
2024-11-13 16:50:17 +00:00
return realm . create < ArrayBuffer > ( buffer . release_value ( ) , realm . intrinsics ( ) . array_buffer_prototype ( ) ) ;
2020-12-02 20:49:31 +00:00
}
2024-11-14 15:01:23 +00:00
GC : : Ref < ArrayBuffer > ArrayBuffer : : create ( Realm & realm , ByteBuffer buffer )
2021-12-13 22:05:42 +00:00
{
2024-11-13 16:50:17 +00:00
return realm . create < ArrayBuffer > ( move ( buffer ) , realm . intrinsics ( ) . array_buffer_prototype ( ) ) ;
2021-12-13 22:05:42 +00:00
}
2024-11-14 15:01:23 +00:00
GC : : Ref < ArrayBuffer > ArrayBuffer : : create ( Realm & realm , ByteBuffer * buffer )
2021-05-17 17:09:31 +00:00
{
2024-11-13 16:50:17 +00:00
return realm . create < ArrayBuffer > ( buffer , realm . intrinsics ( ) . array_buffer_prototype ( ) ) ;
2021-05-17 17:09:31 +00:00
}
2021-09-05 10:13:15 +00:00
ArrayBuffer : : ArrayBuffer ( ByteBuffer buffer , Object & prototype )
2022-12-14 11:17:58 +00:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2023-09-11 08:35:03 +00:00
, m_data_block ( DataBlock { move ( buffer ) , DataBlock : : Shared : : No } )
2021-06-10 19:44:17 +00:00
, m_detach_key ( js_undefined ( ) )
2020-12-02 20:49:31 +00:00
{
}
2021-05-17 17:09:31 +00:00
ArrayBuffer : : ArrayBuffer ( ByteBuffer * buffer , Object & prototype )
2022-12-14 11:17:58 +00:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2023-09-11 08:35:03 +00:00
, m_data_block ( DataBlock { buffer , DataBlock : : Shared : : No } )
2021-06-10 19:44:17 +00:00
, m_detach_key ( js_undefined ( ) )
2021-05-17 17:09:31 +00:00
{
}
2021-06-10 21:59:24 +00:00
void ArrayBuffer : : visit_edges ( Cell : : Visitor & visitor )
{
2021-09-11 12:05:12 +00:00
Base : : visit_edges ( visitor ) ;
2021-06-10 21:59:24 +00:00
visitor . visit ( m_detach_key ) ;
}
2023-05-25 15:22:01 +00:00
// 6.2.9.1 CreateByteDataBlock ( size ), https://tc39.es/ecma262/#sec-createbytedatablock
2023-09-11 08:35:03 +00:00
ThrowCompletionOr < DataBlock > create_byte_data_block ( VM & vm , size_t size )
2023-05-25 15:22:01 +00:00
{
// 1. If size > 2^53 - 1, throw a RangeError exception.
if ( size > MAX_ARRAY_LIKE_INDEX )
return vm . throw_completion < RangeError > ( ErrorType : : InvalidLength , " array buffer " ) ;
// 2. Let db be a new Data Block value consisting of size bytes. If it is impossible to create such a Data Block, throw a RangeError exception.
// 3. Set all of the bytes of db to 0.
auto data_block = ByteBuffer : : create_zeroed ( size ) ;
if ( data_block . is_error ( ) )
return vm . throw_completion < RangeError > ( ErrorType : : NotEnoughMemoryToAllocate , size ) ;
// 4. Return db.
2023-09-11 08:35:03 +00:00
return DataBlock { data_block . release_value ( ) , DataBlock : : Shared : : No } ;
}
// FIXME: The returned DataBlock is not shared in the sense that the standard specifies it.
// 6.2.9.2 CreateSharedByteDataBlock ( size ), https://tc39.es/ecma262/#sec-createsharedbytedatablock
static ThrowCompletionOr < DataBlock > create_shared_byte_data_block ( VM & vm , size_t size )
{
// 1. Let db be a new Shared Data Block value consisting of size bytes. If it is impossible to create such a Shared Data Block, throw a RangeError exception.
auto data_block = ByteBuffer : : create_zeroed ( size ) ;
if ( data_block . is_error ( ) )
return vm . throw_completion < RangeError > ( ErrorType : : NotEnoughMemoryToAllocate , size ) ;
// 2. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
// 3. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
// 4. Let zero be « 0 ».
// 5. For each index i of db, do
// a. Append WriteSharedMemory { [[Order]]: init, [[NoTear]]: true, [[Block]]: db, [[ByteIndex]]: i, [[ElementSize]]: 1, [[Payload]]: zero } to eventsRecord.[[EventList]].
// 6. Return db.
return DataBlock { data_block . release_value ( ) , DataBlock : : Shared : : Yes } ;
2023-05-25 15:22:01 +00:00
}
2023-06-17 19:03:31 +00:00
// 6.2.9.3 CopyDataBlockBytes ( toBlock, toIndex, fromBlock, fromIndex, count ), https://tc39.es/ecma262/#sec-copydatablockbytes
2023-09-12 00:03:42 +00:00
void copy_data_block_bytes ( ByteBuffer & to_block , u64 to_index , ByteBuffer const & from_block , u64 from_index , u64 count )
2023-06-17 19:03:31 +00:00
{
// 1. Assert: fromBlock and toBlock are distinct values.
VERIFY ( & to_block ! = & from_block ) ;
// 2. Let fromSize be the number of bytes in fromBlock.
auto from_size = from_block . size ( ) ;
// 3. Assert: fromIndex + count ≤ fromSize.
VERIFY ( from_index + count < = from_size ) ;
// 4. Let toSize be the number of bytes in toBlock.
auto to_size = to_block . size ( ) ;
// 5. Assert: toIndex + count ≤ toSize.
VERIFY ( to_index + count < = to_size ) ;
// 6. Repeat, while count > 0,
while ( count > 0 ) {
// FIXME: a. If fromBlock is a Shared Data Block, then
// FIXME: i. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
// FIXME: ii. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
// FIXME: iii. Let bytes be a List whose sole element is a nondeterministically chosen byte value.
// FIXME: iv. NOTE: In implementations, bytes is the result of a non-atomic read instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
// FIXME: v. Let readEvent be ReadSharedMemory { [[Order]]: Unordered, [[NoTear]]: true, [[Block]]: fromBlock, [[ByteIndex]]: fromIndex, [[ElementSize]]: 1 }.
// FIXME: vi. Append readEvent to eventsRecord.[[EventList]].
// FIXME: vii. Append Chosen Value Record { [[Event]]: readEvent, [[ChosenValue]]: bytes } to execution.[[ChosenValues]].
// FIXME: viii. If toBlock is a Shared Data Block, then
// FIXME: 1. Append WriteSharedMemory { [[Order]]: Unordered, [[NoTear]]: true, [[Block]]: toBlock, [[ByteIndex]]: toIndex, [[ElementSize]]: 1, [[Payload]]: bytes } to eventsRecord.[[EventList]].
// FIXME: ix. Else,
// FIXME: 1. Set toBlock[toIndex] to bytes[0].
// FIXME: b. Else,
// FIXME: i. Assert: toBlock is not a Shared Data Block.
// ii. Set toBlock[toIndex] to fromBlock[fromIndex].
to_block [ to_index ] = from_block [ from_index ] ;
// c. Set toIndex to toIndex + 1.
+ + to_index ;
// d. Set fromIndex to fromIndex + 1.
+ + from_index ;
// e. Set count to count - 1.
- - count ;
}
// 7. Return unused.
}
2023-10-14 15:20:23 +00:00
// 25.1.3.1 AllocateArrayBuffer ( constructor, byteLength [ , maxByteLength ] ), https://tc39.es/ecma262/#sec-allocatearraybuffer
2023-10-14 23:10:55 +00:00
ThrowCompletionOr < ArrayBuffer * > allocate_array_buffer ( VM & vm , FunctionObject & constructor , size_t byte_length , Optional < size_t > const & max_byte_length )
2021-10-09 11:31:13 +00:00
{
2023-10-14 23:10:55 +00:00
// 1. Let slots be « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] ».
// 2. If maxByteLength is present and maxByteLength is not empty, let allocatingResizableBuffer be true; otherwise let allocatingResizableBuffer be false.
auto allocating_resizable_buffer = max_byte_length . has_value ( ) ;
// 3. If allocatingResizableBuffer is true, then
if ( allocating_resizable_buffer ) {
// a. If byteLength > maxByteLength, throw a RangeError exception.
if ( byte_length > * max_byte_length )
return vm . throw_completion < RangeError > ( ErrorType : : ByteLengthExceedsMaxByteLength , byte_length , * max_byte_length ) ;
// b. Append [[ArrayBufferMaxByteLength]] to slots.
}
// 4. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", slots).
2022-12-14 18:34:32 +00:00
auto obj = TRY ( ordinary_create_from_constructor < ArrayBuffer > ( vm , constructor , & Intrinsics : : array_buffer_prototype , nullptr ) ) ;
2021-10-09 11:31:13 +00:00
2023-10-14 23:10:55 +00:00
// 5. Let block be ? CreateByteDataBlock(byteLength).
2023-05-25 15:22:01 +00:00
auto block = TRY ( create_byte_data_block ( vm , byte_length ) ) ;
2021-10-09 11:31:13 +00:00
2023-10-14 23:10:55 +00:00
// 6. Set obj.[[ArrayBufferData]] to block.
2023-09-11 08:35:03 +00:00
obj - > set_data_block ( move ( block ) ) ;
2021-10-09 11:31:13 +00:00
2023-10-14 23:10:55 +00:00
// 7. Set obj.[[ArrayBufferByteLength]] to byteLength.
2021-10-09 11:31:13 +00:00
2023-10-14 23:10:55 +00:00
// 8. If allocatingResizableBuffer is true, then
if ( allocating_resizable_buffer ) {
// a. If it is not possible to create a Data Block block consisting of maxByteLength bytes, throw a RangeError exception.
// b. 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.
if ( auto result = obj - > buffer ( ) . try_ensure_capacity ( * max_byte_length ) ; result . is_error ( ) )
return vm . throw_completion < RangeError > ( ErrorType : : NotEnoughMemoryToAllocate , * max_byte_length ) ;
// c. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.
obj - > set_max_byte_length ( * max_byte_length ) ;
}
// 9. Return obj.
2022-12-14 18:34:32 +00:00
return obj . ptr ( ) ;
2021-10-09 11:31:13 +00:00
}
2024-08-20 14:29:45 +00:00
// 25.1.3.3 ArrayBufferCopyAndDetach ( arrayBuffer, newLength, preserveResizability ), https://tc39.es/ecma262/#sec-arraybuffercopyanddetach
ThrowCompletionOr < ArrayBuffer * > array_buffer_copy_and_detach ( VM & vm , ArrayBuffer & array_buffer , Value new_length , PreserveResizability preserve_resizability )
{
auto & realm = * vm . current_realm ( ) ;
// 1. Perform ? RequireInternalSlot(arrayBuffer, [[ArrayBufferData]]).
// 2. If IsSharedArrayBuffer(arrayBuffer) is true, throw a TypeError exception.
if ( array_buffer . is_shared_array_buffer ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : SharedArrayBuffer ) ;
// 3. If newLength is undefined, then
// a. Let newByteLength be arrayBuffer.[[ArrayBufferByteLength]].
// 4. Else,
// a. Let newByteLength be ? ToIndex(newLength).
auto new_byte_length = new_length . is_undefined ( ) ? array_buffer . byte_length ( ) : TRY ( new_length . to_index ( vm ) ) ;
// 5. If IsDetachedBuffer(arrayBuffer) is true, throw a TypeError exception.
if ( array_buffer . is_detached ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : DetachedArrayBuffer ) ;
Optional < size_t > new_max_byte_length ;
// 6. If preserveResizability is PRESERVE-RESIZABILITY and IsFixedLengthArrayBuffer(arrayBuffer) is false, then
if ( preserve_resizability = = PreserveResizability : : PreserveResizability & & ! array_buffer . is_fixed_length ( ) ) {
// a. Let newMaxByteLength be arrayBuffer.[[ArrayBufferMaxByteLength]].
new_max_byte_length = array_buffer . max_byte_length ( ) ;
}
// 7. Else,
else {
// a. Let newMaxByteLength be EMPTY.
}
// 8. If arrayBuffer.[[ArrayBufferDetachKey]] is not undefined, throw a TypeError exception.
if ( ! array_buffer . detach_key ( ) . is_undefined ( ) )
return vm . throw_completion < TypeError > ( ErrorType : : DetachKeyMismatch , array_buffer . detach_key ( ) , js_undefined ( ) ) ;
// 9. Let newBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, newByteLength, newMaxByteLength).
auto * new_buffer = TRY ( allocate_array_buffer ( vm , realm . intrinsics ( ) . array_buffer_constructor ( ) , new_byte_length , new_max_byte_length ) ) ;
// 10. Let copyLength be min(newByteLength, arrayBuffer.[[ArrayBufferByteLength]]).
auto copy_length = min ( new_byte_length , array_buffer . byte_length ( ) ) ;
// 11. Let fromBlock be arrayBuffer.[[ArrayBufferData]].
// 12. Let toBlock be newBuffer.[[ArrayBufferData]].
// 13. Perform CopyDataBlockBytes(toBlock, 0, fromBlock, 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 a zero-copy move or a realloc.
copy_data_block_bytes ( new_buffer - > buffer ( ) , 0 , array_buffer . buffer ( ) , 0 , copy_length ) ;
// 15. Perform ! DetachArrayBuffer(arrayBuffer).
2024-08-20 14:34:09 +00:00
MUST ( detach_array_buffer ( vm , array_buffer ) ) ;
2024-08-20 14:29:45 +00:00
// 16. Return newBuffer.
return new_buffer ;
}
// 25.1.3.5 DetachArrayBuffer ( arrayBuffer [ , key ] ), https://tc39.es/ecma262/#sec-detacharraybuffer
2022-08-21 16:25:31 +00:00
ThrowCompletionOr < void > detach_array_buffer ( VM & vm , ArrayBuffer & array_buffer , Optional < Value > key )
2022-04-07 17:03:08 +00:00
{
// 1. Assert: IsSharedArrayBuffer(arrayBuffer) is false.
2023-12-28 14:27:42 +00:00
VERIFY ( ! array_buffer . is_shared_array_buffer ( ) ) ;
2022-04-07 17:03:08 +00:00
// 2. If key is not present, set key to undefined.
if ( ! key . has_value ( ) )
key = js_undefined ( ) ;
// 3. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception.
if ( ! same_value ( array_buffer . detach_key ( ) , * key ) )
2022-08-16 19:33:17 +00:00
return vm . throw_completion < TypeError > ( ErrorType : : DetachKeyMismatch , * key , array_buffer . detach_key ( ) ) ;
2022-04-07 17:03:08 +00:00
// 4. Set arrayBuffer.[[ArrayBufferData]] to null.
// 5. Set arrayBuffer.[[ArrayBufferByteLength]] to 0.
array_buffer . detach_buffer ( ) ;
// 6. Return unused.
2022-05-02 18:54:39 +00:00
return { } ;
2022-04-07 17:03:08 +00:00
}
2024-08-20 14:29:45 +00:00
// 25.1.3.6 CloneArrayBuffer ( srcBuffer, srcByteOffset, srcLength, cloneConstructor ), https://tc39.es/ecma262/#sec-clonearraybuffer
2022-08-21 16:25:31 +00:00
ThrowCompletionOr < ArrayBuffer * > clone_array_buffer ( VM & vm , ArrayBuffer & source_buffer , size_t source_byte_offset , size_t source_length )
2022-02-08 19:36:48 +00:00
{
2022-08-21 16:25:31 +00:00
auto & realm = * vm . current_realm ( ) ;
2022-04-15 15:39:17 +00:00
// 1. Assert: IsDetachedBuffer(srcBuffer) is false.
VERIFY ( ! source_buffer . is_detached ( ) ) ;
2022-02-08 19:36:48 +00:00
2022-04-15 15:39:17 +00:00
// 2. Let targetBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, srcLength).
2023-04-12 22:47:15 +00:00
auto * target_buffer = TRY ( allocate_array_buffer ( vm , realm . intrinsics ( ) . array_buffer_constructor ( ) , source_length ) ) ;
2022-02-08 19:36:48 +00:00
// 3. Let srcBlock be srcBuffer.[[ArrayBufferData]].
auto & source_block = source_buffer . buffer ( ) ;
// 4. Let targetBlock be targetBuffer.[[ArrayBufferData]].
auto & target_block = target_buffer - > buffer ( ) ;
// 5. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).
2023-06-17 19:09:18 +00:00
copy_data_block_bytes ( target_block , 0 , source_block , source_byte_offset , source_length ) ;
2022-02-08 19:36:48 +00:00
// 6. Return targetBuffer.
return target_buffer ;
}
2024-08-20 14:29:45 +00:00
// 25.1.3.7 GetArrayBufferMaxByteLengthOption ( options ), https://tc39.es/ecma262/#sec-getarraybuffermaxbytelengthoption
2023-10-14 23:10:55 +00:00
ThrowCompletionOr < Optional < size_t > > get_array_buffer_max_byte_length_option ( VM & vm , Value options )
{
// 1. If options is not an Object, return empty.
if ( ! options . is_object ( ) )
return OptionalNone { } ;
// 2. Let maxByteLength be ? Get(options, "maxByteLength").
auto max_byte_length = TRY ( options . as_object ( ) . get ( vm . names . maxByteLength ) ) ;
// 3. If maxByteLength is undefined, return empty.
if ( max_byte_length . is_undefined ( ) )
return OptionalNone { } ;
// 4. Return ? ToIndex(maxByteLength).
return TRY ( max_byte_length . to_index ( vm ) ) ;
}
2023-10-14 15:20:23 +00:00
// 25.2.2.1 AllocateSharedArrayBuffer ( constructor, byteLength [ , maxByteLength ] ), https://tc39.es/ecma262/#sec-allocatesharedarraybuffer
2024-11-14 15:01:23 +00:00
ThrowCompletionOr < GC : : Ref < ArrayBuffer > > allocate_shared_array_buffer ( VM & vm , FunctionObject & constructor , size_t byte_length )
2023-07-01 04:07:26 +00:00
{
// 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%SharedArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]] »).
auto obj = TRY ( ordinary_create_from_constructor < ArrayBuffer > ( vm , constructor , & Intrinsics : : shared_array_buffer_prototype , nullptr ) ) ;
2023-09-11 08:35:03 +00:00
// 2. Let block be ? CreateSharedByteDataBlock(byteLength).
auto block = TRY ( create_shared_byte_data_block ( vm , byte_length ) ) ;
2023-07-01 04:07:26 +00:00
// 3. Set obj.[[ArrayBufferData]] to block.
// 4. Set obj.[[ArrayBufferByteLength]] to byteLength.
2023-09-11 08:35:03 +00:00
obj - > set_data_block ( move ( block ) ) ;
2023-07-01 04:07:26 +00:00
// 5. Return obj.
return obj ;
}
2020-12-02 20:49:31 +00:00
}