/* * Copyright (c) 2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace Web::WebIDL { class AsyncIterator : public Bindings::PlatformObject { WEB_PLATFORM_OBJECT(AsyncIterator, Bindings::PlatformObject); GC_DECLARE_ALLOCATOR(AsyncIterator); public: virtual ~AsyncIterator() override; // https://webidl.spec.whatwg.org/#ref-for-dfn-asynchronous-iterator-prototype-object%E2%91%A2 template static JS::ThrowCompletionOr> next(JS::Realm& realm, StringView interface_name) { auto validation_result = validate_this(realm, interface_name); return validation_result.visit( [](GC::Ref iterator) { return iterator->iterator_next_impl(); }, [](JS::ThrowCompletionOr> result) { return result; }); } // https://webidl.spec.whatwg.org/#ref-for-asynchronous-iterator-return template static JS::ThrowCompletionOr> return_(JS::Realm& realm, StringView interface_name, JS::Value value) { auto return_promise_capability = WebIDL::create_promise(realm); auto validation_result = validate_this(realm, interface_name, return_promise_capability); return validation_result.visit( [&](GC::Ref iterator) { return iterator->iterator_return_impl(return_promise_capability, value); }, [](JS::ThrowCompletionOr> result) { return result; }); } protected: AsyncIterator(JS::Realm&, JS::Object::PropertyKind); virtual void visit_edges(Cell::Visitor&) override; virtual GC::Ref next_iteration_result(JS::Realm&) = 0; virtual GC::Ref iterator_return(JS::Realm&, JS::Value); private: template static Variant, GC::Ref> validate_this(JS::Realm& realm, StringView interface_name, GC::Ptr this_validation_promise_capability = {}) { // NOTE: This defines the steps to validate `this` that are common between "next" and "return". auto& vm = realm.vm(); // 1. Let interface be the interface for which the asynchronous iterator prototype object exists. // 2. Let thisValidationPromiseCapability be ! NewPromiseCapability(%Promise%). if (!this_validation_promise_capability) this_validation_promise_capability = WebIDL::create_promise(realm); // 3. Let thisValue be the this value. auto this_value = vm.this_value(); // 4. Let object be Completion(ToObject(thisValue)). // 5. IfAbruptRejectPromise(object, thisValidationPromiseCapability). auto object = TRY_OR_REJECT(vm, this_validation_promise_capability, this_value.to_object(vm)); // FIXME: 6. If object is a platform object, then perform a security check, passing: // * the platform object object, // * the identifier "next", and // * the type "method". // // If this threw an exception e, then: // Perform ! Call(thisValidationPromiseCapability.[[Reject]], undefined, « e »). // Return thisValidationPromiseCapability.[[Promise]]. // 7. If object is not a default asynchronous iterator object for interface, then: auto* iterator = as_if(*object); if (!iterator) { // 1. Let error be a new TypeError. auto error = vm.throw_completion(JS::ErrorType::NotAnObjectOfType, interface_name); // 2. Perform ! Call(thisValidationPromiseCapability.[[Reject]], undefined, « error »). // 3. Return thisValidationPromiseCapability.[[Promise]]. TRY_OR_MUST_REJECT(vm, this_validation_promise_capability, error); VERIFY_NOT_REACHED(); } return GC::Ref { *iterator }; } JS::ThrowCompletionOr> iterator_next_impl(); JS::ThrowCompletionOr> iterator_return_impl(GC::Ref return_promise_capability, JS::Value); JS::Object::PropertyKind m_kind { JS::Object::PropertyKind::Value }; GC::Ptr m_ongoing_promise; bool m_is_finished { false }; }; }