diff --git a/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp b/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp index 6a63ed850f..4265778219 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp @@ -13,11 +13,11 @@ namespace JS { ThrowCompletionOr> IteratorHelper::create(Realm& realm, IteratorRecord underlying_iterator, Closure closure) { - return TRY(realm.heap().allocate(realm, realm.intrinsics().iterator_helper_prototype(), move(underlying_iterator), move(closure))); + return TRY(realm.heap().allocate(realm, realm, realm.intrinsics().iterator_helper_prototype(), move(underlying_iterator), move(closure))); } -IteratorHelper::IteratorHelper(Object& prototype, IteratorRecord underlying_iterator, Closure closure) - : Object(ConstructWithPrototypeTag::Tag, prototype) +IteratorHelper::IteratorHelper(Realm& realm, Object& prototype, IteratorRecord underlying_iterator, Closure closure) + : GeneratorObject(realm, prototype, realm.vm().running_execution_context().copy(), "Iterator Helper"sv) , m_underlying_iterator(move(underlying_iterator)) , m_closure(move(closure)) { @@ -31,15 +31,27 @@ void IteratorHelper::visit_edges(Visitor& visitor) Value IteratorHelper::result(Value value) { - if (value.is_undefined()) - m_done = true; + set_generator_state(value.is_undefined() ? GeneratorState::Completed : GeneratorState::SuspendedYield); return value; } -ThrowCompletionOr IteratorHelper::close_result(Completion completion) +ThrowCompletionOr IteratorHelper::close_result(VM& vm, Completion completion) { - m_done = true; - return *TRY(iterator_close(vm(), underlying_iterator(), move(completion))); + set_generator_state(GeneratorState::Completed); + return *TRY(iterator_close(vm, underlying_iterator(), move(completion))); +} + +ThrowCompletionOr IteratorHelper::execute(VM& vm, JS::Completion const&) +{ + ScopeGuard guard { [&] { vm.pop_execution_context(); } }; + auto result_value = m_closure(vm, *this); + + if (result_value.is_throw_completion()) { + set_generator_state(GeneratorState::Completed); + return result_value; + } + + return create_iterator_result_object(vm, result(result_value.release_value()), generator_state() == GeneratorState::Completed); } } diff --git a/Userland/Libraries/LibJS/Runtime/IteratorHelper.h b/Userland/Libraries/LibJS/Runtime/IteratorHelper.h index 01bf227966..26e47f3c39 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorHelper.h +++ b/Userland/Libraries/LibJS/Runtime/IteratorHelper.h @@ -7,35 +7,34 @@ #pragma once #include +#include #include #include #include namespace JS { -class IteratorHelper final : public Object { - JS_OBJECT(IteratorHelper, Object); +class IteratorHelper final : public GeneratorObject { + JS_OBJECT(IteratorHelper, GeneratorObject); public: - using Closure = JS::SafeFunction(IteratorHelper&)>; + using Closure = JS::SafeFunction(VM&, IteratorHelper&)>; static ThrowCompletionOr> create(Realm&, IteratorRecord, Closure); IteratorRecord const& underlying_iterator() const { return m_underlying_iterator; } - Closure& closure() { return m_closure; } size_t counter() const { return m_counter; } void increment_counter() { ++m_counter; } Value result(Value); - ThrowCompletionOr close_result(Completion); - - bool done() const { return m_done; } + ThrowCompletionOr close_result(VM&, Completion); private: - IteratorHelper(Object& prototype, IteratorRecord, Closure); + IteratorHelper(Realm&, Object& prototype, IteratorRecord, Closure); virtual void visit_edges(Visitor&) override; + virtual ThrowCompletionOr execute(VM&, JS::Completion const& completion) override; IteratorRecord m_underlying_iterator; // [[UnderlyingIterator]] Closure m_closure; diff --git a/Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp b/Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp index 48933a3568..bc2edae1ab 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp @@ -34,12 +34,9 @@ ThrowCompletionOr IteratorHelperPrototype::initialize(Realm& realm) JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::next) { auto iterator = TRY(typed_this_object(vm)); - if (iterator->done()) - return create_iterator_result_object(vm, js_undefined(), true); // 1. Return ? GeneratorResume(this value, undefined, "Iterator Helper"). - auto result = TRY(iterator->closure()(*iterator)); - return create_iterator_result_object(vm, result, iterator->done()); + return iterator->resume(vm, js_undefined(), "Iterator Helper"sv); } // 3.1.2.1.2 %IteratorHelperPrototype%.return ( ), https://tc39.es/proposal-iterator-helpers/#sec-%iteratorhelperprototype%.return diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp index 9cf0215e38..2d0e2ae45b 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp @@ -72,9 +72,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::map) auto iterated = TRY(get_iterator_direct(vm, object)); // 5. Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called: - IteratorHelper::Closure closure = [mapper = NonnullGCPtr { mapper.as_function() }](auto& iterator) -> ThrowCompletionOr { - auto& vm = iterator.vm(); - + IteratorHelper::Closure closure = [mapper = NonnullGCPtr { mapper.as_function() }](auto& vm, auto& iterator) -> ThrowCompletionOr { auto const& iterated = iterator.underlying_iterator(); // a. Let counter be 0. @@ -95,7 +93,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::map) // v. IfAbruptCloseIterator(mapped, iterated). if (mapped.is_error()) - return iterator.close_result(mapped.release_error()); + return iterator.close_result(vm, mapped.release_error()); // viii. Set counter to counter + 1. // NOTE: We do this step early to ensure it occurs before returning. @@ -133,9 +131,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::filter) auto iterated = TRY(get_iterator_direct(vm, object)); // 5. Let closure be a new Abstract Closure with no parameters that captures iterated and predicate and performs the following steps when called: - IteratorHelper::Closure closure = [predicate = NonnullGCPtr { predicate.as_function() }](auto& iterator) -> ThrowCompletionOr { - auto& vm = iterator.vm(); - + IteratorHelper::Closure closure = [predicate = NonnullGCPtr { predicate.as_function() }](auto& vm, auto& iterator) -> ThrowCompletionOr { auto const& iterated = iterator.underlying_iterator(); // a. Let counter be 0. @@ -157,7 +153,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::filter) // v. IfAbruptCloseIterator(selected, iterated). if (selected.is_error()) - return iterator.close_result(selected.release_error()); + return iterator.close_result(vm, selected.release_error()); // vii. Set counter to counter + 1. // NOTE: We do this step early to ensure it occurs before returning. @@ -209,9 +205,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::take) auto iterated = TRY(get_iterator_direct(vm, object)); // 8. Let closure be a new Abstract Closure with no parameters that captures iterated and integerLimit and performs the following steps when called: - IteratorHelper::Closure closure = [integer_limit](auto& iterator) -> ThrowCompletionOr { - auto& vm = iterator.vm(); - + IteratorHelper::Closure closure = [integer_limit](auto& vm, auto& iterator) -> ThrowCompletionOr { auto const& iterated = iterator.underlying_iterator(); // a. Let remaining be integerLimit. @@ -220,7 +214,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::take) // i. If remaining is 0, then if (iterator.counter() >= integer_limit) { // 1. Return ? IteratorClose(iterated, NormalCompletion(undefined)). - return iterator.close_result(normal_completion(js_undefined())); + return iterator.close_result(vm, normal_completion(js_undefined())); } // ii. If remaining is not +∞, then @@ -276,9 +270,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::drop) auto iterated = TRY(get_iterator_direct(vm, object)); // 8. Let closure be a new Abstract Closure with no parameters that captures iterated and integerLimit and performs the following steps when called: - IteratorHelper::Closure closure = [integer_limit](auto& iterator) -> ThrowCompletionOr { - auto& vm = iterator.vm(); - + IteratorHelper::Closure closure = [integer_limit](auto& vm, auto& iterator) -> ThrowCompletionOr { auto const& iterated = iterator.underlying_iterator(); // a. Let remaining be integerLimit. @@ -357,14 +349,14 @@ private: // v. IfAbruptCloseIterator(mapped, iterated). if (mapped.is_error()) - return iterator.close_result(mapped.release_error()); + return iterator.close_result(vm, mapped.release_error()); // vi. Let innerIterator be Completion(GetIteratorFlattenable(mapped, reject-strings)). auto inner_iterator = get_iterator_flattenable(vm, mapped.release_value(), StringHandling::RejectStrings); // vii. IfAbruptCloseIterator(innerIterator, iterated). if (inner_iterator.is_error()) - return iterator.close_result(inner_iterator.release_error()); + return iterator.close_result(vm, inner_iterator.release_error()); // viii. Let innerAlive be true. m_inner_iterator = inner_iterator.release_value(); @@ -386,7 +378,7 @@ private: // 2. IfAbruptCloseIterator(innerNext, iterated). if (inner_next.is_error()) - return iterator.close_result(inner_next.release_error()); + return iterator.close_result(vm, inner_next.release_error()); // 3. If innerNext is false, then if (!inner_next.value()) { @@ -402,7 +394,7 @@ private: // b. IfAbruptCloseIterator(innerValue, iterated). if (inner_value.is_error()) - return iterator.close_result(inner_value.release_error()); + return iterator.close_result(vm, inner_value.release_error()); // c. Let completion be Completion(Yield(innerValue)). // d. If completion is an abrupt completion, then @@ -437,9 +429,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::flat_map) auto flat_map_iterator = MUST_OR_THROW_OOM(vm.heap().allocate(realm)); // 5. Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called: - IteratorHelper::Closure closure = [flat_map_iterator, mapper = NonnullGCPtr { mapper.as_function() }](auto& iterator) mutable -> ThrowCompletionOr { - auto& vm = iterator.vm(); - + IteratorHelper::Closure closure = [flat_map_iterator, mapper = NonnullGCPtr { mapper.as_function() }](auto& vm, auto& iterator) mutable -> ThrowCompletionOr { auto const& iterated = iterator.underlying_iterator(); return flat_map_iterator->next(vm, iterated, iterator, *mapper); };