Coverage Report

Created: 2025-03-04 07:22

/src/serenity/Userland/Libraries/LibJS/Runtime/AsyncGenerator.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
3
 * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <LibJS/Runtime/AsyncGenerator.h>
9
#include <LibJS/Runtime/AsyncGeneratorPrototype.h>
10
#include <LibJS/Runtime/AsyncGeneratorRequest.h>
11
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
12
#include <LibJS/Runtime/GlobalObject.h>
13
#include <LibJS/Runtime/PromiseConstructor.h>
14
15
namespace JS {
16
17
JS_DEFINE_ALLOCATOR(AsyncGenerator);
18
19
ThrowCompletionOr<NonnullGCPtr<AsyncGenerator>> AsyncGenerator::create(Realm& realm, Value initial_value, ECMAScriptFunctionObject* generating_function, NonnullOwnPtr<ExecutionContext> execution_context)
20
0
{
21
0
    auto& vm = realm.vm();
22
    // This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
23
0
    auto generating_function_prototype = TRY(generating_function->get(vm.names.prototype));
24
0
    auto generating_function_prototype_object = TRY(generating_function_prototype.to_object(vm));
25
0
    auto object = realm.heap().allocate<AsyncGenerator>(realm, realm, generating_function_prototype_object, move(execution_context));
26
0
    object->m_generating_function = generating_function;
27
0
    object->m_previous_value = initial_value;
28
0
    return object;
29
0
}
30
31
AsyncGenerator::AsyncGenerator(Realm&, Object& prototype, NonnullOwnPtr<ExecutionContext> context)
32
0
    : Object(ConstructWithPrototypeTag::Tag, prototype)
33
0
    , m_async_generator_context(move(context))
34
0
{
35
0
}
36
37
0
AsyncGenerator::~AsyncGenerator() = default;
38
39
void AsyncGenerator::visit_edges(Cell::Visitor& visitor)
40
0
{
41
0
    Base::visit_edges(visitor);
42
0
    for (auto const& request : m_async_generator_queue) {
43
0
        if (request.completion.value().has_value())
44
0
            visitor.visit(*request.completion.value());
45
0
        visitor.visit(request.capability);
46
0
    }
47
0
    visitor.visit(m_generating_function);
48
0
    visitor.visit(m_previous_value);
49
0
    visitor.visit(m_current_promise);
50
0
    m_async_generator_context->visit_edges(visitor);
51
0
}
52
53
// 27.6.3.4 AsyncGeneratorEnqueue ( generator, completion, promiseCapability ), https://tc39.es/ecma262/#sec-asyncgeneratorenqueue
54
void AsyncGenerator::async_generator_enqueue(Completion completion, NonnullGCPtr<PromiseCapability> promise_capability)
55
0
{
56
    // 1. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }.
57
0
    auto request = AsyncGeneratorRequest { .completion = move(completion), .capability = promise_capability };
58
59
    // 2. Append request to generator.[[AsyncGeneratorQueue]].
60
0
    m_async_generator_queue.append(move(request));
61
62
    // 3. Return unused.
63
0
}
64
65
void AsyncGenerator::set_async_generator_state(Badge<AsyncGeneratorPrototype>, AsyncGenerator::State value)
66
0
{
67
0
    m_async_generator_state = value;
68
0
}
69
70
// 27.7.5.3 Await ( value ), https://tc39.es/ecma262/#await
71
ThrowCompletionOr<void> AsyncGenerator::await(Value value)
72
0
{
73
0
    auto& vm = this->vm();
74
0
    auto& realm = *vm.current_realm();
75
76
    // 1. Let asyncContext be the running execution context.
77
0
    auto& async_context = vm.running_execution_context();
78
79
    // 2. Let promise be ? PromiseResolve(%Promise%, value).
80
0
    auto* promise_object = TRY(promise_resolve(vm, realm.intrinsics().promise_constructor(), value));
81
82
    // 3. Let fulfilledClosure be a new Abstract Closure with parameters (v) that captures asyncContext and performs the
83
    //    following steps when called:
84
0
    auto fulfilled_closure = [this, &async_context](VM& vm) -> ThrowCompletionOr<Value> {
85
0
        auto value = vm.argument(0);
86
87
        // a. Let prevContext be the running execution context.
88
0
        auto& prev_context = vm.running_execution_context();
89
90
        // FIXME: b. Suspend prevContext.
91
92
        // c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
93
0
        TRY(vm.push_execution_context(async_context, {}));
94
95
        // d. Resume the suspended evaluation of asyncContext using NormalCompletion(v) as the result of the operation that
96
        //    suspended it.
97
0
        execute(vm, normal_completion(value));
98
99
        // e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and
100
        //    prevContext is the currently running execution context.
101
0
        VERIFY(&vm.running_execution_context() == &prev_context);
102
103
        // f. Return undefined.
104
0
        return js_undefined();
105
0
    };
106
107
    // 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »).
108
0
    auto on_fulfilled = NativeFunction::create(realm, move(fulfilled_closure), 1, "");
109
110
    // 5. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures asyncContext and performs the
111
    //    following steps when called:
112
0
    auto rejected_closure = [this, &async_context](VM& vm) -> ThrowCompletionOr<Value> {
113
0
        auto reason = vm.argument(0);
114
115
        // a. Let prevContext be the running execution context.
116
0
        auto& prev_context = vm.running_execution_context();
117
118
        // FIXME: b. Suspend prevContext.
119
120
        // c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
121
0
        TRY(vm.push_execution_context(async_context, {}));
122
123
        // d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that
124
        //    suspended it.
125
0
        execute(vm, throw_completion(reason));
126
127
        // e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and
128
        //    prevContext is the currently running execution context.
129
0
        VERIFY(&vm.running_execution_context() == &prev_context);
130
131
        // f. Return undefined.
132
0
        return js_undefined();
133
0
    };
134
135
    // 6. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
136
0
    auto on_rejected = NativeFunction::create(realm, move(rejected_closure), 1, "");
137
138
    // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected).
139
0
    m_current_promise = verify_cast<Promise>(promise_object);
140
0
    m_current_promise->perform_then(on_fulfilled, on_rejected, {});
141
142
    // 8. Remove asyncContext from the execution context stack and restore the execution context that is at the top of the
143
    //    execution context stack as the running execution context.
144
0
    vm.pop_execution_context();
145
146
    // NOTE: None of these are necessary. 10-12 are handled by step d of the above lambdas.
147
    // 9. Let callerContext be the running execution context.
148
    // 10. Resume callerContext passing empty. If asyncContext is ever resumed again, let completion be the Completion Record with which it is resumed.
149
    // 11. Assert: If control reaches here, then asyncContext is the running execution context again.
150
    // 12. Return completion.
151
0
    return {};
152
0
}
153
154
void AsyncGenerator::execute(VM& vm, Completion completion)
155
0
{
156
0
    while (true) {
157
        // Loosely based on step 4 of https://tc39.es/ecma262/#sec-asyncgeneratorstart
158
0
        VERIFY(completion.value().has_value());
159
160
0
        auto generated_value = [](Value value) -> Value {
161
0
            if (value.is_object())
162
0
                return value.as_object().get_without_side_effects("result");
163
0
            return value.is_empty() ? js_undefined() : value;
164
0
        };
165
166
0
        auto generated_continuation = [&](Value value) -> Optional<size_t> {
167
0
            if (value.is_object()) {
168
0
                auto number_value = value.as_object().get_without_side_effects("continuation");
169
0
                if (number_value.is_null())
170
0
                    return {};
171
0
                return static_cast<size_t>(number_value.as_double());
172
0
            }
173
0
            return {};
174
0
        };
175
176
0
        auto generated_is_await = [](Value value) -> bool {
177
0
            if (value.is_object())
178
0
                return value.as_object().get_without_side_effects("isAwait").as_bool();
179
0
            return false;
180
0
        };
181
182
0
        auto& realm = *vm.current_realm();
183
0
        auto completion_object = Object::create(realm, nullptr);
184
0
        completion_object->define_direct_property(vm.names.type, Value(to_underlying(completion.type())), default_attributes);
185
0
        completion_object->define_direct_property(vm.names.value, completion.value().value(), default_attributes);
186
187
0
        auto& bytecode_interpreter = vm.bytecode_interpreter();
188
189
0
        auto const continuation_address = generated_continuation(m_previous_value);
190
191
        // We should never enter `execute` again after the generator is complete.
192
0
        VERIFY(continuation_address.has_value());
193
194
0
        auto next_result = bytecode_interpreter.run_executable(*m_generating_function->bytecode_executable(), continuation_address, completion_object);
195
196
0
        auto result_value = move(next_result.value);
197
0
        if (!result_value.is_throw_completion()) {
198
0
            m_previous_value = result_value.release_value();
199
0
            auto value = generated_value(m_previous_value);
200
0
            bool is_await = generated_is_await(m_previous_value);
201
202
0
            if (is_await) {
203
0
                auto await_result = this->await(value);
204
0
                if (await_result.is_throw_completion()) {
205
0
                    completion = await_result.release_error();
206
0
                    continue;
207
0
                }
208
0
                return;
209
0
            }
210
0
        }
211
212
0
        bool done = result_value.is_throw_completion() || !generated_continuation(m_previous_value).has_value();
213
0
        if (!done) {
214
            // 27.6.3.8 AsyncGeneratorYield ( value ), https://tc39.es/ecma262/#sec-asyncgeneratoryield
215
            // 1. Let genContext be the running execution context.
216
            // 2. Assert: genContext is the execution context of a generator.
217
            // 3. Let generator be the value of the Generator component of genContext.
218
            // 4. Assert: GetGeneratorKind() is async.
219
            // NOTE: genContext is `m_async_generator_context`, generator is `this`.
220
221
            // 5. Let completion be NormalCompletion(value).
222
0
            auto value = generated_value(m_previous_value);
223
0
            auto yield_completion = normal_completion(value);
224
225
            // 6. Assert: The execution context stack has at least two elements.
226
0
            VERIFY(vm.execution_context_stack().size() >= 2);
227
228
            // 7. Let previousContext be the second to top element of the execution context stack.
229
0
            auto& previous_context = vm.execution_context_stack().at(vm.execution_context_stack().size() - 2);
230
231
            // 8. Let previousRealm be previousContext's Realm.
232
0
            auto previous_realm = previous_context->realm;
233
234
            // 9. Perform AsyncGeneratorCompleteStep(generator, completion, false, previousRealm).
235
0
            complete_step(yield_completion, false, previous_realm.ptr());
236
237
            // 10. Let queue be generator.[[AsyncGeneratorQueue]].
238
0
            auto& queue = m_async_generator_queue;
239
240
            // 11. If queue is not empty, then
241
0
            if (!queue.is_empty()) {
242
                // a. NOTE: Execution continues without suspending the generator.
243
                // b. Let toYield be the first element of queue.
244
0
                auto& to_yield = queue.first();
245
246
                // c. Let resumptionValue be Completion(toYield.[[Completion]]).
247
0
                completion = Completion(to_yield.completion);
248
249
                // d. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue).
250
                // NOTE: AsyncGeneratorUnwrapYieldResumption is performed inside the continuation block inside the generator,
251
                //       so we just need to enter the generator again.
252
0
                continue;
253
0
            }
254
            // 12. Else,
255
0
            else {
256
                // a. Set generator.[[AsyncGeneratorState]] to suspendedYield.
257
0
                m_async_generator_state = State::SuspendedYield;
258
259
                // b. Remove genContext from the execution context stack and restore the execution context that is at the top of the
260
                //    execution context stack as the running execution context.
261
0
                vm.pop_execution_context();
262
263
                // c. Let callerContext be the running execution context.
264
                // d. Resume callerContext passing undefined. If genContext is ever resumed again, let resumptionValue be the Completion Record with which it is resumed.
265
                // e. Assert: If control reaches here, then genContext is the running execution context again.
266
                // f. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue).
267
                // NOTE: e-f are performed whenever someone calls `execute` again.
268
0
                return;
269
0
            }
270
0
        }
271
272
        // 27.6.3.2 AsyncGeneratorStart ( generator, generatorBody ), https://tc39.es/ecma262/#sec-asyncgeneratorstart
273
        // 4.e. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return.
274
        // 4.f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
275
0
        vm.pop_execution_context();
276
277
        // 4.g. Set acGenerator.[[AsyncGeneratorState]] to completed.
278
0
        m_async_generator_state = State::Completed;
279
280
        // 4.h. If result.[[Type]] is normal, set result to NormalCompletion(undefined).
281
        // 4.i. If result.[[Type]] is return, set result to NormalCompletion(result.[[Value]]).
282
0
        Completion result;
283
0
        if (!result_value.is_throw_completion()) {
284
0
            result = normal_completion(generated_value(m_previous_value));
285
0
        } else {
286
0
            result = result_value.release_error();
287
0
        }
288
289
        // 4.j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true).
290
0
        complete_step(result, true);
291
292
        // 4.k. Perform AsyncGeneratorDrainQueue(acGenerator).
293
0
        drain_queue();
294
295
        // 4.l. Return undefined.
296
0
        return;
297
0
    }
298
0
}
299
300
// 27.6.3.6 AsyncGeneratorResume ( generator, completion ), https://tc39.es/ecma262/#sec-asyncgeneratorresume
301
ThrowCompletionOr<void> AsyncGenerator::resume(VM& vm, Completion completion)
302
0
{
303
    // 1. Assert: generator.[[AsyncGeneratorState]] is either suspendedStart or suspendedYield.
304
0
    VERIFY(m_async_generator_state == State::SuspendedStart || m_async_generator_state == State::SuspendedYield);
305
306
    // 2. Let genContext be generator.[[AsyncGeneratorContext]].
307
0
    auto& generator_context = m_async_generator_context;
308
309
    // 3. Let callerContext be the running execution context.
310
0
    auto const& caller_context = vm.running_execution_context();
311
312
    // FIXME: 4. Suspend callerContext.
313
314
    // 5. Set generator.[[AsyncGeneratorState]] to executing.
315
0
    m_async_generator_state = State::Executing;
316
317
    // 6. Push genContext onto the execution context stack; genContext is now the running execution context.
318
0
    TRY(vm.push_execution_context(*generator_context, {}));
319
320
    // 7. Resume the suspended evaluation of genContext using completion as the result of the operation that suspended
321
    //    it. Let result be the Completion Record returned by the resumed computation.
322
    // 8. Assert: result is never an abrupt completion.
323
0
    execute(vm, completion);
324
325
    // 9. Assert: When we return here, genContext has already been removed from the execution context stack and
326
    //    callerContext is the currently running execution context.
327
0
    VERIFY(&vm.running_execution_context() == &caller_context);
328
329
    // 10. Return unused.
330
0
    return {};
331
0
}
332
333
// 27.6.3.9 AsyncGeneratorAwaitReturn ( generator ), https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn
334
// With unmerged broken promise fixup from https://github.com/tc39/ecma262/pull/2683
335
void AsyncGenerator::await_return()
336
0
{
337
0
    auto& vm = this->vm();
338
0
    auto& realm = *vm.current_realm();
339
340
    // 1. Let queue be generator.[[AsyncGeneratorQueue]].
341
0
    auto& queue = m_async_generator_queue;
342
343
    // 2. Assert: queue is not empty.
344
0
    VERIFY(!queue.is_empty());
345
346
    // 3. Let next be the first element of queue.
347
0
    auto& next = m_async_generator_queue.first();
348
349
    // 4. Let completion be Completion(next.[[Completion]]).
350
0
    auto completion = Completion(next.completion);
351
352
    // 5. Assert: completion.[[Type]] is return.
353
0
    VERIFY(completion.type() == Completion::Type::Return);
354
355
    // 6. Let promiseCompletion be Completion(PromiseResolve(%Promise%, _completion_.[[Value]])).
356
0
    auto promise_completion = promise_resolve(vm, realm.intrinsics().promise_constructor(), completion.value().value());
357
358
    // 7. If promiseCompletion is an abrupt completion, then
359
0
    if (promise_completion.is_throw_completion()) {
360
        // a. Set generator.[[AsyncGeneratorState]] to completed.
361
0
        m_async_generator_state = State::Completed;
362
363
        // b. Perform AsyncGeneratorCompleteStep(generator, promiseCompletion, true).
364
0
        complete_step(promise_completion.release_error(), true);
365
366
        // c. Perform AsyncGeneratorDrainQueue(generator).
367
0
        drain_queue();
368
369
        // d. Return unused.
370
0
        return;
371
0
    }
372
373
    // 8. Assert: promiseCompletion.[[Type]] is normal.
374
0
    VERIFY(!promise_completion.is_throw_completion());
375
376
    // 9. Let promise be promiseCompletion.[[Value]].
377
0
    auto* promise = promise_completion.release_value();
378
379
    // 10. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures generator and performs
380
    //    the following steps when called:
381
0
    auto fulfilled_closure = [this](VM& vm) -> ThrowCompletionOr<Value> {
382
        // a. Set generator.[[AsyncGeneratorState]] to completed.
383
0
        m_async_generator_state = State::Completed;
384
385
        // b. Let result be NormalCompletion(value).
386
0
        auto result = normal_completion(vm.argument(0));
387
388
        // c. Perform AsyncGeneratorCompleteStep(generator, result, true).
389
0
        complete_step(result, true);
390
391
        // d. Perform AsyncGeneratorDrainQueue(generator).
392
0
        drain_queue();
393
394
        // e. Return undefined.
395
0
        return js_undefined();
396
0
    };
397
398
    // 11. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »).
399
0
    auto on_fulfilled = NativeFunction::create(realm, move(fulfilled_closure), 1, "");
400
401
    // 12. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures generator and performs
402
    //    the following steps when called:
403
0
    auto rejected_closure = [this](VM& vm) -> ThrowCompletionOr<Value> {
404
        // a. Set generator.[[AsyncGeneratorState]] to completed.
405
0
        m_async_generator_state = State::Completed;
406
407
        // b. Let result be ThrowCompletion(reason).
408
0
        auto result = throw_completion(vm.argument(0));
409
410
        // c. Perform AsyncGeneratorCompleteStep(generator, result, true).
411
0
        complete_step(result, true);
412
413
        // d. Perform AsyncGeneratorDrainQueue(generator).
414
0
        drain_queue();
415
416
        // e. Return undefined.
417
0
        return js_undefined();
418
0
    };
419
420
    // 13. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
421
0
    auto on_rejected = NativeFunction::create(realm, move(rejected_closure), 1, "");
422
423
    // 14. Perform PerformPromiseThen(promise, onFulfilled, onRejected).
424
    // NOTE: await_return should only be called when the generator is in SuspendedStart or Completed state,
425
    //       so an await shouldn't be running currently, so it should be safe to overwrite m_current_promise.
426
0
    m_current_promise = verify_cast<Promise>(promise);
427
0
    m_current_promise->perform_then(on_fulfilled, on_rejected, {});
428
429
    // 15. Return unused.
430
0
    return;
431
0
}
432
433
// 27.6.3.5 AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] ), https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep
434
void AsyncGenerator::complete_step(Completion completion, bool done, Realm* realm)
435
0
{
436
0
    auto& vm = this->vm();
437
438
    // 1. Assert: generator.[[AsyncGeneratorQueue]] is not empty.
439
0
    VERIFY(!m_async_generator_queue.is_empty());
440
441
    // 2. Let next be the first element of generator.[[AsyncGeneratorQueue]].
442
    // 3. Remove the first element from generator.[[AsyncGeneratorQueue]].
443
0
    auto next = m_async_generator_queue.take_first();
444
445
    // 4. Let promiseCapability be next.[[Capability]].
446
0
    auto promise_capability = next.capability;
447
448
    // 5. Let value be completion.[[Value]].
449
0
    auto value = completion.value().value();
450
451
    // 6. If completion.[[Type]] is throw, then
452
0
    if (completion.type() == Completion::Type::Throw) {
453
        // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
454
0
        MUST(call(vm, *promise_capability->reject(), js_undefined(), value));
455
0
    }
456
    // 7. Else,
457
0
    else {
458
        // a. Assert: completion.[[Type]] is normal.
459
0
        VERIFY(completion.type() == Completion::Type::Normal);
460
461
0
        GCPtr<Object> iterator_result;
462
463
        // b. If realm is present, then
464
0
        if (realm) {
465
            // i. Let oldRealm be the running execution context's Realm.
466
0
            auto old_realm = vm.running_execution_context().realm;
467
468
            // ii. Set the running execution context's Realm to realm.
469
0
            vm.running_execution_context().realm = realm;
470
471
            // iii. Let iteratorResult be CreateIterResultObject(value, done).
472
0
            iterator_result = create_iterator_result_object(vm, value, done);
473
474
            // iv. Set the running execution context's Realm to oldRealm.
475
0
            vm.running_execution_context().realm = old_realm;
476
0
        }
477
        // c. Else,
478
0
        else {
479
            // i. Let iteratorResult be CreateIterResultObject(value, done).
480
0
            iterator_result = create_iterator_result_object(vm, value, done);
481
0
        }
482
483
0
        VERIFY(iterator_result);
484
485
        // d. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).
486
0
        MUST(call(vm, *promise_capability->resolve(), js_undefined(), iterator_result));
487
0
    }
488
489
    // 8. Return unused.
490
0
}
491
492
// 27.6.3.10 AsyncGeneratorDrainQueue ( generator ), https://tc39.es/ecma262/#sec-asyncgeneratordrainqueue
493
void AsyncGenerator::drain_queue()
494
0
{
495
    // 1. Assert: generator.[[AsyncGeneratorState]] is completed.
496
0
    VERIFY(m_async_generator_state == State::Completed);
497
498
    // 2. Let queue be generator.[[AsyncGeneratorQueue]].
499
0
    auto& queue = m_async_generator_queue;
500
501
    // 3. If queue is empty, return unused.
502
0
    if (queue.is_empty())
503
0
        return;
504
505
    // 4. Let done be false.
506
0
    bool done = false;
507
508
    // 5. Repeat, while done is false,
509
0
    while (!done) {
510
        // a. Let next be the first element of queue.
511
0
        auto& next = m_async_generator_queue.first();
512
513
        // b. Let completion be Completion(next.[[Completion]]).
514
0
        auto completion = Completion(next.completion);
515
516
        // c. If completion.[[Type]] is return, then
517
0
        if (completion.type() == Completion::Type::Return) {
518
            // i. Set generator.[[AsyncGeneratorState]] to awaiting-return.
519
0
            m_async_generator_state = State::AwaitingReturn;
520
521
            // ii. Perform AsyncGeneratorAwaitReturn(generator).
522
0
            await_return();
523
524
            // iii. Set done to true.
525
0
            done = true;
526
0
        }
527
        // d. Else,
528
0
        else {
529
            // i. If completion.[[Type]] is normal, then
530
0
            if (completion.type() == Completion::Type::Normal) {
531
                // 1. Set completion to NormalCompletion(undefined).
532
0
                completion = normal_completion(js_undefined());
533
0
            }
534
535
            // ii. Perform AsyncGeneratorCompleteStep(generator, completion, true).
536
0
            complete_step(completion, true);
537
538
            // iii. If queue is empty, set done to true.
539
0
            if (queue.is_empty())
540
0
                done = true;
541
0
        }
542
0
    }
543
544
    // 6. Return unused.
545
0
}
546
547
}