diff options
author | Timothy Flynn <trflynn89@pm.me> | 2022-04-15 09:09:37 -0400 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-04-16 16:49:52 +0100 |
commit | c076b363ceec055627a9c2ab110d12bea90fce9c (patch) | |
tree | bebf8b9b00da6874a8460fe1e019fd83b0001f20 /Userland/Libraries/LibJS | |
parent | 1c80b377b2f75c462ace0f22a7230b42e223f161 (diff) | |
download | serenity-c076b363ceec055627a9c2ab110d12bea90fce9c.zip |
LibJS: Define SetTypedArrayFrom{TypedArray,ArrayLike} AOs out of line
%TypedArray%.prototype.set was a bit hard to read / compare to the spec
with these AOs defined inside it.
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp | 321 |
1 files changed, 167 insertions, 154 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp index c4808d2835..79e5f392f3 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp @@ -604,202 +604,215 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::to_string_tag_getter) return js_string(vm, static_cast<TypedArrayBase&>(this_object).element_name()); } -// 23.2.3.24 %TypedArray%.prototype.set ( source [ , offset ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.set -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::set) +// 23.2.3.23.1 SetTypedArrayFromTypedArray ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray +static ThrowCompletionOr<void> set_typed_array_from_typed_array(GlobalObject& global_object, TypedArrayBase& target, double target_offset, TypedArrayBase& source) { - auto source = vm.argument(0); + auto& vm = global_object.vm(); - // 1. Let target be the this value. - // 2. Perform ? RequireInternalSlot(target, [[TypedArrayName]]). - auto* typed_array = TRY(typed_array_from_this(global_object)); + // 1. Let targetBuffer be target.[[ViewedArrayBuffer]]. + auto* target_buffer = target.viewed_array_buffer(); - // 3. Assert: target has a [[ViewedArrayBuffer]] internal slot. + // 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + if (target_buffer->is_detached()) + return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // 4. Let targetOffset be ? ToIntegerOrInfinity(offset). - auto target_offset = TRY(vm.argument(1).to_integer_or_infinity(global_object)); + // 3. Let targetLength be target.[[ArrayLength]]. + auto target_length = target.array_length(); - // 5. If targetOffset < 0, throw a RangeError exception. - if (target_offset < 0) - return vm.throw_completion<RangeError>(global_object, "Invalid target offset"); + // 4. Let srcBuffer be source.[[ViewedArrayBuffer]]. + auto* source_buffer = source.viewed_array_buffer(); - // 6. If source is an Object that has a [[TypedArrayName]] internal slot, then - if (source.is_object() && is<TypedArrayBase>(source.as_object())) { - // a. Perform ? SetTypedArrayFromTypedArray(target, targetOffset, source). + // 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. + if (source_buffer->is_detached()) + return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // 23.2.3.23.1 SetTypedArrayFromTypedArray ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray + // 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. + auto target_element_size = target.element_size(); - auto& source_typed_array = static_cast<TypedArrayBase&>(source.as_object()); + // 9. Let targetByteOffset be target.[[ByteOffset]]. + auto target_byte_offset = target.byte_offset(); - // 1. Let targetBuffer be target.[[ViewedArrayBuffer]]. - auto* target_buffer = typed_array->viewed_array_buffer(); + // 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. + auto source_element_size = source.element_size(); - // 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. - if (target_buffer->is_detached()) - return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); + // 13. Let srcLength be source.[[ArrayLength]]. + auto source_length = source.array_length(); - // 3. Let targetLength be target.[[ArrayLength]]. - auto target_length = typed_array->array_length(); + // 14. Let srcByteOffset be source.[[ByteOffset]]. + auto source_byte_offset = source.byte_offset(); - // 4. Let srcBuffer be source.[[ViewedArrayBuffer]]. - auto* source_buffer = source_typed_array.viewed_array_buffer(); + // 15. If targetOffset is +∞, throw a RangeError exception. + if (isinf(target_offset)) + return vm.throw_completion<RangeError>(global_object, "Invalid target offset"); - // 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception. - if (source_buffer->is_detached()) - return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); + // 16. 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, "Overflow or out of bounds in target length"); - // 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. - auto target_element_size = typed_array->element_size(); + // 17. If target.[[ContentType]] ≠ source.[[ContentType]], throw a TypeError exception. + if (target.content_type() != source.content_type()) + return vm.throw_completion<TypeError>(global_object, "Copy between arrays of different content types is prohibited"); - // 9. Let targetByteOffset be target.[[ByteOffset]]. - auto target_byte_offset = typed_array->byte_offset(); + // FIXME: 18. 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. - // 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. - auto source_element_size = source_typed_array.element_size(); + // 19. Else, let same be SameValue(srcBuffer, targetBuffer). + auto same = same_value(source_buffer, target_buffer); - // 13. Let srcLength be source.[[ArrayLength]]. - auto source_length = source_typed_array.array_length(); + size_t source_byte_index; - // 14. Let srcByteOffset be source.[[ByteOffset]]. - auto source_byte_offset = source_typed_array.byte_offset(); + // 20. If same is true, then + if (same) { + // a. Let srcByteLength be source.[[ByteLength]]. + auto source_byte_length = source.byte_length(); - // 15. If targetOffset is +∞, throw a RangeError exception. - if (isinf(target_offset)) - return vm.throw_completion<RangeError>(global_object, "Invalid target offset"); + // b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%). + source_buffer = TRY(clone_array_buffer(global_object, *source_buffer, source_byte_offset, source_byte_length, *global_object.array_buffer_constructor())); + // c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects. - // 16. 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, "Overflow or out of bounds in target length"); + // d. Let srcByteIndex be 0. + source_byte_index = 0; + } else { + // 21. Else, let srcByteIndex be srcByteOffset. + source_byte_index = source_byte_offset; + } - // 17. If target.[[ContentType]] ≠ source.[[ContentType]], throw a TypeError exception. - if (typed_array->content_type() != source_typed_array.content_type()) - return vm.throw_completion<TypeError>(global_object, "Copy between arrays of different content types is prohibited"); + // 22. 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; + if (checked_target_byte_index.has_overflow()) + return vm.throw_completion<RangeError>(global_object, "Overflow in target byte index"); + auto target_byte_index = checked_target_byte_index.value(); + + // 23. Let limit be targetByteIndex + targetElementSize × srcLength. + Checked<size_t> checked_limit(source_length); + checked_limit *= target_element_size; + checked_limit += target_byte_index; + if (checked_limit.has_overflow()) + return vm.throw_completion<RangeError>(global_object, "Overflow in target limit"); + auto limit = checked_limit.value(); + + // 24. 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, + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, true, Unordered). + // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, value, true, Unordered). + // 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 { + // a. Repeat, while targetByteIndex < limit, + while (target_byte_index < limit) { + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, Unordered). + auto value = source.get_value_from_buffer(source_byte_index, ArrayBuffer::Unordered); + // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered). + target.set_value_in_buffer(target_byte_index, value, ArrayBuffer::Unordered); + // iii. Set srcByteIndex to srcByteIndex + srcElementSize. + source_byte_index += source_element_size; + // iv. Set targetByteIndex to targetByteIndex + targetElementSize. + target_byte_index += target.element_size(); + } + } - // FIXME: 18. 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. + return {}; +} - // 19. Else, let same be SameValue(srcBuffer, targetBuffer). - auto same = same_value(source_buffer, target_buffer); +// 23.2.3.23.2 SetTypedArrayFromArrayLike ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromarraylike +static ThrowCompletionOr<void> set_typed_array_from_array_like(GlobalObject& global_object, TypedArrayBase& target, double target_offset, Value source) +{ + auto& vm = global_object.vm(); - size_t source_byte_index; + // 1. Let targetBuffer be target.[[ViewedArrayBuffer]]. + auto* target_buffer = target.viewed_array_buffer(); - // 20. If same is true, then - if (same) { - // a. Let srcByteLength be source.[[ByteLength]]. - auto source_byte_length = source_typed_array.byte_length(); + // 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. + if (target_buffer->is_detached()) + return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); - // b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%). - source_buffer = TRY(clone_array_buffer(global_object, *source_buffer, source_byte_offset, source_byte_length, *global_object.array_buffer_constructor())); - // c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects. + // 3. Let targetLength be target.[[ArrayLength]]. + auto target_length = target.array_length(); - // d. Let srcByteIndex be 0. - source_byte_index = 0; - } else { - // 21. Else, let srcByteIndex be srcByteOffset. - source_byte_index = source_byte_offset; - } + // 4. Let src be ? ToObject(source). + auto* src = TRY(source.to_object(global_object)); - // 22. 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; - if (checked_target_byte_index.has_overflow()) - return vm.throw_completion<RangeError>(global_object, "Overflow in target byte index"); - auto target_byte_index = checked_target_byte_index.value(); - - // 23. Let limit be targetByteIndex + targetElementSize × srcLength. - Checked<size_t> checked_limit(source_length); - checked_limit *= target_element_size; - checked_limit += target_byte_index; - if (checked_limit.has_overflow()) - return vm.throw_completion<RangeError>(global_object, "Overflow in target limit"); - auto limit = checked_limit.value(); - - // 24. If srcType is the same as targetType, then - if (source_typed_array.element_name() == typed_array->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, - // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, true, Unordered). - // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, value, true, Unordered). - // 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 { - // a. Repeat, while targetByteIndex < limit, - while (target_byte_index < limit) { - // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, Unordered). - auto value = source_typed_array.get_value_from_buffer(source_byte_index, ArrayBuffer::Unordered); - // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered). - typed_array->set_value_in_buffer(target_byte_index, value, ArrayBuffer::Unordered); - // iii. Set srcByteIndex to srcByteIndex + srcElementSize. - source_byte_index += source_element_size; - // iv. Set targetByteIndex to targetByteIndex + targetElementSize. - target_byte_index += typed_array->element_size(); - } - } - } - // 7. Else, - else { - // a. Perform ? SetTypedArrayFromArrayLike(target, targetOffset, source). + // 5. Let srcLength be ? LengthOfArrayLike(src). + auto source_length = TRY(length_of_array_like(global_object, *src)); - // 23.2.3.23.2 SetTypedArrayFromArrayLike ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromarraylike + // 6. If targetOffset is +∞, throw a RangeError exception. + if (isinf(target_offset)) + return vm.throw_completion<RangeError>(global_object, "Invalid target offset"); - // 1. Let targetBuffer be target.[[ViewedArrayBuffer]]. - auto* target_buffer = typed_array->viewed_array_buffer(); + // 7. 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, "Overflow or out of bounds in target length"); - // 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception. - if (target_buffer->is_detached()) - return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer); + // 8. Let k be 0. + size_t k = 0; - // 3. Let targetLength be target.[[ArrayLength]]. - auto target_length = typed_array->array_length(); + // 9. Repeat, while k < srcLength, + while (k < source_length) { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let value be ? Get(src, Pk). + auto value = TRY(src->get(k)); - // 4. Let src be ? ToObject(source). - auto* src = TRY(source.to_object(global_object)); + // c. Let targetIndex be 𝔽(targetOffset + k). + CanonicalIndex target_index(CanonicalIndex::Type::Index, target_offset + k); - // 5. Let srcLength be ? LengthOfArrayLike(src). - auto source_length = TRY(length_of_array_like(global_object, *src)); + // d. Perform ? IntegerIndexedElementSet(target, targetIndex, value). + // FIXME: This is very awkward. +#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \ + if (is<ClassName>(target)) \ + TRY(integer_indexed_element_set<Type>(target, target_index, value)); + JS_ENUMERATE_TYPED_ARRAYS +#undef __JS_ENUMERATE - // 6. If targetOffset is +∞, throw a RangeError exception. - if (isinf(target_offset)) - return vm.throw_completion<RangeError>(global_object, "Invalid target offset"); + // e. Set k to k + 1. + ++k; + } - // 7. 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, "Overflow or out of bounds in target length"); + // 10. Return unused. + return {}; +} - // 8. Let k be 0. - size_t k = 0; +// 23.2.3.24 %TypedArray%.prototype.set ( source [ , offset ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.set +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::set) +{ + auto source = vm.argument(0); + + // 1. Let target be the this value. + // 2. Perform ? RequireInternalSlot(target, [[TypedArrayName]]). + auto* typed_array = TRY(typed_array_from_this(global_object)); - // 9. Repeat, while k < srcLength, - while (k < source_length) { - // a. Let Pk be ! ToString(𝔽(k)). - // b. Let value be ? Get(src, Pk). - auto value = TRY(src->get(k)); + // 3. Assert: target has a [[ViewedArrayBuffer]] internal slot. - // c. Let targetIndex be 𝔽(targetOffset + k). - CanonicalIndex target_index(CanonicalIndex::Type::Index, target_offset + k); + // 4. Let targetOffset be ? ToIntegerOrInfinity(offset). + auto target_offset = TRY(vm.argument(1).to_integer_or_infinity(global_object)); - // d. Perform ? IntegerIndexedElementSet(target, targetIndex, value). - // FIXME: This is very awkward. -#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \ - if (is<ClassName>(typed_array)) \ - TRY(integer_indexed_element_set<Type>(*typed_array, target_index, value)); - JS_ENUMERATE_TYPED_ARRAYS -#undef __JS_ENUMERATE + // 5. If targetOffset < 0, throw a RangeError exception. + if (target_offset < 0) + return vm.throw_completion<RangeError>(global_object, "Invalid target offset"); - // e. Set k to k + 1. - ++k; - } + // 6. If source is an Object that has a [[TypedArrayName]] internal slot, then + if (source.is_object() && is<TypedArrayBase>(source.as_object())) { + auto& source_typed_array = static_cast<TypedArrayBase&>(source.as_object()); - // 10. Return unused. + // a. Perform ? SetTypedArrayFromTypedArray(target, targetOffset, source). + TRY(set_typed_array_from_typed_array(global_object, *typed_array, target_offset, source_typed_array)); + } + // 7. Else, + else { + // a. Perform ? SetTypedArrayFromArrayLike(target, targetOffset, source). + TRY(set_typed_array_from_array_like(global_object, *typed_array, target_offset, source)); } // 8. Return undefined. |