Skip to content

Commit c04476f

Browse files
trflynn89linusg
authored andcommitted
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.
1 parent 82df3ce commit c04476f

File tree

4 files changed

+40
-42
lines changed

4 files changed

+40
-42
lines changed

Userland/Libraries/LibJS/Runtime/IteratorHelper.cpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ namespace JS {
1313

1414
ThrowCompletionOr<NonnullGCPtr<IteratorHelper>> IteratorHelper::create(Realm& realm, IteratorRecord underlying_iterator, Closure closure)
1515
{
16-
return TRY(realm.heap().allocate<IteratorHelper>(realm, realm.intrinsics().iterator_helper_prototype(), move(underlying_iterator), move(closure)));
16+
return TRY(realm.heap().allocate<IteratorHelper>(realm, realm, realm.intrinsics().iterator_helper_prototype(), move(underlying_iterator), move(closure)));
1717
}
1818

19-
IteratorHelper::IteratorHelper(Object& prototype, IteratorRecord underlying_iterator, Closure closure)
20-
: Object(ConstructWithPrototypeTag::Tag, prototype)
19+
IteratorHelper::IteratorHelper(Realm& realm, Object& prototype, IteratorRecord underlying_iterator, Closure closure)
20+
: GeneratorObject(realm, prototype, realm.vm().running_execution_context().copy(), "Iterator Helper"sv)
2121
, m_underlying_iterator(move(underlying_iterator))
2222
, m_closure(move(closure))
2323
{
@@ -31,15 +31,27 @@ void IteratorHelper::visit_edges(Visitor& visitor)
3131

3232
Value IteratorHelper::result(Value value)
3333
{
34-
if (value.is_undefined())
35-
m_done = true;
34+
set_generator_state(value.is_undefined() ? GeneratorState::Completed : GeneratorState::SuspendedYield);
3635
return value;
3736
}
3837

39-
ThrowCompletionOr<Value> IteratorHelper::close_result(Completion completion)
38+
ThrowCompletionOr<Value> IteratorHelper::close_result(VM& vm, Completion completion)
4039
{
41-
m_done = true;
42-
return *TRY(iterator_close(vm(), underlying_iterator(), move(completion)));
40+
set_generator_state(GeneratorState::Completed);
41+
return *TRY(iterator_close(vm, underlying_iterator(), move(completion)));
42+
}
43+
44+
ThrowCompletionOr<Value> IteratorHelper::execute(VM& vm, JS::Completion const&)
45+
{
46+
ScopeGuard guard { [&] { vm.pop_execution_context(); } };
47+
auto result_value = m_closure(vm, *this);
48+
49+
if (result_value.is_throw_completion()) {
50+
set_generator_state(GeneratorState::Completed);
51+
return result_value;
52+
}
53+
54+
return create_iterator_result_object(vm, result(result_value.release_value()), generator_state() == GeneratorState::Completed);
4355
}
4456

4557
}

Userland/Libraries/LibJS/Runtime/IteratorHelper.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,34 @@
77
#pragma once
88

99
#include <LibJS/Runtime/Completion.h>
10+
#include <LibJS/Runtime/GeneratorObject.h>
1011
#include <LibJS/Runtime/Iterator.h>
1112
#include <LibJS/Runtime/Object.h>
1213
#include <LibJS/SafeFunction.h>
1314

1415
namespace JS {
1516

16-
class IteratorHelper final : public Object {
17-
JS_OBJECT(IteratorHelper, Object);
17+
class IteratorHelper final : public GeneratorObject {
18+
JS_OBJECT(IteratorHelper, GeneratorObject);
1819

1920
public:
20-
using Closure = JS::SafeFunction<ThrowCompletionOr<Value>(IteratorHelper&)>;
21+
using Closure = JS::SafeFunction<ThrowCompletionOr<Value>(VM&, IteratorHelper&)>;
2122

2223
static ThrowCompletionOr<NonnullGCPtr<IteratorHelper>> create(Realm&, IteratorRecord, Closure);
2324

2425
IteratorRecord const& underlying_iterator() const { return m_underlying_iterator; }
25-
Closure& closure() { return m_closure; }
2626

2727
size_t counter() const { return m_counter; }
2828
void increment_counter() { ++m_counter; }
2929

3030
Value result(Value);
31-
ThrowCompletionOr<Value> close_result(Completion);
32-
33-
bool done() const { return m_done; }
31+
ThrowCompletionOr<Value> close_result(VM&, Completion);
3432

3533
private:
36-
IteratorHelper(Object& prototype, IteratorRecord, Closure);
34+
IteratorHelper(Realm&, Object& prototype, IteratorRecord, Closure);
3735

3836
virtual void visit_edges(Visitor&) override;
37+
virtual ThrowCompletionOr<Value> execute(VM&, JS::Completion const& completion) override;
3938

4039
IteratorRecord m_underlying_iterator; // [[UnderlyingIterator]]
4140
Closure m_closure;

Userland/Libraries/LibJS/Runtime/IteratorHelperPrototype.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,9 @@ ThrowCompletionOr<void> IteratorHelperPrototype::initialize(Realm& realm)
3434
JS_DEFINE_NATIVE_FUNCTION(IteratorHelperPrototype::next)
3535
{
3636
auto iterator = TRY(typed_this_object(vm));
37-
if (iterator->done())
38-
return create_iterator_result_object(vm, js_undefined(), true);
3937

4038
// 1. Return ? GeneratorResume(this value, undefined, "Iterator Helper").
41-
auto result = TRY(iterator->closure()(*iterator));
42-
return create_iterator_result_object(vm, result, iterator->done());
39+
return iterator->resume(vm, js_undefined(), "Iterator Helper"sv);
4340
}
4441

4542
// 3.1.2.1.2 %IteratorHelperPrototype%.return ( ), https://tc39.es/proposal-iterator-helpers/#sec-%iteratorhelperprototype%.return

Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::map)
7272
auto iterated = TRY(get_iterator_direct(vm, object));
7373

7474
// 5. Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called:
75-
IteratorHelper::Closure closure = [mapper = NonnullGCPtr { mapper.as_function() }](auto& iterator) -> ThrowCompletionOr<Value> {
76-
auto& vm = iterator.vm();
77-
75+
IteratorHelper::Closure closure = [mapper = NonnullGCPtr { mapper.as_function() }](auto& vm, auto& iterator) -> ThrowCompletionOr<Value> {
7876
auto const& iterated = iterator.underlying_iterator();
7977

8078
// a. Let counter be 0.
@@ -95,7 +93,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::map)
9593

9694
// v. IfAbruptCloseIterator(mapped, iterated).
9795
if (mapped.is_error())
98-
return iterator.close_result(mapped.release_error());
96+
return iterator.close_result(vm, mapped.release_error());
9997

10098
// viii. Set counter to counter + 1.
10199
// NOTE: We do this step early to ensure it occurs before returning.
@@ -133,9 +131,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::filter)
133131
auto iterated = TRY(get_iterator_direct(vm, object));
134132

135133
// 5. Let closure be a new Abstract Closure with no parameters that captures iterated and predicate and performs the following steps when called:
136-
IteratorHelper::Closure closure = [predicate = NonnullGCPtr { predicate.as_function() }](auto& iterator) -> ThrowCompletionOr<Value> {
137-
auto& vm = iterator.vm();
138-
134+
IteratorHelper::Closure closure = [predicate = NonnullGCPtr { predicate.as_function() }](auto& vm, auto& iterator) -> ThrowCompletionOr<Value> {
139135
auto const& iterated = iterator.underlying_iterator();
140136

141137
// a. Let counter be 0.
@@ -157,7 +153,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::filter)
157153

158154
// v. IfAbruptCloseIterator(selected, iterated).
159155
if (selected.is_error())
160-
return iterator.close_result(selected.release_error());
156+
return iterator.close_result(vm, selected.release_error());
161157

162158
// vii. Set counter to counter + 1.
163159
// NOTE: We do this step early to ensure it occurs before returning.
@@ -209,9 +205,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::take)
209205
auto iterated = TRY(get_iterator_direct(vm, object));
210206

211207
// 8. Let closure be a new Abstract Closure with no parameters that captures iterated and integerLimit and performs the following steps when called:
212-
IteratorHelper::Closure closure = [integer_limit](auto& iterator) -> ThrowCompletionOr<Value> {
213-
auto& vm = iterator.vm();
214-
208+
IteratorHelper::Closure closure = [integer_limit](auto& vm, auto& iterator) -> ThrowCompletionOr<Value> {
215209
auto const& iterated = iterator.underlying_iterator();
216210

217211
// a. Let remaining be integerLimit.
@@ -220,7 +214,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::take)
220214
// i. If remaining is 0, then
221215
if (iterator.counter() >= integer_limit) {
222216
// 1. Return ? IteratorClose(iterated, NormalCompletion(undefined)).
223-
return iterator.close_result(normal_completion(js_undefined()));
217+
return iterator.close_result(vm, normal_completion(js_undefined()));
224218
}
225219

226220
// ii. If remaining is not +∞, then
@@ -276,9 +270,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::drop)
276270
auto iterated = TRY(get_iterator_direct(vm, object));
277271

278272
// 8. Let closure be a new Abstract Closure with no parameters that captures iterated and integerLimit and performs the following steps when called:
279-
IteratorHelper::Closure closure = [integer_limit](auto& iterator) -> ThrowCompletionOr<Value> {
280-
auto& vm = iterator.vm();
281-
273+
IteratorHelper::Closure closure = [integer_limit](auto& vm, auto& iterator) -> ThrowCompletionOr<Value> {
282274
auto const& iterated = iterator.underlying_iterator();
283275

284276
// a. Let remaining be integerLimit.
@@ -357,14 +349,14 @@ class FlatMapIterator : public Cell {
357349

358350
// v. IfAbruptCloseIterator(mapped, iterated).
359351
if (mapped.is_error())
360-
return iterator.close_result(mapped.release_error());
352+
return iterator.close_result(vm, mapped.release_error());
361353

362354
// vi. Let innerIterator be Completion(GetIteratorFlattenable(mapped, reject-strings)).
363355
auto inner_iterator = get_iterator_flattenable(vm, mapped.release_value(), StringHandling::RejectStrings);
364356

365357
// vii. IfAbruptCloseIterator(innerIterator, iterated).
366358
if (inner_iterator.is_error())
367-
return iterator.close_result(inner_iterator.release_error());
359+
return iterator.close_result(vm, inner_iterator.release_error());
368360

369361
// viii. Let innerAlive be true.
370362
m_inner_iterator = inner_iterator.release_value();
@@ -386,7 +378,7 @@ class FlatMapIterator : public Cell {
386378

387379
// 2. IfAbruptCloseIterator(innerNext, iterated).
388380
if (inner_next.is_error())
389-
return iterator.close_result(inner_next.release_error());
381+
return iterator.close_result(vm, inner_next.release_error());
390382

391383
// 3. If innerNext is false, then
392384
if (!inner_next.value()) {
@@ -402,7 +394,7 @@ class FlatMapIterator : public Cell {
402394

403395
// b. IfAbruptCloseIterator(innerValue, iterated).
404396
if (inner_value.is_error())
405-
return iterator.close_result(inner_value.release_error());
397+
return iterator.close_result(vm, inner_value.release_error());
406398

407399
// c. Let completion be Completion(Yield(innerValue)).
408400
// d. If completion is an abrupt completion, then
@@ -437,9 +429,7 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::flat_map)
437429
auto flat_map_iterator = MUST_OR_THROW_OOM(vm.heap().allocate<FlatMapIterator>(realm));
438430

439431
// 5. Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called:
440-
IteratorHelper::Closure closure = [flat_map_iterator, mapper = NonnullGCPtr { mapper.as_function() }](auto& iterator) mutable -> ThrowCompletionOr<Value> {
441-
auto& vm = iterator.vm();
442-
432+
IteratorHelper::Closure closure = [flat_map_iterator, mapper = NonnullGCPtr { mapper.as_function() }](auto& vm, auto& iterator) mutable -> ThrowCompletionOr<Value> {
443433
auto const& iterated = iterator.underlying_iterator();
444434
return flat_map_iterator->next(vm, iterated, iterator, *mapper);
445435
};

0 commit comments

Comments
 (0)