/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 | | } |