Browse Source

LibJS: Convert IteratorHelper to be a GeneratorObject

This is required for %IteratorHelperPrototype%.return, which needs to
operate on the internal slots of the base GeneratorObject. Doing so also
provides us with the appropriate VM to be used in invocations to the
overridden IteratorHelper::execute.
Timothy Flynn 2 years ago
parent
commit
c04476f09d

+ 20 - 8
Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp

@@ -13,11 +13,11 @@ namespace JS {
 
 ThrowCompletionOr<NonnullGCPtr<IteratorHelper>> IteratorHelper::create(Realm& realm, IteratorRecord underlying_iterator, Closure closure)
 {
-    return TRY(realm.heap().allocate<IteratorHelper>(realm, realm.intrinsics().iterator_helper_prototype(), move(underlying_iterator), move(closure)));
+    return TRY(realm.heap().allocate<IteratorHelper>(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<Value> IteratorHelper::close_result(Completion completion)
+ThrowCompletionOr<Value> 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<Value> 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);
 }
 
 }

+ 7 - 8
Userland/Libraries/LibJS/Runtime/IteratorHelper.h

@@ -7,35 +7,34 @@
 #pragma once
 
 #include <LibJS/Runtime/Completion.h>
+#include <LibJS/Runtime/GeneratorObject.h>
 #include <LibJS/Runtime/Iterator.h>
 #include <LibJS/Runtime/Object.h>
 #include <LibJS/SafeFunction.h>
 
 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<ThrowCompletionOr<Value>(IteratorHelper&)>;
+    using Closure = JS::SafeFunction<ThrowCompletionOr<Value>(VM&, IteratorHelper&)>;
 
     static ThrowCompletionOr<NonnullGCPtr<IteratorHelper>> 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<Value> close_result(Completion);
-
-    bool done() const { return m_done; }
+    ThrowCompletionOr<Value> close_result(VM&, Completion);
 
 private:
-    IteratorHelper(Object& prototype, IteratorRecord, Closure);
+    IteratorHelper(Realm&, Object& prototype, IteratorRecord, Closure);
 
     virtual void visit_edges(Visitor&) override;
+    virtual ThrowCompletionOr<Value> execute(VM&, JS::Completion const& completion) override;
 
     IteratorRecord m_underlying_iterator; // [[UnderlyingIterator]]
     Closure m_closure;

+ 1 - 4
Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp

@@ -34,12 +34,9 @@ ThrowCompletionOr<void> 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

+ 12 - 22
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<Value> {
-        auto& vm = iterator.vm();
-
+    IteratorHelper::Closure closure = [mapper = NonnullGCPtr { mapper.as_function() }](auto& vm, auto& iterator) -> ThrowCompletionOr<Value> {
         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<Value> {
-        auto& vm = iterator.vm();
-
+    IteratorHelper::Closure closure = [predicate = NonnullGCPtr { predicate.as_function() }](auto& vm, auto& iterator) -> ThrowCompletionOr<Value> {
         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<Value> {
-        auto& vm = iterator.vm();
-
+    IteratorHelper::Closure closure = [integer_limit](auto& vm, auto& iterator) -> ThrowCompletionOr<Value> {
         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<Value> {
-        auto& vm = iterator.vm();
-
+    IteratorHelper::Closure closure = [integer_limit](auto& vm, auto& iterator) -> ThrowCompletionOr<Value> {
         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<FlatMapIterator>(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<Value> {
-        auto& vm = iterator.vm();
-
+    IteratorHelper::Closure closure = [flat_map_iterator, mapper = NonnullGCPtr { mapper.as_function() }](auto& vm, auto& iterator) mutable -> ThrowCompletionOr<Value> {
         auto const& iterated = iterator.underlying_iterator();
         return flat_map_iterator->next(vm, iterated, iterator, *mapper);
     };