From e3c634fdd05b5eefc41d7952adffa0bdb41099fa Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Fri, 16 Apr 2021 23:54:41 +0300 Subject: [PATCH] LibJS: Implement initializing a TypedArray from an iterable object Based on these specifications (which required IterableToList as well): https://tc39.es/ecma262/#sec-typedarray https://tc39.es/ecma262/#sec-initializetypedarrayfromlist --- .../LibJS/Runtime/IteratorOperations.cpp | 19 +++++++++-- .../LibJS/Runtime/IteratorOperations.h | 4 ++- .../Libraries/LibJS/Runtime/TypedArray.cpp | 32 +++++++++++++++++-- .../Tests/builtins/TypedArray/TypedArray.js | 11 +++++++ 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp b/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp index 6dd9259203..0b80ab0625 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp @@ -92,6 +92,21 @@ void iterator_close([[maybe_unused]] Object& iterator) TODO(); } +MarkedValueList iterable_to_list(GlobalObject& global_object, Value iterable, Value method) +{ + auto& vm = global_object.vm(); + MarkedValueList values(vm.heap()); + get_iterator_values( + global_object, iterable, [&](auto value) { + if (vm.exception()) + return IterationDecision::Break; + values.append(value); + return IterationDecision::Continue; + }, + method); + return values; +} + Value create_iterator_result_object(GlobalObject& global_object, Value value, bool done) { auto& vm = global_object.vm(); @@ -101,11 +116,11 @@ Value create_iterator_result_object(GlobalObject& global_object, Value value, bo return object; } -void get_iterator_values(GlobalObject& global_object, Value value, AK::Function callback) +void get_iterator_values(GlobalObject& global_object, Value value, AK::Function callback, Value method) { auto& vm = global_object.vm(); - auto iterator = get_iterator(global_object, value); + auto iterator = get_iterator(global_object, value, "sync", method); if (!iterator) return; diff --git a/Userland/Libraries/LibJS/Runtime/IteratorOperations.h b/Userland/Libraries/LibJS/Runtime/IteratorOperations.h index db113eec97..fe586e23e7 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorOperations.h +++ b/Userland/Libraries/LibJS/Runtime/IteratorOperations.h @@ -41,6 +41,8 @@ Value create_iterator_result_object(GlobalObject&, Value value, bool done); Object* iterator_next(Object& iterator, Value value = {}); void iterator_close(Object& iterator); -void get_iterator_values(GlobalObject&, Value value, AK::Function callback); +MarkedValueList iterable_to_list(GlobalObject&, Value iterable, Value method = {}); + +void get_iterator_values(GlobalObject&, Value value, AK::Function callback, Value method = {}); } diff --git a/Userland/Libraries/LibJS/Runtime/TypedArray.cpp b/Userland/Libraries/LibJS/Runtime/TypedArray.cpp index 12fe9905d8..b79fa97ea7 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArray.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArray.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -166,6 +167,31 @@ static void initialize_typed_array_from_array_like(GlobalObject& global_object, return; } } +template +static void initialize_typed_array_from_list(GlobalObject& global_object, TypedArray& typed_array, const MarkedValueList& list) +{ + // 23.2.5.1.4 InitializeTypedArrayFromList, https://tc39.es/ecma262/#sec-initializetypedarrayfromlist + + auto element_size = typed_array.element_size(); + if (Checked::multiplication_would_overflow(element_size, list.size())) { + global_object.vm().throw_exception(global_object, ErrorType::InvalidLength, "typed array"); + return; + } + auto byte_length = element_size * list.size(); + auto array_buffer = ArrayBuffer::create(global_object, byte_length); + typed_array.set_viewed_array_buffer(array_buffer); + typed_array.set_byte_length(byte_length); + typed_array.set_byte_offset(0); + typed_array.set_array_length(list.size()); + + auto& vm = global_object.vm(); + for (size_t k = 0; k < list.size(); k++) { + auto value = list[k]; + typed_array.put_by_index(k, value); + if (vm.exception()) + return; + } +} void TypedArrayBase::visit_edges(Visitor& visitor) { @@ -234,8 +260,10 @@ void TypedArrayBase::visit_edges(Visitor& visitor) if (vm.exception()) \ return {}; \ if (iterator.is_function()) { \ - /* FIXME: Initialize from Iterator */ \ - TODO(); \ + auto values = iterable_to_list(global_object(), first_argument, iterator); \ + if (vm.exception()) \ + return {}; \ + initialize_typed_array_from_list(global_object(), *typed_array, values); \ } else { \ initialize_typed_array_from_array_like(global_object(), *typed_array, first_argument.as_object()); \ } \ diff --git a/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.js b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.js index da000762ac..7e3c97f290 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.js +++ b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.js @@ -195,6 +195,17 @@ test("typed array from Array-Like", () => { }); }); +test("typed array from Iterable", () => { + const from = new String("123"); + + TYPED_ARRAYS.forEach(T => { + const newTypedArray = new T(from); + expect(newTypedArray[0]).toBe(1); + expect(newTypedArray[1]).toBe(2); + expect(newTypedArray[2]).toBe(3); + }); +}); + test("TypedArray is not exposed on the global object", () => { expect(globalThis.TypedArray).toBeUndefined(); });