From 3d37cb07531b80093927007240f76d3c30a9e625 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sun, 19 Nov 2023 12:31:20 +1300 Subject: [PATCH] LibWeb: Partially implement ReadableByteStreamControllerPullInto This implementation does not handle DataViews at the moment, but should handle all of the typed arrays. --- .../LibWeb/Streams/AbstractOperations.cpp | 157 ++++++++++++++++++ .../LibWeb/Streams/AbstractOperations.h | 1 + 2 files changed, 158 insertions(+) diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp index f59d1b7786..d01c663975 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp @@ -8,8 +8,12 @@ */ #include +#include +#include +#include #include #include +#include #include #include #include @@ -654,6 +658,159 @@ JS::Value readable_byte_stream_controller_convert_pull_into_descriptor(JS::Realm return MUST(JS::construct(vm, *pull_into_descriptor.view_constructor, buffer, JS::Value(pull_into_descriptor.byte_offset), JS::Value(bytes_filled / element_size))); } +// https://streams.spec.whatwg.org/#readable-byte-stream-controller-pull-into +void readable_byte_stream_controller_pull_into(ReadableByteStreamController& controller, JS::Value view_value, ReadIntoRequest& read_into_request) +{ + auto& vm = controller.vm(); + auto& realm = controller.realm(); + + // FIXME: Support DataView + auto& view_object = view_value.as_object(); + auto const& view = verify_cast(view_object); + + // 1. Let stream be controller.[[stream]]. + auto stream = controller.stream(); + + // 2. Let elementSize be 1. + size_t element_size = 1; + + // 3. Let ctor be %DataView%. + JS::NativeFunction* ctor = realm.intrinsics().data_view_constructor(); + + // 4. If view has a [[TypedArrayName]] internal slot (i.e., it is not a DataView), + if (!is(view_object)) { + // 1. Set elementSize to the element size specified in the typed array constructors table for view.[[TypedArrayName]]. + element_size = view.element_size(); + + // 2. Set ctor to the constructor specified in the typed array constructors table for view.[[TypedArrayName]]. + if (is(view_object)) + ctor = realm.intrinsics().int16_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().int32_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().int8_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().uint8_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().uint16_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().uint32_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().uint8_clamped_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().big_int64_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().big_uint64_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().float32_array_constructor(); + else if (is(view_object)) + ctor = realm.intrinsics().float64_array_constructor(); + else + VERIFY_NOT_REACHED(); + } + + // 5. Let byteOffset be view.[[ByteOffset]]. + auto byte_offset = view.byte_offset(); + + // 6. Let byteLength be view.[[ByteLength]]. + auto byte_length = view.byte_length(); + + // 7. Let bufferResult be TransferArrayBuffer(view.[[ViewedArrayBuffer]]). + auto buffer_result = transfer_array_buffer(realm, *view.viewed_array_buffer()); + + // 8. If bufferResult is an abrupt completion, + if (buffer_result.is_exception()) { + // 1. Perform readIntoRequest’s error steps, given bufferResult.[[Value]]. + auto throw_completion = Bindings::dom_exception_to_throw_completion(vm, buffer_result.exception()); + read_into_request.on_error(*throw_completion.release_value()); + + // 2. Return. + return; + } + + // 9. Let buffer be bufferResult.[[Value]]. + auto buffer = buffer_result.value(); + + // 10. Let pullIntoDescriptor be a new pull-into descriptor with buffer buffer, buffer byte length buffer.[[ArrayBufferByteLength]], + // byte offset byteOffset, byte length byteLength, bytes filled 0, element size elementSize, view constructor ctor, and reader type "byob". + PullIntoDescriptor pull_into_descriptor { + .buffer = buffer, + .buffer_byte_length = buffer->byte_length(), + .byte_offset = byte_offset, + .byte_length = byte_length, + .bytes_filled = 0, + .element_size = element_size, + .view_constructor = *ctor, + .reader_type = ReaderType::Byob, + }; + + // 11. If controller.[[pendingPullIntos]] is not empty, + if (!controller.pending_pull_intos().is_empty()) { + // 1. Append pullIntoDescriptor to controller.[[pendingPullIntos]]. + controller.pending_pull_intos().append(pull_into_descriptor); + + // 2. Perform ! ReadableStreamAddReadIntoRequest(stream, readIntoRequest). + readable_stream_add_read_into_request(*stream, read_into_request); + + // 3. Return. + return; + } + + // 12. If stream.[[state]] is "closed", + if (stream->is_closed()) { + // 1. Let emptyView be ! Construct(ctor, « pullIntoDescriptor’s buffer, pullIntoDescriptor’s byte offset, 0 »). + auto empty_view = MUST(JS::construct(vm, *ctor, pull_into_descriptor.buffer, JS::Value(pull_into_descriptor.byte_offset), JS::Value(0))); + + // 2. Perform readIntoRequest’s close steps, given emptyView. + read_into_request.on_close(empty_view); + + // 3. Return. + return; + } + + // 13. If controller.[[queueTotalSize]] > 0, + if (controller.queue_total_size() > 0) { + // 1. If ! ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) is true, + if (readable_byte_stream_controller_fill_pull_into_descriptor_from_queue(controller, pull_into_descriptor)) { + // 1. Let filledView be ! ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor). + auto filled_view = readable_byte_stream_controller_convert_pull_into_descriptor(realm, pull_into_descriptor); + + // 2. Perform ! ReadableByteStreamControllerHandleQueueDrain(controller). + MUST(readable_byte_stream_controller_handle_queue_drain(controller)); + + // 3. Perform readIntoRequest’s chunk steps, given filledView. + read_into_request.on_chunk(filled_view); + + // 4. Return. + return; + } + + // 2. If controller.[[closeRequested]] is true, + if (controller.close_requested()) { + // 1. Let e be a TypeError exception. + auto error = JS::TypeError::create(realm, "Reader has been released"sv); + + // 2. Perform ! ReadableByteStreamControllerError(controller, e). + readable_byte_stream_controller_error(controller, error); + + // 3. Perform readIntoRequest’s error steps, given e. + read_into_request.on_error(error); + + // 4. Return. + return; + } + } + + // 14. Append pullIntoDescriptor to controller.[[pendingPullIntos]]. + controller.pending_pull_intos().append(pull_into_descriptor); + + // 15. Perform ! ReadableStreamAddReadIntoRequest(stream, readIntoRequest). + readable_stream_add_read_into_request(*stream, read_into_request); + + // 16. Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller). + MUST(readable_byte_stream_controller_call_pull_if_needed(controller)); +} + // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease WebIDL::ExceptionOr readable_stream_default_reader_release(ReadableStreamDefaultReader& reader) { diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h index dd190e0525..942376af1d 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h @@ -54,6 +54,7 @@ WebIDL::ExceptionOr readable_stream_reader_generic_release(ReadableStreamG void readable_stream_default_reader_error_read_requests(ReadableStreamDefaultReader&, JS::Value error); void readable_stream_byob_reader_error_read_into_requests(ReadableStreamBYOBReader&, JS::Value error); JS::Value readable_byte_stream_controller_convert_pull_into_descriptor(JS::Realm&, PullIntoDescriptor const&); +void readable_byte_stream_controller_pull_into(ReadableByteStreamController&, JS::Value view_value, ReadIntoRequest&); void readable_byte_stream_controller_fill_head_pull_into_descriptor(ReadableByteStreamController const&, u64 size, PullIntoDescriptor&); WebIDL::ExceptionOr readable_stream_default_reader_read(ReadableStreamDefaultReader&, ReadRequest&);