/* * Copyright (c) 2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include namespace Web::WebIDL { GC_DEFINE_ALLOCATOR(AsyncIterator); AsyncIterator::AsyncIterator(JS::Realm& realm, JS::Object::PropertyKind iteration_kind) : PlatformObject(realm) , m_kind(iteration_kind) { } AsyncIterator::~AsyncIterator() = default; void AsyncIterator::visit_edges(JS::Cell::Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(m_ongoing_promise); } // https://webidl.spec.whatwg.org/#ref-for-dfn-asynchronous-iterator-prototype-object%E2%91%A2 JS::ThrowCompletionOr> AsyncIterator::iterator_next_impl() { auto& realm = this->realm(); auto& vm = this->vm(); // 8. Let nextSteps be the following steps: auto next_steps = [this](JS::VM& vm) { auto& realm = this->realm(); // 1. Let nextPromiseCapability be ! NewPromiseCapability(%Promise%). auto next_promise_capability = WebIDL::create_promise(realm); // 2. If object’s is finished is true, then: if (m_is_finished) { // 1. Let result be CreateIteratorResultObject(undefined, true). auto result = JS::create_iterator_result_object(vm, JS::js_undefined(), true); // 2. Perform ! Call(nextPromiseCapability.[[Resolve]], undefined, « result »). MUST(JS::call(vm, *next_promise_capability->resolve(), JS::js_undefined(), result)); // 3. Return nextPromiseCapability.[[Promise]]. return next_promise_capability->promise(); } // 3. Let kind be object’s kind. // 4. Let nextPromise be the result of getting the next iteration result with object’s target and object. auto& next_promise = as(*next_iteration_result(realm)->promise()); // 5. Let fulfillSteps be the following steps, given next: auto fulfill_steps = [this](JS::VM& vm) { auto next = vm.argument(0); // 1. Set object’s ongoing promise to null. m_ongoing_promise = nullptr; // 2. If next is end of iteration, then: if (next.is_special_empty_value()) { // 1. Set object’s is finished to true. m_is_finished = true; // 2. Return CreateIteratorResultObject(undefined, true). return JS::create_iterator_result_object(vm, JS::js_undefined(), true); } // FIXME: 2. Otherwise, if interface has a pair asynchronously iterable declaration: else if (false) { // 1. Assert: next is a value pair. // 2. Return the iterator result for next and kind. } // Otherwise: else { // 1. Assert: interface has a value asynchronously iterable declaration. // 2. Assert: next is a value of the type that appears in the declaration. // 3. Let value be next, converted to a JavaScript value. // 4. Return CreateIteratorResultObject(value, false). return JS::create_iterator_result_object(vm, next, false); } }; // 6. Let onFulfilled be CreateBuiltinFunction(fulfillSteps, « »). auto on_fulfilled = JS::NativeFunction::create(realm, move(fulfill_steps), 0); // 7. Let rejectSteps be the following steps, given reason: auto reject_steps = [this](JS::VM& vm) { auto reason = vm.argument(0); // 1. Set object’s ongoing promise to null. m_ongoing_promise = nullptr; // 2. Set object’s is finished to true. m_is_finished = true; // 3. Throw reason. return JS::throw_completion(reason); }; // 8. Let onRejected be CreateBuiltinFunction(rejectSteps, « »). auto on_rejected = JS::NativeFunction::create(realm, move(reject_steps), 0); // 9. Perform PerformPromiseThen(nextPromise, onFulfilled, onRejected, nextPromiseCapability). next_promise.perform_then(on_fulfilled, on_rejected, next_promise_capability); // 10. Return nextPromiseCapability.[[Promise]]. return next_promise_capability->promise(); }; // 9. Let ongoingPromise be object’s ongoing promise. // 10. If ongoingPromise is not null, then: if (m_ongoing_promise) { // 1. Let afterOngoingPromiseCapability be ! NewPromiseCapability(%Promise%). auto after_ongoing_promise_capability = WebIDL::create_promise(realm); // 2. Let onSettled be CreateBuiltinFunction(nextSteps, « »). auto on_settled = JS::NativeFunction::create(realm, move(next_steps), 0); // 3. Perform PerformPromiseThen(ongoingPromise, onSettled, onSettled, afterOngoingPromiseCapability). m_ongoing_promise->perform_then(on_settled, on_settled, after_ongoing_promise_capability); // 4. Set object’s ongoing promise to afterOngoingPromiseCapability.[[Promise]]. m_ongoing_promise = as(*after_ongoing_promise_capability->promise()); } // 11. Otherwise: else { // 1. Set object’s ongoing promise to the result of running nextSteps. m_ongoing_promise = as(*next_steps(vm)); } // 12. Return object’s ongoing promise. return m_ongoing_promise; } // https://webidl.spec.whatwg.org/#ref-for-asynchronous-iterator-return JS::ThrowCompletionOr> AsyncIterator::iterator_return_impl(GC::Ref return_promise_capability, JS::Value value) { auto& realm = this->realm(); auto& vm = this->vm(); // 8. Let returnSteps be the following steps: auto return_steps = [this, value](JS::VM& vm) { auto& realm = this->realm(); // 1. Let returnPromiseCapability be ! NewPromiseCapability(%Promise%). auto return_promise_capability = WebIDL::create_promise(realm); // 2. If object’s is finished is true, then: if (m_is_finished) { // 1. Let result be CreateIteratorResultObject(value, true). auto result = JS::create_iterator_result_object(vm, value, true); // 2. Perform ! Call(returnPromiseCapability.[[Resolve]], undefined, « result »). MUST(JS::call(vm, *return_promise_capability->resolve(), JS::js_undefined(), result)); // 3. Return returnPromiseCapability.[[Promise]]. return return_promise_capability->promise(); } // 3. Set object’s is finished to true. m_is_finished = true; // 4. Return the result of running the asynchronous iterator return algorithm for interface, given object’s target, object, and value. return iterator_return(realm, value)->promise(); }; // 9. Let ongoingPromise be object’s ongoing promise. // 10. If ongoingPromise is not null, then: if (m_ongoing_promise) { // 1. Let afterOngoingPromiseCapability be ! NewPromiseCapability(%Promise%). auto after_ongoing_promise_capability = WebIDL::create_promise(realm); // 2. Let onSettled be CreateBuiltinFunction(returnSteps, « »). auto on_settled = JS::NativeFunction::create(realm, move(return_steps), 0); // 3. Perform PerformPromiseThen(ongoingPromise, onSettled, onSettled, afterOngoingPromiseCapability). m_ongoing_promise->perform_then(on_settled, on_settled, after_ongoing_promise_capability); // 4. Set object’s ongoing promise to afterOngoingPromiseCapability.[[Promise]]. m_ongoing_promise = as(*after_ongoing_promise_capability->promise()); } // 11. Otherwise: else { // 1. Set object’s ongoing promise to the result of running returnSteps. m_ongoing_promise = as(*return_steps(vm)); } // 12. Let fulfillSteps be the following steps: auto fulfill_steps = [value](JS::VM& vm) { // 1. Return CreateIteratorResultObject(value, true). return JS::create_iterator_result_object(vm, value, true); }; // 13. Let onFulfilled be CreateBuiltinFunction(fulfillSteps, « »). auto on_fulfilled = JS::NativeFunction::create(realm, move(fulfill_steps), 0); // 14. Perform PerformPromiseThen(object’s ongoing promise, onFulfilled, undefined, returnPromiseCapability). m_ongoing_promise->perform_then(on_fulfilled, JS::js_undefined(), return_promise_capability); // 15. Return returnPromiseCapability.[[Promise]]. return return_promise_capability->promise(); } GC::Ref AsyncIterator::iterator_return(JS::Realm&, JS::Value) { // If this is reached, a `return` data property was generated for your async iterator, but you neglected to override this method. VERIFY_NOT_REACHED(); } }