Prechádzať zdrojové kódy

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.
Timothy Flynn 3 rokov pred
rodič
commit
c076b363ce

+ 167 - 154
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.