diff --git a/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp b/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp index 4265778219..04cab652e3 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp @@ -11,15 +11,16 @@ namespace JS { -ThrowCompletionOr> IteratorHelper::create(Realm& realm, IteratorRecord underlying_iterator, Closure closure) +ThrowCompletionOr> IteratorHelper::create(Realm& realm, IteratorRecord underlying_iterator, Closure closure, Optional abrupt_closure) { - return TRY(realm.heap().allocate(realm, 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), move(abrupt_closure))); } -IteratorHelper::IteratorHelper(Realm& realm, Object& prototype, IteratorRecord underlying_iterator, Closure closure) +IteratorHelper::IteratorHelper(Realm& realm, Object& prototype, IteratorRecord underlying_iterator, Closure closure, Optional abrupt_closure) : GeneratorObject(realm, prototype, realm.vm().running_execution_context().copy(), "Iterator Helper"sv) , m_underlying_iterator(move(underlying_iterator)) , m_closure(move(closure)) + , m_abrupt_closure(move(abrupt_closure)) { } @@ -41,9 +42,16 @@ ThrowCompletionOr IteratorHelper::close_result(VM& vm, Completion complet return *TRY(iterator_close(vm, underlying_iterator(), move(completion))); } -ThrowCompletionOr IteratorHelper::execute(VM& vm, JS::Completion const&) +ThrowCompletionOr IteratorHelper::execute(VM& vm, JS::Completion const& completion) { ScopeGuard guard { [&] { vm.pop_execution_context(); } }; + + if (completion.is_abrupt()) { + if (m_abrupt_closure.has_value()) + return (*m_abrupt_closure)(vm, *this, completion); + return close_result(vm, completion); + } + auto result_value = m_closure(vm, *this); if (result_value.is_throw_completion()) { diff --git a/Userland/Libraries/LibJS/Runtime/IteratorHelper.h b/Userland/Libraries/LibJS/Runtime/IteratorHelper.h index 26e47f3c39..2d4ed3de1b 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorHelper.h +++ b/Userland/Libraries/LibJS/Runtime/IteratorHelper.h @@ -19,8 +19,9 @@ class IteratorHelper final : public GeneratorObject { public: using Closure = JS::SafeFunction(VM&, IteratorHelper&)>; + using AbruptClosure = JS::SafeFunction(VM&, IteratorHelper&, Completion const&)>; - static ThrowCompletionOr> create(Realm&, IteratorRecord, Closure); + static ThrowCompletionOr> create(Realm&, IteratorRecord, Closure, Optional = {}); IteratorRecord const& underlying_iterator() const { return m_underlying_iterator; } @@ -31,13 +32,14 @@ public: ThrowCompletionOr close_result(VM&, Completion); private: - IteratorHelper(Realm&, Object& prototype, IteratorRecord, Closure); + IteratorHelper(Realm&, Object& prototype, IteratorRecord, Closure, Optional); virtual void visit_edges(Visitor&) override; virtual ThrowCompletionOr execute(VM&, JS::Completion const& completion) override; IteratorRecord m_underlying_iterator; // [[UnderlyingIterator]] Closure m_closure; + Optional m_abrupt_closure; size_t m_counter { 0 }; bool m_done { false }; diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp index 2d0e2ae45b..dda30cca57 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp @@ -321,6 +321,24 @@ public: return next_outer_iterator(vm, iterated, iterator, mapper); } + // NOTE: This implements step 5.b.ix.4.d of Iterator.prototype.flatMap. + ThrowCompletionOr on_abrupt_completion(VM& vm, IteratorHelper& iterator, Completion const& completion) + { + VERIFY(m_inner_iterator.has_value()); + + // d. If completion is an abrupt completion, then + + // i. Let backupCompletion be Completion(IteratorClose(innerIterator, completion)). + auto backup_completion = iterator_close(vm, *m_inner_iterator, completion); + + // ii. IfAbruptCloseIterator(backupCompletion, iterated). + if (backup_completion.is_error()) + return iterator.close_result(vm, backup_completion.release_error()); + + // iii. Return ? IteratorClose(completion, iterated). + return iterator.close_result(vm, completion); + } + private: FlatMapIterator() = default; @@ -397,10 +415,7 @@ private: return iterator.close_result(vm, inner_value.release_error()); // c. Let completion be Completion(Yield(innerValue)). - // d. If completion is an abrupt completion, then - // i. Let backupCompletion be Completion(IteratorClose(innerIterator, completion)). - // ii. IfAbruptCloseIterator(backupCompletion, iterated). - // iii. Return ? IteratorClose(completion, iterated). + // NOTE: Step d is implemented via on_abrupt_completion. return inner_value.release_value(); } } @@ -434,9 +449,13 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::flat_map) return flat_map_iterator->next(vm, iterated, iterator, *mapper); }; + IteratorHelper::AbruptClosure abrupt_closure = [flat_map_iterator](auto& vm, auto& iterator, auto const& completion) -> ThrowCompletionOr { + return flat_map_iterator->on_abrupt_completion(vm, iterator, completion); + }; + // 6. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, « [[UnderlyingIterator]] »). // 7. Set result.[[UnderlyingIterator]] to iterated. - auto result = TRY(IteratorHelper::create(realm, move(iterated), move(closure))); + auto result = TRY(IteratorHelper::create(realm, move(iterated), move(closure), move(abrupt_closure))); // 8. Return result. return result;