diff options
author | Timothy Flynn <trflynn89@pm.me> | 2022-04-15 11:13:58 -0400 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-04-16 16:49:52 +0100 |
commit | 39b308ba524133a197f123a6ef7e7dfba1eaf285 (patch) | |
tree | 19f4aba3eb6a8d47127d8c410f42b89afb9678f6 /Userland/Libraries/LibJS | |
parent | 0174993bea2fd030eaf1d57dec650a213e76b489 (diff) | |
download | serenity-39b308ba524133a197f123a6ef7e7dfba1eaf285.zip |
LibJS: Factor out TypedArrayElement{Size,Type} abstract operations
This is an editorial change in the ECMA-262 spec. See:
https://github.com/tc39/ecma262/commit/a90670d5
This also adds missing spec comments to the following prototypes which
were affected by this change:
Atomics.load
Atomics.store
%TypedArray%.prototype.slice
%TypedArray%.prototype.subarray
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp | 83 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/TypedArray.cpp | 85 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/TypedArray.h | 34 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp | 201 |
4 files changed, 244 insertions, 159 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp b/Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp index e5fb0632a6..3a0bb7d368 100644 --- a/Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/AtomicsObject.cpp @@ -27,25 +27,23 @@ static ThrowCompletionOr<ArrayBuffer*> validate_integer_typed_array(GlobalObject // 3. Let buffer be typedArray.[[ViewedArrayBuffer]]. auto* buffer = typed_array.viewed_array_buffer(); - // 4. Let typeName be typedArray.[[TypedArrayName]]. auto const& type_name = typed_array.element_name(); - // 5. Let type be the Element Type value in Table 72 for typeName. - - // 6. If waitable is true, then + // 4. If waitable is true, then if (waitable) { - // a. If typeName is not "Int32Array" or "BigInt64Array", throw a TypeError exception. + // a. If typedArray.[[TypedArrayName]] is not "Int32Array" or "BigInt64Array", throw a TypeError exception. if ((type_name != vm.names.Int32Array.as_string()) && (type_name != vm.names.BigInt64Array.as_string())) return vm.throw_completion<TypeError>(global_object, ErrorType::TypedArrayTypeIsNot, type_name, "Int32 or BigInt64"sv); } - // 7. Else, + // 5. Else, else { - // a. If ! IsUnclampedIntegerElementType(type) is false and ! IsBigIntElementType(type) is false, throw a TypeError exception. + // a. Let type be TypedArrayElementType(typedArray). + // b. If ! IsUnclampedIntegerElementType(type) is false and ! IsBigIntElementType(type) is false, throw a TypeError exception. if (!typed_array.is_unclamped_integer_element_type() && !typed_array.is_bigint_element_type()) return vm.throw_completion<TypeError>(global_object, ErrorType::TypedArrayTypeIsNot, type_name, "an unclamped integer or BigInt"sv); } - // 8. Return buffer. + // 6. Return buffer. return buffer; } @@ -66,14 +64,13 @@ static ThrowCompletionOr<size_t> validate_atomic_access(GlobalObject& global_obj if (access_index >= length) return vm.throw_completion<RangeError>(global_object, ErrorType::IndexOutOfRange, access_index, typed_array.array_length()); - // 5. Let arrayTypeName be typedArray.[[TypedArrayName]]. - // 6. Let elementSize be the Element Size value specified in Table 72 for arrayTypeName. + // 5. Let elementSize be TypedArrayElementSize(typedArray). auto element_size = typed_array.element_size(); - // 7. Let offset be typedArray.[[ByteOffset]]. + // 6. Let offset be typedArray.[[ByteOffset]]. auto offset = typed_array.byte_offset(); - // 8. Return (accessIndex ร elementSize) + offset. + // 7. Return (accessIndex ร elementSize) + offset. return (access_index * element_size) + offset; } @@ -88,26 +85,23 @@ static ThrowCompletionOr<Value> atomic_read_modify_write(GlobalObject& global_ob // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). auto indexed_position = TRY(validate_atomic_access(global_object, typed_array, index)); - // 3. Let arrayTypeName be typedArray.[[TypedArrayName]]. - Value value_to_set; - // 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value). + // 3. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value). if (typed_array.content_type() == TypedArrayBase::ContentType::BigInt) value_to_set = TRY(value.to_bigint(global_object)); - // 5. Otherwise, let v be ๐ฝ(? ToIntegerOrInfinity(value)). + // 4. Otherwise, let v be ๐ฝ(? ToIntegerOrInfinity(value)). else value_to_set = Value(TRY(value.to_integer_or_infinity(global_object))); - // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if (buffer->is_detached()) return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // 7. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached. - - // 8. Let elementType be the Element Type value in Table 72 for arrayTypeName. + // 6. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached. - // 9. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op). + // 7. Let elementType be TypedArrayElementType(typedArray). + // 8. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op). return typed_array.get_modify_set_value_in_buffer(indexed_position, value_to_set, move(operation)); } @@ -208,12 +202,10 @@ static ThrowCompletionOr<Value> atomic_compare_exchange_impl(GlobalObject& globa // 3. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). auto indexed_position = TRY(validate_atomic_access(global_object, typed_array, vm.argument(1))); - // 4. Let arrayTypeName be typedArray.[[TypedArrayName]]. - Value expected; Value replacement; - // 5. If typedArray.[[ContentType]] is BigInt, then + // 4. If typedArray.[[ContentType]] is BigInt, then if (typed_array.content_type() == TypedArrayBase::ContentType::BigInt) { // a. Let expected be ? ToBigInt(expectedValue). expected = TRY(vm.argument(2).to_bigint(global_object)); @@ -221,7 +213,7 @@ static ThrowCompletionOr<Value> atomic_compare_exchange_impl(GlobalObject& globa // b. Let replacement be ? ToBigInt(replacementValue). replacement = TRY(vm.argument(3).to_bigint(global_object)); } - // 6. Else, + // 5. Else, else { // a. Let expected be ๐ฝ(? ToIntegerOrInfinity(expectedValue)). expected = Value(TRY(vm.argument(2).to_integer_or_infinity(global_object))); @@ -230,28 +222,28 @@ static ThrowCompletionOr<Value> atomic_compare_exchange_impl(GlobalObject& globa replacement = Value(TRY(vm.argument(3).to_integer_or_infinity(global_object))); } - // 7. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if (buffer->is_detached()) return vm.template throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // 8. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached. + // 7. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached. - // 9. Let elementType be the Element Type value in Table 72 for arrayTypeName. - // 10. Let elementSize be the Element Size value specified in Table 72 for Element Type elementType. + // 8. Let elementType be TypedArrayElementType(typedArray). + // 9. Let elementSize be TypedArrayElementSize(typedArray). - // 11. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. + // 10. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record. constexpr bool is_little_endian = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__; - // 12. Let expectedBytes be NumericToRawBytes(elementType, expected, isLittleEndian). + // 11. Let expectedBytes be NumericToRawBytes(elementType, expected, isLittleEndian). auto expected_bytes = numeric_to_raw_bytes<T>(global_object, expected, is_little_endian); - // 13. Let replacementBytes be NumericToRawBytes(elementType, replacement, isLittleEndian). + // 12. Let replacementBytes be NumericToRawBytes(elementType, replacement, isLittleEndian). auto replacement_bytes = numeric_to_raw_bytes<T>(global_object, replacement, is_little_endian); // FIXME: Implement SharedArrayBuffer case. - // 14. If IsSharedArrayBuffer(buffer) is true, then + // 13. If IsSharedArrayBuffer(buffer) is true, then // a-i. - // 15. Else, + // 14. Else, // a. Let rawBytesRead be a List of length elementSize whose elements are the sequence of elementSize bytes starting with block[indexedPosition]. auto raw_bytes_read = block.slice(indexed_position, sizeof(T)); @@ -269,7 +261,7 @@ static ThrowCompletionOr<Value> atomic_compare_exchange_impl(GlobalObject& globa (void)AK::atomic_compare_exchange_strong(v, *e, *r); } - // 16. Return RawBytesToNumeric(elementType, rawBytesRead, isLittleEndian). + // 15. Return RawBytesToNumeric(elementType, rawBytesRead, isLittleEndian). return raw_bytes_to_numeric<T>(global_object, raw_bytes_read, is_little_endian); } @@ -321,15 +313,21 @@ JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::is_lock_free) // 25.4.8 Atomics.load ( typedArray, index ), https://tc39.es/ecma262/#sec-atomics.load JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::load) { + // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray). auto* typed_array = TRY(typed_array_from(global_object, vm.argument(0))); - TRY(validate_integer_typed_array(global_object, *typed_array)); + // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). auto indexed_position = TRY(validate_atomic_access(global_object, *typed_array, vm.argument(1))); + // 3. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if (typed_array->viewed_array_buffer()->is_detached()) return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); + // 4. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ValidateAtomicAccess on the preceding line can have arbitrary side effects, which could cause the buffer to become detached. + + // 5. Let elementType be TypedArrayElementType(typedArray). + // 6. Return GetValueFromBuffer(buffer, indexedPosition, elementType, true, SeqCst). return typed_array->get_value_from_buffer(indexed_position, ArrayBuffer::Order::SeqCst, true); } @@ -352,23 +350,34 @@ JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::or_) // 25.4.10 Atomics.store ( typedArray, index, value ), https://tc39.es/ecma262/#sec-atomics.store JS_DEFINE_NATIVE_FUNCTION(AtomicsObject::store) { + // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray). auto* typed_array = TRY(typed_array_from(global_object, vm.argument(0))); - TRY(validate_integer_typed_array(global_object, *typed_array)); + // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index). auto indexed_position = TRY(validate_atomic_access(global_object, *typed_array, vm.argument(1))); auto value = vm.argument(2); Value value_to_set; + + // 3. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value). if (typed_array->content_type() == TypedArrayBase::ContentType::BigInt) value_to_set = TRY(value.to_bigint(global_object)); + // 4. Otherwise, let v be ๐ฝ(? ToIntegerOrInfinity(value)). else value_to_set = Value(TRY(value.to_integer_or_infinity(global_object))); + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if (typed_array->viewed_array_buffer()->is_detached()) return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); + // 6. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call to ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could cause the buffer to become detached. + + // 7. Let elementType be TypedArrayElementType(typedArray). + // 8. Perform SetValueInBuffer(buffer, indexedPosition, elementType, v, true, SeqCst). typed_array->set_value_in_buffer(indexed_position, value_to_set, ArrayBuffer::Order::SeqCst, true); + + // 9. Return v. return value_to_set; } diff --git a/Userland/Libraries/LibJS/Runtime/TypedArray.cpp b/Userland/Libraries/LibJS/Runtime/TypedArray.cpp index 54208e61fe..b3f8f3277b 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArray.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArray.cpp @@ -54,36 +54,34 @@ static ThrowCompletionOr<void> initialize_typed_array_from_array_buffer(GlobalOb { auto& vm = global_object.vm(); - // 1. Let constructorName be the String value of O.[[TypedArrayName]]. - - // 2. Let elementSize be the Element Size value specified in Table 72 for constructorName. + // 1. Let elementSize be TypedArrayElementSize(O). auto element_size = typed_array.element_size(); - // 3. Let offset be ? ToIndex(byteOffset). + // 2. Let offset be ? ToIndex(byteOffset). auto offset = TRY(byte_offset.to_index(global_object)); - // 4. If offset modulo elementSize โ 0, throw a RangeError exception. + // 3. If offset modulo elementSize โ 0, throw a RangeError exception. if (offset % element_size != 0) return vm.throw_completion<RangeError>(global_object, ErrorType::TypedArrayInvalidByteOffset, typed_array.class_name(), element_size, offset); size_t new_length { 0 }; - // 5. If length is not undefined, then + // 4. If length is not undefined, then if (!length.is_undefined()) { // a. Let newLength be ? ToIndex(length). new_length = TRY(length.to_index(global_object)); } - // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. if (array_buffer.is_detached()) return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // 7. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. + // 6. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. auto buffer_byte_length = array_buffer.byte_length(); Checked<size_t> new_byte_length; - // 8. If length is undefined, then + // 7. If length is undefined, then if (length.is_undefined()) { // a. If bufferByteLength modulo elementSize โ 0, throw a RangeError exception. if (buffer_byte_length % element_size != 0) @@ -96,7 +94,7 @@ static ThrowCompletionOr<void> initialize_typed_array_from_array_buffer(GlobalOb new_byte_length = buffer_byte_length; new_byte_length -= offset; } - // 9. Else, + // 8. Else, else { // a. Let newByteLength be newLength ร elementSize. new_byte_length = new_length; @@ -116,18 +114,19 @@ static ThrowCompletionOr<void> initialize_typed_array_from_array_buffer(GlobalOb if (new_byte_length.has_overflow()) return vm.throw_completion<RangeError>(global_object, ErrorType::InvalidLength, "typed array"); - // 10. Set O.[[ViewedArrayBuffer]] to buffer. + // 9. Set O.[[ViewedArrayBuffer]] to buffer. typed_array.set_viewed_array_buffer(&array_buffer); - // 11. Set O.[[ByteLength]] to newByteLength. + // 10. Set O.[[ByteLength]] to newByteLength. typed_array.set_byte_length(new_byte_length.value()); - // 12. Set O.[[ByteOffset]] to offset. + // 11. Set O.[[ByteOffset]] to offset. typed_array.set_byte_offset(offset); - // 13. Set O.[[ArrayLength]] to newByteLength / elementSize. + // 12. Set O.[[ArrayLength]] to newByteLength / elementSize. typed_array.set_array_length(new_byte_length.value() / element_size); + // 13. Return unused. return {}; } @@ -145,38 +144,34 @@ static ThrowCompletionOr<void> initialize_typed_array_from_typed_array(GlobalObj if (src_data->is_detached()) return vm.template throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // 3. Let constructorName be the String value of O.[[TypedArrayName]]. - // 4. Let elementType be the Element Type value in Table 72 for constructorName. - - // 5. Let elementLength be srcArray.[[ArrayLength]]. - auto element_length = src_array.array_length(); - - // 6. Let srcName be the String value of srcArray.[[TypedArrayName]]. - // 7. Let srcType be the Element Type value in Table 72 for srcName. + // 3. Let elementType be TypedArrayElementType(O). + // 4. Let elementSize be TypedArrayElementSize(O). + auto element_size = dest_array.element_size(); - // 8. Let srcElementSize be the Element Size value specified in Table 72 for srcName. + // 5. Let srcType be TypedArrayElementType(srcArray). + // 6. Let srcElementSize be TypedArrayElementSize(srcArray). auto src_element_size = src_array.element_size(); - // 9. Let srcByteOffset be srcArray.[[ByteOffset]]. + // 7. Let srcByteOffset be srcArray.[[ByteOffset]]. auto src_byte_offset = src_array.byte_offset(); - // 10. Let elementSize be the Element Size value specified in Table 72 for constructorName. - auto element_size = dest_array.element_size(); + // 8. Let elementLength be srcArray.[[ArrayLength]]. + auto element_length = src_array.array_length(); - // 11. Let byteLength be elementSize ร elementLength. + // 9. Let byteLength be elementSize ร elementLength. Checked<size_t> byte_length = element_size; byte_length *= element_length; if (byte_length.has_overflow()) return vm.template throw_completion<RangeError>(global_object, ErrorType::InvalidLength, "typed array"); // FIXME: - // 12. If IsSharedArrayBuffer(srcData) is false, then + // 10. If IsSharedArrayBuffer(srcData) is false, then // a. Let bufferConstructor be ? SpeciesConstructor(srcData, %ArrayBuffer%). + // 11. Else, + // a. Let bufferConstructor be %ArrayBuffer%. + // 12. If elementType is the same as srcType, then + // a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor). // 13. Else, - // a. Let bufferConstructor be %ArrayBuffer%. - // 14. If elementType is the same as srcType, then - // a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor). - // 15. Else, // a. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength). auto* data = TRY(allocate_array_buffer(global_object, *global_object.array_buffer_constructor(), byte_length.value())); @@ -213,18 +208,19 @@ static ThrowCompletionOr<void> initialize_typed_array_from_typed_array(GlobalObj // v. Set count to count - 1. } - // 16. Set O.[[ViewedArrayBuffer]] to data. + // 14. Set O.[[ViewedArrayBuffer]] to data. dest_array.set_viewed_array_buffer(data); - // 17. Set O.[[ByteLength]] to byteLength. + // 15. Set O.[[ByteLength]] to byteLength. dest_array.set_byte_length(byte_length.value()); - // 18. Set O.[[ByteOffset]] to 0. + // 16. Set O.[[ByteOffset]] to 0. dest_array.set_byte_offset(0); - // 19. Set O.[[ArrayLength]] to elementLength. + // 17. Set O.[[ArrayLength]] to elementLength. dest_array.set_array_length(element_length); + // 18. Return unused. return {}; } @@ -239,32 +235,31 @@ static ThrowCompletionOr<void> allocate_typed_array_buffer(GlobalObject& global_ return vm.template throw_completion<RangeError>(global_object, ErrorType::InvalidLength, "typed array"); // 1. Assert: O.[[ViewedArrayBuffer]] is undefined. - // 2. Let constructorName be the String value of O.[[TypedArrayName]]. - // 3. Let elementSize be the Element Size value specified in Table 72 for constructorName. + // 2. Let elementSize be TypedArrayElementSize(O). auto element_size = typed_array.element_size(); if (Checked<size_t>::multiplication_would_overflow(element_size, length)) return vm.template throw_completion<RangeError>(global_object, ErrorType::InvalidLength, "typed array"); - // 4. Let byteLength be elementSize ร length. + // 3. Let byteLength be elementSize ร length. auto byte_length = element_size * length; - // 5. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength). + // 4. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength). auto* data = TRY(allocate_array_buffer(global_object, *global_object.array_buffer_constructor(), byte_length)); - // 6. Set O.[[ViewedArrayBuffer]] to data. + // 5. Set O.[[ViewedArrayBuffer]] to data. typed_array.set_viewed_array_buffer(data); - // 7. Set O.[[ByteLength]] to byteLength. + // 6. Set O.[[ByteLength]] to byteLength. typed_array.set_byte_length(byte_length); - // 8. Set O.[[ByteOffset]] to 0. + // 7. Set O.[[ByteOffset]] to 0. typed_array.set_byte_offset(0); - // 9. Set O.[[ArrayLength]] to length. + // 8. Set O.[[ArrayLength]] to length. typed_array.set_array_length(length); - // 10. Return unused. + // 9. Return unused. return {}; } diff --git a/Userland/Libraries/LibJS/Runtime/TypedArray.h b/Userland/Libraries/LibJS/Runtime/TypedArray.h index d8f27ac701..565b8597b0 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArray.h +++ b/Userland/Libraries/LibJS/Runtime/TypedArray.h @@ -97,18 +97,15 @@ inline bool is_valid_integer_index(TypedArrayBase const& typed_array, CanonicalI template<typename T> inline Value integer_indexed_element_get(TypedArrayBase const& typed_array, CanonicalIndex property_index) { - // 1. Assert: O is an Integer-Indexed exotic object. - - // 2. If ! IsValidIntegerIndex(O, index) is false, return undefined. + // 1. If IsValidIntegerIndex(O, index) is false, return undefined. if (!is_valid_integer_index(typed_array, property_index)) return js_undefined(); - // 3. Let offset be O.[[ByteOffset]]. + // 2. Let offset be O.[[ByteOffset]]. auto offset = typed_array.byte_offset(); - // 4. Let arrayTypeName be the String value of O.[[TypedArrayName]]. - // 5. Let elementSize be the Element Size value specified in Table 64 for arrayTypeName. - // 6. Let indexedPosition be (โ(index) ร elementSize) + offset. + // 3. Let elementSize be TypedArrayElementSize(O). + // 4. Let indexedPosition be (โ(index) ร elementSize) + offset. Checked<size_t> indexed_position = property_index.as_index(); indexed_position *= typed_array.element_size(); indexed_position += offset; @@ -119,8 +116,8 @@ inline Value integer_indexed_element_get(TypedArrayBase const& typed_array, Cano return js_undefined(); } - // 7. Let elementType be the Element Type value in Table 64 for arrayTypeName. - // 8. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, true, Unordered). + // 5. Let elementType be TypedArrayElementType(O). + // 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, true, Unordered). return typed_array.viewed_array_buffer()->template get_value<T>(indexed_position.value(), true, ArrayBuffer::Order::Unordered); } @@ -132,18 +129,16 @@ inline ThrowCompletionOr<void> integer_indexed_element_set(TypedArrayBase& typed VERIFY(!value.is_empty()); auto& global_object = typed_array.global_object(); - // 1. Assert: O is an Integer-Indexed exotic object. - Value num_value; - // 2. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value). + // 1. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value). if (typed_array.content_type() == TypedArrayBase::ContentType::BigInt) num_value = TRY(value.to_bigint(global_object)); - // 3. Otherwise, let numValue be ? ToNumber(value). + // 2. Otherwise, let numValue be ? ToNumber(value). else num_value = TRY(value.to_number(global_object)); - // 4. If ! IsValidIntegerIndex(O, index) is true, then + // 3. If IsValidIntegerIndex(O, index) is true, then // NOTE: Inverted for flattened logic. if (!is_valid_integer_index(typed_array, property_index)) return {}; @@ -151,9 +146,8 @@ inline ThrowCompletionOr<void> integer_indexed_element_set(TypedArrayBase& typed // a. Let offset be O.[[ByteOffset]]. auto offset = typed_array.byte_offset(); - // b. Let arrayTypeName be the String value of O.[[TypedArrayName]]. - // c. Let elementSize be the Element Size value specified in Table 64 for arrayTypeName. - // d. Let indexedPosition be (โ(index) ร elementSize) + offset. + // b. Let elementSize be TypedArrayElementSize(O). + // c. Let indexedPosition be (โ(index) ร elementSize) + offset. Checked<size_t> indexed_position = property_index.as_index(); indexed_position *= typed_array.element_size(); indexed_position += offset; @@ -164,11 +158,11 @@ inline ThrowCompletionOr<void> integer_indexed_element_set(TypedArrayBase& typed return {}; } - // e. Let elementType be the Element Type value in Table 64 for arrayTypeName. - // f. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, numValue, true, Unordered). + // d. Let elementType be TypedArrayElementType(O). + // e. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, numValue, true, Unordered). typed_array.viewed_array_buffer()->template set_value<T>(indexed_position.value(), num_value, true, ArrayBuffer::Order::Unordered); - // 5. Return NormalCompletion(undefined). + // 4. Return unused. return {}; } diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp index e392e91163..d2621706fa 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp @@ -626,48 +626,46 @@ static ThrowCompletionOr<void> set_typed_array_from_typed_array(GlobalObject& gl if (source_buffer->is_detached()) return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // 6. Let targetName be the String value of target.[[TypedArrayName]]. - // 7. Let targetType be the Element Type value in Table 69 for targetName. - // 8. Let targetElementSize be the Element Size value specified in Table 69 for targetName. + // 6. Let targetType be TypedArrayElementType(target). + // 7. Let targetElementSize be TypedArrayElementSize(target). auto target_element_size = target.element_size(); - // 9. Let targetByteOffset be target.[[ByteOffset]]. + // 8. Let targetByteOffset be target.[[ByteOffset]]. auto target_byte_offset = target.byte_offset(); - // 10. Let srcName be the String value of source.[[TypedArrayName]]. - // 11. Let srcType be the Element Type value in Table 69 for srcName. - // 12. Let srcElementSize be the Element Size value specified in Table 69 for srcName. + // 9. Let srcType be TypedArrayElementType(source). + // 10. Let srcElementSize be TypedArrayElementSize(source). auto source_element_size = source.element_size(); - // 13. Let srcLength be source.[[ArrayLength]]. + // 11. Let srcLength be source.[[ArrayLength]]. auto source_length = source.array_length(); - // 14. Let srcByteOffset be source.[[ByteOffset]]. + // 12. Let srcByteOffset be source.[[ByteOffset]]. auto source_byte_offset = source.byte_offset(); - // 15. If targetOffset is +โ, throw a RangeError exception. + // 13. If targetOffset is +โ, throw a RangeError exception. if (isinf(target_offset)) return vm.throw_completion<RangeError>(global_object, ErrorType::TypedArrayInvalidTargetOffset, "finite"); - // 16. If srcLength + targetOffset > targetLength, throw a RangeError exception. + // 14. If srcLength + targetOffset > targetLength, throw a RangeError exception. Checked<size_t> checked = source_length; checked += static_cast<u32>(target_offset); if (checked.has_overflow() || checked.value() > target_length) return vm.throw_completion<RangeError>(global_object, ErrorType::TypedArrayOverflowOrOutOfBounds, "target length"); - // 17. If target.[[ContentType]] โ source.[[ContentType]], throw a TypeError exception. + // 15. If target.[[ContentType]] โ source.[[ContentType]], throw a TypeError exception. if (target.content_type() != source.content_type()) return vm.throw_completion<TypeError>(global_object, ErrorType::TypedArrayInvalidCopy, target.class_name(), source.class_name()); - // FIXME: 18. If both IsSharedArrayBuffer(srcBuffer) and IsSharedArrayBuffer(targetBuffer) are true, then + // FIXME: 16. If both IsSharedArrayBuffer(srcBuffer) and IsSharedArrayBuffer(targetBuffer) are true, then // FIXME: a. If srcBuffer.[[ArrayBufferData]] and targetBuffer.[[ArrayBufferData]] are the same Shared Data Block values, let same be true; else let same be false. - // 19. Else, let same be SameValue(srcBuffer, targetBuffer). + // 17. Else, let same be SameValue(srcBuffer, targetBuffer). auto same = same_value(source_buffer, target_buffer); - size_t source_byte_index; + size_t source_byte_index = 0; - // 20. If same is true, then + // 18. If same is true, then if (same) { // a. Let srcByteLength be source.[[ByteLength]]. auto source_byte_length = source.byte_length(); @@ -678,12 +676,13 @@ static ThrowCompletionOr<void> set_typed_array_from_typed_array(GlobalObject& gl // d. Let srcByteIndex be 0. source_byte_index = 0; - } else { - // 21. Else, let srcByteIndex be srcByteOffset. + } + // 19. Else, let srcByteIndex be srcByteOffset. + else { source_byte_index = source_byte_offset; } - // 22. Let targetByteIndex be targetOffset ร targetElementSize + targetByteOffset. + // 20. Let targetByteIndex be targetOffset ร targetElementSize + targetByteOffset. Checked<size_t> checked_target_byte_index(static_cast<size_t>(target_offset)); checked_target_byte_index *= target_element_size; checked_target_byte_index += target_byte_offset; @@ -691,7 +690,7 @@ static ThrowCompletionOr<void> set_typed_array_from_typed_array(GlobalObject& gl return vm.throw_completion<RangeError>(global_object, ErrorType::TypedArrayOverflow, "target byte index"); auto target_byte_index = checked_target_byte_index.value(); - // 23. Let limit be targetByteIndex + targetElementSize ร srcLength. + // 21. Let limit be targetByteIndex + targetElementSize ร srcLength. Checked<size_t> checked_limit(source_length); checked_limit *= target_element_size; checked_limit += target_byte_index; @@ -699,7 +698,7 @@ static ThrowCompletionOr<void> set_typed_array_from_typed_array(GlobalObject& gl return vm.throw_completion<RangeError>(global_object, ErrorType::TypedArrayOverflow, "target limit"); auto limit = checked_limit.value(); - // 24. If srcType is the same as targetType, then + // 22. If srcType is the same as targetType, then if (source.element_name() == target.element_name()) { // a. 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. // b. Repeat, while targetByteIndex < limit, @@ -708,7 +707,9 @@ static ThrowCompletionOr<void> set_typed_array_from_typed_array(GlobalObject& gl // iii. Set srcByteIndex to srcByteIndex + 1. // iv. Set targetByteIndex to targetByteIndex + 1. target_buffer->buffer().overwrite(target_byte_index, source_buffer->buffer().data() + source_byte_index, limit - target_byte_index); - } else { + } + // 23. Else, + else { // a. Repeat, while targetByteIndex < limit, while (target_byte_index < limit) { // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, Unordered). @@ -722,6 +723,7 @@ static ThrowCompletionOr<void> set_typed_array_from_typed_array(GlobalObject& gl } } + // 24. Return unused. return {}; } @@ -822,62 +824,113 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::set) // 23.2.3.25 %TypedArray%.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::slice) { + auto start = vm.argument(0); + auto end = vm.argument(1); + + // 1. Let O be the this value. + // 2. Perform ? ValidateTypedArray(O). auto* typed_array = TRY(validate_typed_array_from_this(global_object)); + // 3. Let len be O.[[ArrayLength]]. auto length = typed_array->array_length(); - auto relative_start = TRY(vm.argument(0).to_integer_or_infinity(global_object)); + // 4. Let relativeStart be ? ToIntegerOrInfinity(start). + auto relative_start = TRY(start.to_integer_or_infinity(global_object)); - i32 k; + i32 k = 0; + + // 5. If relativeStart is -โ, let k be 0. if (Value(relative_start).is_negative_infinity()) k = 0; + // 6. Else if relativeStart < 0, let k be max(len + relativeStart, 0). else if (relative_start < 0) k = max(length + relative_start, 0); + // 7. Else, let k be min(relativeStart, len). else k = min(relative_start, length); - double relative_end; - if (vm.argument(1).is_undefined()) + double relative_end = 0; + + // 8. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). + if (end.is_undefined()) relative_end = length; else - relative_end = TRY(vm.argument(1).to_integer_or_infinity(global_object)); + relative_end = TRY(end.to_integer_or_infinity(global_object)); + + i32 final = 0; - i32 final; + // 9. If relativeEnd is -โ, let final be 0. if (Value(relative_end).is_negative_infinity()) final = 0; + // 10. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0). else if (relative_end < 0) final = max(length + relative_end, 0); + // 11. Else, let final be min(relativeEnd, len). else final = min(relative_end, length); + // 12. Let count be max(final - k, 0). auto count = max(final - k, 0); + // 13. Let A be ? TypedArraySpeciesCreate(O, ยซ ๐ฝ(count) ยป). MarkedVector<Value> arguments(vm.heap()); arguments.empend(count); auto* new_array = TRY(typed_array_species_create(global_object, *typed_array, move(arguments))); + // 14. If count > 0, then if (count > 0) { + // a. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception. if (typed_array->viewed_array_buffer()->is_detached()) return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); + // b. Let srcType be TypedArrayElementType(O). + // c. Let targetType be TypedArrayElementType(A). + + // d. If srcType is different from targetType, then if (typed_array->element_name() != new_array->element_name()) { + // i. Let n be 0. + // ii. Repeat, while k < final, for (i32 n = 0; k < final; ++k, ++n) { + // 1. Let Pk be ! ToString(๐ฝ(k)). + // 2. Let kValue be ! Get(O, Pk). auto k_value = MUST(typed_array->get(k)); + + // 3. Perform ! Set(A, ! ToString(๐ฝ(n)), kValue, true). MUST(new_array->set(n, k_value, Object::ShouldThrowExceptions::Yes)); + + // 4. Set k to k + 1. + // 5. Set n to n + 1. } - } else { + } + // e. Else, + else { + // i. Let srcBuffer be O.[[ViewedArrayBuffer]]. + auto& source_buffer = *typed_array->viewed_array_buffer(); + + // ii. Let targetBuffer be A.[[ViewedArrayBuffer]]. + auto& target_buffer = *new_array->viewed_array_buffer(); + + // iii. Let elementSize be TypedArrayElementSize(O). auto element_size = typed_array->element_size(); + // iv. 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. + + // v. Let srcByteOffset be O.[[ByteOffset]]. + auto source_byte_offset = typed_array->byte_offset(); + + // vi. Let targetByteIndex be A.[[ByteOffset]]. + auto target_byte_index = new_array->byte_offset(); + + // vii. Let srcByteIndex be (k ร elementSize) + srcByteOffset. Checked<u32> source_byte_index = k; source_byte_index *= element_size; - source_byte_index += typed_array->byte_offset(); + source_byte_index += source_byte_offset; if (source_byte_index.has_overflow()) { dbgln("TypedArrayPrototype::slice: source_byte_index overflowed, returning as if succeeded."); return new_array; } - auto target_byte_index = new_array->byte_offset(); - + // viii. Let limit be targetByteIndex + count ร elementSize. Checked<u32> limit = count; limit *= element_size; limit += target_byte_index; @@ -886,15 +939,21 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::slice) return new_array; } - auto& source_buffer = *typed_array->viewed_array_buffer(); - auto& target_buffer = *new_array->viewed_array_buffer(); + // ix. Repeat, while targetByteIndex < limit, for (; target_byte_index < limit.value(); ++source_byte_index, ++target_byte_index) { + // 1. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, true, Unordered). auto value = source_buffer.get_value<u8>(source_byte_index.value(), true, ArrayBuffer::Unordered); + + // 2. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, value, true, Unordered). target_buffer.set_value<u8>(target_byte_index, value, true, ArrayBuffer::Unordered); + + // 3. Set srcByteIndex to srcByteIndex + 1. + // 4. Set targetByteIndex to targetByteIndex + 1. } } } + // 15. Return A. return new_array; } @@ -1017,36 +1076,61 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::sort) // 23.2.3.28 %TypedArray%.prototype.subarray ( begin, end ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::subarray) { + auto begin = vm.argument(0); + auto end = vm.argument(1); + + // 1. Let O be the this value. + // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). auto* typed_array = TRY(typed_array_from_this(global_object)); - auto length = typed_array->array_length(); + // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. + // 4. Let buffer be O.[[ViewedArrayBuffer]]. + auto* buffer = typed_array->viewed_array_buffer(); + + // 5. Let srcLength be O.[[ArrayLength]]. + auto source_length = typed_array->array_length(); - auto relative_begin = TRY(vm.argument(0).to_integer_or_infinity(global_object)); + // 6. Let relativeBegin be ? ToIntegerOrInfinity(begin). + auto relative_begin = TRY(begin.to_integer_or_infinity(global_object)); - i32 begin_index; + i32 begin_index = 0; + + // 7. If relativeBegin is -โ, let beginIndex be 0. if (Value(relative_begin).is_negative_infinity()) begin_index = 0; + // 8. Else if relativeBegin < 0, let beginIndex be max(srcLength + relativeBegin, 0). else if (relative_begin < 0) - begin_index = max(length + relative_begin, 0); + begin_index = max(source_length + relative_begin, 0); + // 9. Else, let beginIndex be min(relativeBegin, srcLength). else - begin_index = min(relative_begin, length); + begin_index = min(relative_begin, source_length); - double relative_end; - if (vm.argument(1).is_undefined()) - relative_end = length; + double relative_end = 0; + + // 10. If end is undefined, let relativeEnd be srcLength; else let relativeEnd be ? ToIntegerOrInfinity(end). + if (end.is_undefined()) + relative_end = source_length; else - relative_end = TRY(vm.argument(1).to_integer_or_infinity(global_object)); + relative_end = TRY(end.to_integer_or_infinity(global_object)); + + i32 end_index = 0; - i32 end_index; + // 11. If relativeEnd is -โ, let endIndex be 0. if (Value(relative_end).is_negative_infinity()) end_index = 0; + // 12. Else if relativeEnd < 0, let endIndex be max(srcLength + relativeEnd, 0). else if (relative_end < 0) - end_index = max(length + relative_end, 0); + end_index = max(source_length + relative_end, 0); + // 13. Else, let endIndex be min(relativeEnd, srcLength). else - end_index = min(relative_end, length); + end_index = min(relative_end, source_length); + // 14. Let newLength be max(endIndex - beginIndex, 0). auto new_length = max(end_index - begin_index, 0); + // 15. Let elementSize be TypedArrayElementSize(O). + // 16. Let srcByteOffset be O.[[ByteOffset]]. + // 17. Let beginByteOffset be srcByteOffset + beginIndex ร elementSize. Checked<u32> begin_byte_offset = begin_index; begin_byte_offset *= typed_array->element_size(); begin_byte_offset += typed_array->byte_offset(); @@ -1055,10 +1139,13 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::subarray) return typed_array; } + // 18. Let argumentsList be ยซ buffer, ๐ฝ(beginByteOffset), ๐ฝ(newLength) ยป. MarkedVector<Value> arguments(vm.heap()); - arguments.empend(typed_array->viewed_array_buffer()); + arguments.empend(buffer); arguments.empend(begin_byte_offset.value()); arguments.empend(new_length); + + // 19. Return ? TypedArraySpeciesCreate(O, argumentsList). return TRY(typed_array_species_create(global_object, *typed_array, move(arguments))); } @@ -1176,17 +1263,16 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within) if (buffer->is_detached()) return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // d. Let typedArrayName be the String value of O.[[TypedArrayName]]. - // e. Let elementSize be the Element Size value specified in Table 64 for typedArrayName. + // d. Let elementSize be TypedArrayElementSize(O). auto element_size = typed_array->element_size(); - // f. Let byteOffset be O.[[ByteOffset]]. + // e. Let byteOffset be O.[[ByteOffset]]. auto byte_offset = typed_array->byte_offset(); // FIXME: Not exactly sure what we should do when overflow occurs. // Just return as if succeeded for now. (This goes for steps g to j) - // g. Let toByteIndex be to ร elementSize + byteOffset. + // f. Let toByteIndex be to ร elementSize + byteOffset. Checked<size_t> to_byte_index_checked = static_cast<size_t>(to); to_byte_index_checked *= element_size; to_byte_index_checked += byte_offset; @@ -1195,7 +1281,7 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within) return typed_array; } - // h. Let fromByteIndex be from ร elementSize + byteOffset. + // g. Let fromByteIndex be from ร elementSize + byteOffset. Checked<size_t> from_byte_index_checked = static_cast<size_t>(from); from_byte_index_checked *= element_size; from_byte_index_checked += byte_offset; @@ -1204,7 +1290,7 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within) return typed_array; } - // i. Let countBytes be count ร elementSize. + // h. Let countBytes be count ร elementSize. Checked<size_t> count_bytes_checked = static_cast<size_t>(count); count_bytes_checked *= element_size; if (count_bytes_checked.has_overflow()) { @@ -1225,7 +1311,7 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within) i8 direction; - // j. If fromByteIndex < toByteIndex and toByteIndex < fromByteIndex + countBytes, then + // i. If fromByteIndex < toByteIndex and toByteIndex < fromByteIndex + countBytes, then if (from_byte_index < to_byte_index && to_byte_index < from_plus_count.value()) { // i. Let direction be -1. direction = -1; @@ -1242,13 +1328,14 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within) // iii. Set toByteIndex to toByteIndex + countBytes - 1. to_byte_index = to_plus_count.value() - 1; - } else { - // k. Else, + } + // j. Else, + else { // i. Let direction be 1. direction = 1; } - // l. Repeat, while countBytes > 0, + // k. Repeat, while countBytes > 0, for (; count_bytes > 0; --count_bytes) { // i. Let value be GetValueFromBuffer(buffer, fromByteIndex, Uint8, true, Unordered). auto value = buffer->get_value<u8>(from_byte_index, true, ArrayBuffer::Order::Unordered); |