LibWeb: Implement AO readable_stream_from_iterable

This commit is contained in:
Kenneth Myhra 2024-06-08 10:43:54 +02:00 committed by Andreas Kling
parent ce521a196d
commit 01a8b5ee54
Notes: sideshowbarker 2024-07-17 11:34:34 +09:00
2 changed files with 112 additions and 0 deletions

View File

@ -12,6 +12,7 @@
#include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/DataViewConstructor.h>
#include <LibJS/Runtime/Intrinsics.h>
#include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/PromiseCapability.h>
#include <LibJS/Runtime/PromiseConstructor.h>
#include <LibJS/Runtime/TypedArray.h>
@ -1383,6 +1384,116 @@ void readable_stream_error(ReadableStream& stream, JS::Value error)
}
}
// https://streams.spec.whatwg.org/#readable-stream-from-iterable
WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> readable_stream_from_iterable(JS::VM& vm, JS::Value async_iterable)
{
auto& realm = *vm.current_realm();
// 1. Let stream be undefined.
// NON-STANDARD: We capture 'stream' in a lambda later, so it needs to be allocated now.
// 'stream' is still in an uninitialized state and will be initialized / set up at step 6.
auto stream = realm.heap().allocate<ReadableStream>(realm, realm);
// 2. Let iteratorRecord be ? GetIterator(asyncIterable, async).
auto iterator_record = TRY(JS::get_iterator(vm, async_iterable, JS::IteratorHint::Async));
// 3. Let startAlgorithm be an algorithm that returns undefined.
auto start_algorithm = JS::create_heap_function(realm.heap(), []() -> WebIDL::ExceptionOr<JS::Value> {
return JS::js_undefined();
});
// 4. Let pullAlgorithm be the following steps:
auto pull_algorithm = JS::create_heap_function(realm.heap(), [&vm, &realm, stream, iterator_record]() mutable {
// 1. Let nextResult be IteratorNext(iteratorRecord).
auto next_result = JS::iterator_next(vm, iterator_record);
// 2. If nextResult is an abrupt completion, return a promise rejected with nextResult.[[Value]].
if (next_result.is_error())
return WebIDL::create_rejected_promise(realm, *next_result.throw_completion().release_value());
// 3. Let nextPromise be a promise resolved with nextResult.[[Value]].
auto next_promise = WebIDL::create_resolved_promise(realm, next_result.release_value());
// 4. Return the result of reacting to nextPromise with the following fulfillment steps, given iterResult:
auto react_result = WebIDL::react_to_promise(*next_promise,
JS::create_heap_function(realm.heap(), [&vm, stream](JS::Value iter_result) -> WebIDL::ExceptionOr<JS::Value> {
// 1. If Type(iterResult) is not Object, throw a TypeError.
if (!iter_result.is_object())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "iterResult is not an Object"sv };
// 2. Let done be ? IteratorComplete(iterResult).
auto done = TRY(JS::iterator_complete(vm, iter_result.as_object()));
// 3. If done is true:
if (done) {
// 1. Perform ! ReadableStreamDefaultControllerClose(stream.[[controller]]).
readable_stream_default_controller_close(*stream->controller()->get<JS::NonnullGCPtr<ReadableStreamDefaultController>>());
}
// 4. Otherwise:
else {
// 1. Let value be ? IteratorValue(iterResult).
auto value = TRY(JS::iterator_value(vm, iter_result.as_object()));
// 2. Perform ! ReadableStreamDefaultControllerEnqueue(stream.[[controller]], value).
MUST(readable_stream_default_controller_enqueue(*stream->controller()->get<JS::NonnullGCPtr<ReadableStreamDefaultController>>(), value));
}
return JS::js_undefined();
}),
{});
return WebIDL::create_resolved_promise(realm, react_result);
});
// 5. Let cancelAlgorithm be the following steps, given reason:
auto cancel_algorithm = JS::create_heap_function(realm.heap(), [&vm, &realm, iterator_record](JS::Value reason) {
// 1. Let iterator be iteratorRecord.[[Iterator]].
auto iterator = iterator_record->iterator;
// 2. Let returnMethod be GetMethod(iterator, "return").
auto return_method = iterator->get(vm.names.return_);
// 3. If returnMethod is an abrupt completion, return a promise rejected with returnMethod.[[Value]].
if (return_method.is_error())
return WebIDL::create_rejected_promise(realm, *return_method.throw_completion().release_value());
// 4. If returnMethod.[[Value]] is undefined, return a promise resolved with undefined.
if (return_method.value().is_undefined())
return WebIDL::create_resolved_promise(realm, JS::js_undefined());
// 5. Let returnResult be Call(returnMethod.[[Value]], iterator, « reason »).
auto return_result = JS::call(vm, return_method.value(), reason);
// 6. If returnResult is an abrupt completion, return a promise rejected with returnResult.[[Value]].
if (return_result.is_error())
return WebIDL::create_rejected_promise(realm, *return_result.throw_completion().release_value());
// 7. Let returnPromise be a promise resolved with returnResult.[[Value]].
auto return_promise = WebIDL::create_resolved_promise(realm, return_result.release_value());
// 8. Return the result of reacting to returnPromise with the following fulfillment steps, given iterResult:
auto react_result = WebIDL::react_to_promise(*return_promise,
JS::create_heap_function(realm.heap(), [](JS::Value iter_result) -> WebIDL::ExceptionOr<JS::Value> {
// 1. If Type(iterResult) is not Object, throw a TypeError.
if (!iter_result.is_object())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "iterResult is not an Object"sv };
// 2. Return undefined.
return JS::js_undefined();
}),
{});
return WebIDL::create_resolved_promise(realm, react_result);
});
// 6. Set stream to ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, 0).
// NON-STANDARD: 'stream' is captured in a lambda defined earlier, so we cannot overwrite it by assigning the ReadableStream returned by CreateReadableStream.
MUST(set_up_readable_stream(realm, *stream, start_algorithm, pull_algorithm, cancel_algorithm, 0));
// 7. Return stream.
return stream;
}
// https://streams.spec.whatwg.org/#readable-stream-add-read-request
void readable_stream_add_read_request(ReadableStream& stream, JS::NonnullGCPtr<ReadRequest> read_request)
{

View File

@ -37,6 +37,7 @@ WebIDL::ExceptionOr<double> extract_high_water_mark(QueuingStrategy const&, doub
void readable_stream_close(ReadableStream&);
void readable_stream_error(ReadableStream&, JS::Value error);
WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> readable_stream_from_iterable(JS::VM& vm, JS::Value async_iterable);
void readable_stream_add_read_request(ReadableStream&, JS::NonnullGCPtr<ReadRequest>);
void readable_stream_add_read_into_request(ReadableStream&, JS::NonnullGCPtr<ReadIntoRequest>);
JS::NonnullGCPtr<WebIDL::Promise> readable_stream_cancel(ReadableStream&, JS::Value reason);