mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-24 09:52:31 +00:00
LibWeb/Streams: Ensure pending pull into's objects are visited by GC
While PendingPullIntos are typically visted by their controller there were some cases that we were removing those references from the controller and storing them in a SinglyLinkedList on the stack which is not safe. Instead, make PendingPullInto a GC::Cell type, which also allows us to remove an awkward copy of the struct where the underlying reference was previously being destroyed.
This commit is contained in:
parent
927bdf909b
commit
add8bf4790
Notes:
github-actions[bot]
2025-01-18 09:27:46 +00:00
Author: https://github.com/shannonbooth
Commit: add8bf4790
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3289
Reviewed-by: https://github.com/awesomekling
4 changed files with 86 additions and 61 deletions
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
|
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
|
||||||
* Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
|
* Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org>
|
||||||
* Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
* Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -1686,7 +1686,7 @@ void readable_stream_byob_reader_error_read_into_requests(ReadableStreamBYOBRead
|
||||||
void readable_byte_stream_controller_fill_head_pull_into_descriptor(ReadableByteStreamController const& controller, u64 size, PullIntoDescriptor& pull_into_descriptor)
|
void readable_byte_stream_controller_fill_head_pull_into_descriptor(ReadableByteStreamController const& controller, u64 size, PullIntoDescriptor& pull_into_descriptor)
|
||||||
{
|
{
|
||||||
// 1. Assert: either controller.[[pendingPullIntos]] is empty, or controller.[[pendingPullIntos]][0] is pullIntoDescriptor.
|
// 1. Assert: either controller.[[pendingPullIntos]] is empty, or controller.[[pendingPullIntos]][0] is pullIntoDescriptor.
|
||||||
VERIFY(controller.pending_pull_intos().is_empty() || &controller.pending_pull_intos().first() == &pull_into_descriptor);
|
VERIFY(controller.pending_pull_intos().is_empty() || controller.pending_pull_intos().first().ptr() == &pull_into_descriptor);
|
||||||
|
|
||||||
// 2. Assert: controller.[[byobRequest]] is null.
|
// 2. Assert: controller.[[byobRequest]] is null.
|
||||||
VERIFY(!controller.raw_byob_request());
|
VERIFY(!controller.raw_byob_request());
|
||||||
|
@ -1915,17 +1915,16 @@ void readable_byte_stream_controller_pull_into(ReadableByteStreamController& con
|
||||||
|
|
||||||
// 10. Let pullIntoDescriptor be a new pull-into descriptor with buffer buffer, buffer byte length buffer.[[ArrayBufferByteLength]],
|
// 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".
|
// byte offset byteOffset, byte length byteLength, bytes filled 0, element size elementSize, view constructor ctor, and reader type "byob".
|
||||||
PullIntoDescriptor pull_into_descriptor {
|
auto pull_into_descriptor = vm.heap().allocate<PullIntoDescriptor>(
|
||||||
.buffer = buffer,
|
buffer,
|
||||||
.buffer_byte_length = buffer->byte_length(),
|
buffer->byte_length(),
|
||||||
.byte_offset = byte_offset,
|
byte_offset,
|
||||||
.byte_length = byte_length,
|
byte_length,
|
||||||
.bytes_filled = 0,
|
0,
|
||||||
.minimum_fill = minimum_fill,
|
minimum_fill,
|
||||||
.element_size = element_size,
|
element_size,
|
||||||
.view_constructor = *ctor,
|
*ctor,
|
||||||
.reader_type = ReaderType::Byob,
|
ReaderType::Byob);
|
||||||
};
|
|
||||||
|
|
||||||
// 11. If controller.[[pendingPullIntos]] is not empty,
|
// 11. If controller.[[pendingPullIntos]] is not empty,
|
||||||
if (!controller.pending_pull_intos().is_empty()) {
|
if (!controller.pending_pull_intos().is_empty()) {
|
||||||
|
@ -1942,7 +1941,7 @@ void readable_byte_stream_controller_pull_into(ReadableByteStreamController& con
|
||||||
// 12. If stream.[[state]] is "closed",
|
// 12. If stream.[[state]] is "closed",
|
||||||
if (stream->is_closed()) {
|
if (stream->is_closed()) {
|
||||||
// 1. Let emptyView be ! Construct(ctor, « pullIntoDescriptor’s buffer, pullIntoDescriptor’s byte offset, 0 »).
|
// 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)));
|
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.
|
// 2. Perform readIntoRequest’s close steps, given emptyView.
|
||||||
read_into_request.on_close(empty_view);
|
read_into_request.on_close(empty_view);
|
||||||
|
@ -2263,7 +2262,7 @@ GC::Ptr<ReadableStreamBYOBRequest> readable_byte_stream_controller_get_byob_requ
|
||||||
// 1. If controller.[[byobRequest]] is null and controller.[[pendingPullIntos]] is not empty,
|
// 1. If controller.[[byobRequest]] is null and controller.[[pendingPullIntos]] is not empty,
|
||||||
if (!controller->raw_byob_request() && !controller->pending_pull_intos().is_empty()) {
|
if (!controller->raw_byob_request() && !controller->pending_pull_intos().is_empty()) {
|
||||||
// 1. Let firstDescriptor be controller.[[pendingPullIntos]][0].
|
// 1. Let firstDescriptor be controller.[[pendingPullIntos]][0].
|
||||||
auto const& first_descriptor = controller->pending_pull_intos().first();
|
auto const& first_descriptor = *controller->pending_pull_intos().first();
|
||||||
|
|
||||||
// 2. Let view be ! Construct(%Uint8Array%, « firstDescriptor’s buffer, firstDescriptor’s byte offset + firstDescriptor’s bytes filled, firstDescriptor’s byte length − firstDescriptor’s bytes filled »).
|
// 2. Let view be ! Construct(%Uint8Array%, « firstDescriptor’s buffer, firstDescriptor’s byte offset + firstDescriptor’s bytes filled, firstDescriptor’s byte length − firstDescriptor’s bytes filled »).
|
||||||
auto view = MUST(JS::construct(vm, *realm.intrinsics().uint8_array_constructor(), first_descriptor.buffer, JS::Value(first_descriptor.byte_offset + first_descriptor.bytes_filled), JS::Value(first_descriptor.byte_length - first_descriptor.bytes_filled)));
|
auto view = MUST(JS::construct(vm, *realm.intrinsics().uint8_array_constructor(), first_descriptor.buffer, JS::Value(first_descriptor.byte_offset + first_descriptor.bytes_filled), JS::Value(first_descriptor.byte_length - first_descriptor.bytes_filled)));
|
||||||
|
@ -2319,7 +2318,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_respond_in_readable_st
|
||||||
// 3. For each filledPullInto of filledPullIntos,
|
// 3. For each filledPullInto of filledPullIntos,
|
||||||
for (auto& filled_pull_into : filled_pulled_intos) {
|
for (auto& filled_pull_into : filled_pulled_intos) {
|
||||||
// 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto).
|
// 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto).
|
||||||
readable_byte_stream_controller_commit_pull_into_descriptor(*controller.stream(), filled_pull_into);
|
readable_byte_stream_controller_commit_pull_into_descriptor(*controller.stream(), *filled_pull_into);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Return.
|
// 4. Return.
|
||||||
|
@ -2333,34 +2332,33 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_respond_in_readable_st
|
||||||
// NOTE: A descriptor for a read() request that is not yet filled up to its minimum length will stay at the head of the queue, so the underlying source can keep filling it.
|
// NOTE: A descriptor for a read() request that is not yet filled up to its minimum length will stay at the head of the queue, so the underlying source can keep filling it.
|
||||||
|
|
||||||
// 5. Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
|
// 5. Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
|
||||||
// NOTE: We need to take a copy of pull_into_descriptor here as the shift destroys the pull into descriptor we are given.
|
readable_byte_stream_controller_shift_pending_pull_into(controller);
|
||||||
auto pull_into_descriptor_copy = readable_byte_stream_controller_shift_pending_pull_into(controller);
|
|
||||||
|
|
||||||
// 6. Let remainderSize be the remainder after dividing pullIntoDescriptor’s bytes filled by pullIntoDescriptor’s element size.
|
// 6. Let remainderSize be the remainder after dividing pullIntoDescriptor’s bytes filled by pullIntoDescriptor’s element size.
|
||||||
auto remainder_size = pull_into_descriptor_copy.bytes_filled % pull_into_descriptor_copy.element_size;
|
auto remainder_size = pull_into_descriptor.bytes_filled % pull_into_descriptor.element_size;
|
||||||
|
|
||||||
// 7. If remainderSize > 0,
|
// 7. If remainderSize > 0,
|
||||||
if (remainder_size > 0) {
|
if (remainder_size > 0) {
|
||||||
// 1. Let end be pullIntoDescriptor’s byte offset + pullIntoDescriptor’s bytes filled.
|
// 1. Let end be pullIntoDescriptor’s byte offset + pullIntoDescriptor’s bytes filled.
|
||||||
auto end = pull_into_descriptor_copy.byte_offset + pull_into_descriptor_copy.bytes_filled;
|
auto end = pull_into_descriptor.byte_offset + pull_into_descriptor.bytes_filled;
|
||||||
|
|
||||||
// 2. Perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller, pullIntoDescriptor’s buffer, end − remainderSize, remainderSize).
|
// 2. Perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller, pullIntoDescriptor’s buffer, end − remainderSize, remainderSize).
|
||||||
TRY(readable_byte_stream_controller_enqueue_cloned_chunk_to_queue(controller, *pull_into_descriptor_copy.buffer, end - remainder_size, remainder_size));
|
TRY(readable_byte_stream_controller_enqueue_cloned_chunk_to_queue(controller, *pull_into_descriptor.buffer, end - remainder_size, remainder_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Set pullIntoDescriptor’s bytes filled to pullIntoDescriptor’s bytes filled − remainderSize.
|
// 8. Set pullIntoDescriptor’s bytes filled to pullIntoDescriptor’s bytes filled − remainderSize.
|
||||||
pull_into_descriptor_copy.bytes_filled -= remainder_size;
|
pull_into_descriptor.bytes_filled -= remainder_size;
|
||||||
|
|
||||||
// 9. Let filledPullIntos be the result of performing ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
|
// 9. Let filledPullIntos be the result of performing ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
|
||||||
auto filled_pulled_intos = readable_byte_stream_controller_process_pull_into_descriptors_using_queue(controller);
|
auto filled_pulled_intos = readable_byte_stream_controller_process_pull_into_descriptors_using_queue(controller);
|
||||||
|
|
||||||
// 10. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], pullIntoDescriptor).
|
// 10. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], pullIntoDescriptor).
|
||||||
readable_byte_stream_controller_commit_pull_into_descriptor(*controller.stream(), pull_into_descriptor_copy);
|
readable_byte_stream_controller_commit_pull_into_descriptor(*controller.stream(), pull_into_descriptor);
|
||||||
|
|
||||||
// 11. For each filledPullInto of filledPullIntos,
|
// 11. For each filledPullInto of filledPullIntos,
|
||||||
for (auto& filled_pull_into : filled_pulled_intos) {
|
for (auto& filled_pull_into : filled_pulled_intos) {
|
||||||
// 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto).
|
// 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto).
|
||||||
readable_byte_stream_controller_commit_pull_into_descriptor(*controller.stream(), filled_pull_into);
|
readable_byte_stream_controller_commit_pull_into_descriptor(*controller.stream(), *filled_pull_into);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2381,7 +2379,7 @@ void readable_byte_stream_controller_respond_in_closed_state(ReadableByteStreamC
|
||||||
// 4. If ! ReadableStreamHasBYOBReader(stream) is true,
|
// 4. If ! ReadableStreamHasBYOBReader(stream) is true,
|
||||||
if (readable_stream_has_byob_reader(stream)) {
|
if (readable_stream_has_byob_reader(stream)) {
|
||||||
// 1. Let filledPullIntos be a new empty list.
|
// 1. Let filledPullIntos be a new empty list.
|
||||||
SinglyLinkedList<PullIntoDescriptor> filled_pull_intos;
|
SinglyLinkedList<GC::Root<PullIntoDescriptor>> filled_pull_intos;
|
||||||
|
|
||||||
// 2. Let i be 0.
|
// 2. Let i be 0.
|
||||||
u64 i = 0;
|
u64 i = 0;
|
||||||
|
@ -2400,7 +2398,7 @@ void readable_byte_stream_controller_respond_in_closed_state(ReadableByteStreamC
|
||||||
// 4. For each filledPullInto of filledPullIntos,
|
// 4. For each filledPullInto of filledPullIntos,
|
||||||
for (auto& filled_pull_into : filled_pull_intos) {
|
for (auto& filled_pull_into : filled_pull_intos) {
|
||||||
// 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(stream, filledPullInto).
|
// 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(stream, filledPullInto).
|
||||||
readable_byte_stream_controller_commit_pull_into_descriptor(stream, filled_pull_into);
|
readable_byte_stream_controller_commit_pull_into_descriptor(stream, *filled_pull_into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2410,7 +2408,7 @@ void readable_byte_stream_controller_respond_in_closed_state(ReadableByteStreamC
|
||||||
WebIDL::ExceptionOr<void> readable_byte_stream_controller_respond_internal(ReadableByteStreamController& controller, u64 bytes_written)
|
WebIDL::ExceptionOr<void> readable_byte_stream_controller_respond_internal(ReadableByteStreamController& controller, u64 bytes_written)
|
||||||
{
|
{
|
||||||
// 1. Let firstDescriptor be controller.[[pendingPullIntos]][0].
|
// 1. Let firstDescriptor be controller.[[pendingPullIntos]][0].
|
||||||
auto& first_descriptor = controller.pending_pull_intos().first();
|
auto& first_descriptor = *controller.pending_pull_intos().first();
|
||||||
|
|
||||||
// 2. Assert: ! CanTransferArrayBuffer(firstDescriptor’s buffer) is true.
|
// 2. Assert: ! CanTransferArrayBuffer(firstDescriptor’s buffer) is true.
|
||||||
VERIFY(can_transfer_array_buffer(*first_descriptor.buffer));
|
VERIFY(can_transfer_array_buffer(*first_descriptor.buffer));
|
||||||
|
@ -2456,7 +2454,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_respond(ReadableByteSt
|
||||||
VERIFY(!controller.pending_pull_intos().is_empty());
|
VERIFY(!controller.pending_pull_intos().is_empty());
|
||||||
|
|
||||||
// 2. Let firstDescriptor be controller.[[pendingPullIntos]][0].
|
// 2. Let firstDescriptor be controller.[[pendingPullIntos]][0].
|
||||||
auto& first_descriptor = controller.pending_pull_intos().first();
|
auto& first_descriptor = *controller.pending_pull_intos().first();
|
||||||
|
|
||||||
// 3. Let state be controller.[[stream]].[[state]].
|
// 3. Let state be controller.[[stream]].[[state]].
|
||||||
auto state = controller.stream()->state();
|
auto state = controller.stream()->state();
|
||||||
|
@ -2498,7 +2496,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_respond_with_new_view(
|
||||||
VERIFY(!view.viewed_array_buffer()->is_detached());
|
VERIFY(!view.viewed_array_buffer()->is_detached());
|
||||||
|
|
||||||
// 3. Let firstDescriptor be controller.[[pendingPullIntos]][0].
|
// 3. Let firstDescriptor be controller.[[pendingPullIntos]][0].
|
||||||
auto& first_descriptor = controller.pending_pull_intos().first();
|
auto& first_descriptor = *controller.pending_pull_intos().first();
|
||||||
|
|
||||||
// 4. Let state be controller.[[stream]].[[state]].
|
// 4. Let state be controller.[[stream]].[[state]].
|
||||||
auto state = controller.stream()->state();
|
auto state = controller.stream()->state();
|
||||||
|
@ -2809,7 +2807,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_close(ReadableByteStre
|
||||||
// 4. If controller.[[pendingPullIntos]] is not empty,
|
// 4. If controller.[[pendingPullIntos]] is not empty,
|
||||||
if (!controller.pending_pull_intos().is_empty()) {
|
if (!controller.pending_pull_intos().is_empty()) {
|
||||||
// 1. Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
|
// 1. Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
|
||||||
auto& first_pending_pull_into = controller.pending_pull_intos().first();
|
auto& first_pending_pull_into = *controller.pending_pull_intos().first();
|
||||||
|
|
||||||
// 2. If the remainder after dividing firstPendingPullInto’s bytes filled by firstPendingPullInto’s element size is not 0,
|
// 2. If the remainder after dividing firstPendingPullInto’s bytes filled by firstPendingPullInto’s element size is not 0,
|
||||||
if (first_pending_pull_into.bytes_filled % first_pending_pull_into.element_size != 0) {
|
if (first_pending_pull_into.bytes_filled % first_pending_pull_into.element_size != 0) {
|
||||||
|
@ -3312,7 +3310,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_enqueue(ReadableByteSt
|
||||||
// 8. If controller.[[pendingPullIntos]] is not empty,
|
// 8. If controller.[[pendingPullIntos]] is not empty,
|
||||||
if (!controller.pending_pull_intos().is_empty()) {
|
if (!controller.pending_pull_intos().is_empty()) {
|
||||||
// 1. Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
|
// 1. Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
|
||||||
auto& first_pending_pull_into = controller.pending_pull_intos().first();
|
auto& first_pending_pull_into = *controller.pending_pull_intos().first();
|
||||||
|
|
||||||
// 2. If ! IsDetachedBuffer(firstPendingPullInto’s buffer) is true, throw a TypeError exception.
|
// 2. If ! IsDetachedBuffer(firstPendingPullInto’s buffer) is true, throw a TypeError exception.
|
||||||
if (first_pending_pull_into.buffer->is_detached()) {
|
if (first_pending_pull_into.buffer->is_detached()) {
|
||||||
|
@ -3352,7 +3350,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_enqueue(ReadableByteSt
|
||||||
// 2. If controller.[[pendingPullIntos]] is not empty,
|
// 2. If controller.[[pendingPullIntos]] is not empty,
|
||||||
if (!controller.pending_pull_intos().is_empty()) {
|
if (!controller.pending_pull_intos().is_empty()) {
|
||||||
// 1. Assert: controller.[[pendingPullIntos]][0]'s reader type is "default".
|
// 1. Assert: controller.[[pendingPullIntos]][0]'s reader type is "default".
|
||||||
VERIFY(controller.pending_pull_intos().first().reader_type == ReaderType::Default);
|
VERIFY(controller.pending_pull_intos().first()->reader_type == ReaderType::Default);
|
||||||
|
|
||||||
// 2. Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
|
// 2. Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
|
||||||
readable_byte_stream_controller_shift_pending_pull_into(controller);
|
readable_byte_stream_controller_shift_pending_pull_into(controller);
|
||||||
|
@ -3376,7 +3374,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_enqueue(ReadableByteSt
|
||||||
// 3. For each filledPullInto of filledPullIntos,
|
// 3. For each filledPullInto of filledPullIntos,
|
||||||
for (auto& filled_pull_into : filled_pull_intos) {
|
for (auto& filled_pull_into : filled_pull_intos) {
|
||||||
// 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto).
|
// 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto).
|
||||||
readable_byte_stream_controller_commit_pull_into_descriptor(*stream, filled_pull_into);
|
readable_byte_stream_controller_commit_pull_into_descriptor(*stream, *filled_pull_into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 11. Otherwise,
|
// 11. Otherwise,
|
||||||
|
@ -3468,13 +3466,13 @@ void readable_byte_stream_controller_commit_pull_into_descriptor(ReadableStream&
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-process-pull-into-descriptors-using-queue
|
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-process-pull-into-descriptors-using-queue
|
||||||
SinglyLinkedList<PullIntoDescriptor> readable_byte_stream_controller_process_pull_into_descriptors_using_queue(ReadableByteStreamController& controller)
|
SinglyLinkedList<GC::Root<PullIntoDescriptor>> readable_byte_stream_controller_process_pull_into_descriptors_using_queue(ReadableByteStreamController& controller)
|
||||||
{
|
{
|
||||||
// 1. Assert: controller.[[closeRequested]] is false.
|
// 1. Assert: controller.[[closeRequested]] is false.
|
||||||
VERIFY(!controller.close_requested());
|
VERIFY(!controller.close_requested());
|
||||||
|
|
||||||
// 2. Let filledPullIntos be a new empty list.
|
// 2. Let filledPullIntos be a new empty list.
|
||||||
SinglyLinkedList<PullIntoDescriptor> filled_pull_intos;
|
SinglyLinkedList<GC::Root<PullIntoDescriptor>> filled_pull_intos;
|
||||||
|
|
||||||
// 3. While controller.[[pendingPullIntos]] is not empty,
|
// 3. While controller.[[pendingPullIntos]] is not empty,
|
||||||
while (!controller.pending_pull_intos().is_empty()) {
|
while (!controller.pending_pull_intos().is_empty()) {
|
||||||
|
@ -3568,7 +3566,7 @@ WebIDL::ExceptionOr<void> readable_byte_stream_controller_enqueue_cloned_chunk_t
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-shift-pending-pull-into
|
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-shift-pending-pull-into
|
||||||
PullIntoDescriptor readable_byte_stream_controller_shift_pending_pull_into(ReadableByteStreamController& controller)
|
GC::Ref<PullIntoDescriptor> readable_byte_stream_controller_shift_pending_pull_into(ReadableByteStreamController& controller)
|
||||||
{
|
{
|
||||||
// 1. Assert: controller.[[byobRequest]] is null.
|
// 1. Assert: controller.[[byobRequest]] is null.
|
||||||
VERIFY(!controller.raw_byob_request());
|
VERIFY(!controller.raw_byob_request());
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
|
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
|
||||||
* Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
|
* Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org>
|
||||||
* Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
* Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -89,10 +89,10 @@ WebIDL::ExceptionOr<GC::Ref<JS::ArrayBuffer>> transfer_array_buffer(JS::Realm& r
|
||||||
WebIDL::ExceptionOr<void> readable_byte_stream_controller_enqueue_detached_pull_into_queue(ReadableByteStreamController& controller, PullIntoDescriptor& pull_into_descriptor);
|
WebIDL::ExceptionOr<void> readable_byte_stream_controller_enqueue_detached_pull_into_queue(ReadableByteStreamController& controller, PullIntoDescriptor& pull_into_descriptor);
|
||||||
void readable_byte_stream_controller_commit_pull_into_descriptor(ReadableStream&, PullIntoDescriptor const&);
|
void readable_byte_stream_controller_commit_pull_into_descriptor(ReadableStream&, PullIntoDescriptor const&);
|
||||||
void readable_byte_stream_controller_process_read_requests_using_queue(ReadableByteStreamController& controller);
|
void readable_byte_stream_controller_process_read_requests_using_queue(ReadableByteStreamController& controller);
|
||||||
[[nodiscard]] SinglyLinkedList<PullIntoDescriptor> readable_byte_stream_controller_process_pull_into_descriptors_using_queue(ReadableByteStreamController&);
|
[[nodiscard]] SinglyLinkedList<GC::Root<PullIntoDescriptor>> readable_byte_stream_controller_process_pull_into_descriptors_using_queue(ReadableByteStreamController&);
|
||||||
void readable_byte_stream_controller_enqueue_chunk_to_queue(ReadableByteStreamController& controller, GC::Ref<JS::ArrayBuffer> buffer, u32 byte_offset, u32 byte_length);
|
void readable_byte_stream_controller_enqueue_chunk_to_queue(ReadableByteStreamController& controller, GC::Ref<JS::ArrayBuffer> buffer, u32 byte_offset, u32 byte_length);
|
||||||
WebIDL::ExceptionOr<void> readable_byte_stream_controller_enqueue_cloned_chunk_to_queue(ReadableByteStreamController& controller, JS::ArrayBuffer& buffer, u64 byte_offset, u64 byte_length);
|
WebIDL::ExceptionOr<void> readable_byte_stream_controller_enqueue_cloned_chunk_to_queue(ReadableByteStreamController& controller, JS::ArrayBuffer& buffer, u64 byte_offset, u64 byte_length);
|
||||||
PullIntoDescriptor readable_byte_stream_controller_shift_pending_pull_into(ReadableByteStreamController& controller);
|
GC::Ref<PullIntoDescriptor> readable_byte_stream_controller_shift_pending_pull_into(ReadableByteStreamController& controller);
|
||||||
|
|
||||||
void readable_byte_stream_controller_call_pull_if_needed(ReadableByteStreamController&);
|
void readable_byte_stream_controller_call_pull_if_needed(ReadableByteStreamController&);
|
||||||
void readable_byte_stream_controller_clear_algorithms(ReadableByteStreamController&);
|
void readable_byte_stream_controller_clear_algorithms(ReadableByteStreamController&);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
|
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
|
||||||
* Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
|
* Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -17,8 +17,16 @@
|
||||||
|
|
||||||
namespace Web::Streams {
|
namespace Web::Streams {
|
||||||
|
|
||||||
|
GC_DEFINE_ALLOCATOR(PullIntoDescriptor);
|
||||||
GC_DEFINE_ALLOCATOR(ReadableByteStreamController);
|
GC_DEFINE_ALLOCATOR(ReadableByteStreamController);
|
||||||
|
|
||||||
|
void PullIntoDescriptor::visit_edges(Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(buffer);
|
||||||
|
visitor.visit(view_constructor);
|
||||||
|
}
|
||||||
|
|
||||||
// https://streams.spec.whatwg.org/#rbs-controller-desired-size
|
// https://streams.spec.whatwg.org/#rbs-controller-desired-size
|
||||||
Optional<double> ReadableByteStreamController::desired_size() const
|
Optional<double> ReadableByteStreamController::desired_size() const
|
||||||
{
|
{
|
||||||
|
@ -149,20 +157,19 @@ void ReadableByteStreamController::pull_steps(GC::Ref<ReadRequest> read_request)
|
||||||
|
|
||||||
// 3. Let pullIntoDescriptor be a new pull-into descriptor with buffer buffer.[[Value]], buffer byte length autoAllocateChunkSize, byte offset 0,
|
// 3. Let pullIntoDescriptor be a new pull-into descriptor with buffer buffer.[[Value]], buffer byte length autoAllocateChunkSize, byte offset 0,
|
||||||
// byte length autoAllocateChunkSize, bytes filled 0, element size 1, view constructor %Uint8Array%, and reader type "default".
|
// byte length autoAllocateChunkSize, bytes filled 0, element size 1, view constructor %Uint8Array%, and reader type "default".
|
||||||
PullIntoDescriptor pull_into_descriptor {
|
auto pull_into_descriptor = realm.heap().allocate<PullIntoDescriptor>(
|
||||||
.buffer = buffer.release_value(),
|
buffer.release_value(),
|
||||||
.buffer_byte_length = *m_auto_allocate_chunk_size,
|
*m_auto_allocate_chunk_size,
|
||||||
.byte_offset = 0,
|
0,
|
||||||
.byte_length = *m_auto_allocate_chunk_size,
|
*m_auto_allocate_chunk_size,
|
||||||
.bytes_filled = 0,
|
0,
|
||||||
.minimum_fill = 1,
|
1,
|
||||||
.element_size = 1,
|
1,
|
||||||
.view_constructor = *realm.intrinsics().uint8_array_constructor(),
|
*realm.intrinsics().uint8_array_constructor(),
|
||||||
.reader_type = ReaderType::Default,
|
ReaderType::Default);
|
||||||
};
|
|
||||||
|
|
||||||
// 4. Append pullIntoDescriptor to this.[[pendingPullIntos]].
|
// 4. Append pullIntoDescriptor to this.[[pendingPullIntos]].
|
||||||
m_pending_pull_intos.append(move(pull_into_descriptor));
|
m_pending_pull_intos.append(pull_into_descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Perform ! ReadableStreamAddReadRequest(stream, readRequest).
|
// 6. Perform ! ReadableStreamAddReadRequest(stream, readRequest).
|
||||||
|
@ -178,7 +185,7 @@ void ReadableByteStreamController::release_steps()
|
||||||
// 1. If this.[[pendingPullIntos]] is not empty,
|
// 1. If this.[[pendingPullIntos]] is not empty,
|
||||||
if (!m_pending_pull_intos.is_empty()) {
|
if (!m_pending_pull_intos.is_empty()) {
|
||||||
// 1. Let firstPendingPullInto be this.[[pendingPullIntos]][0].
|
// 1. Let firstPendingPullInto be this.[[pendingPullIntos]][0].
|
||||||
auto first_pending_pull_into = m_pending_pull_intos.first();
|
auto& first_pending_pull_into = *m_pending_pull_intos.first();
|
||||||
|
|
||||||
// 2. Set firstPendingPullInto’s reader type to "none".
|
// 2. Set firstPendingPullInto’s reader type to "none".
|
||||||
first_pending_pull_into.reader_type = ReaderType::None;
|
first_pending_pull_into.reader_type = ReaderType::None;
|
||||||
|
@ -193,10 +200,8 @@ void ReadableByteStreamController::visit_edges(Cell::Visitor& visitor)
|
||||||
{
|
{
|
||||||
Base::visit_edges(visitor);
|
Base::visit_edges(visitor);
|
||||||
visitor.visit(m_byob_request);
|
visitor.visit(m_byob_request);
|
||||||
for (auto const& pending_pull_into : m_pending_pull_intos) {
|
for (auto const& pending_pull_into : m_pending_pull_intos)
|
||||||
visitor.visit(pending_pull_into.buffer);
|
visitor.visit(pending_pull_into);
|
||||||
visitor.visit(pending_pull_into.view_constructor);
|
|
||||||
}
|
|
||||||
for (auto const& item : m_queue)
|
for (auto const& item : m_queue)
|
||||||
visitor.visit(item.buffer);
|
visitor.visit(item.buffer);
|
||||||
visitor.visit(m_stream);
|
visitor.visit(m_stream);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
|
* Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
|
||||||
|
* Copyright (c) 2025, Shannon Booth <shannon@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -20,7 +21,10 @@ enum class ReaderType {
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://streams.spec.whatwg.org/#pull-into-descriptor
|
// https://streams.spec.whatwg.org/#pull-into-descriptor
|
||||||
struct PullIntoDescriptor {
|
struct PullIntoDescriptor : public GC::Cell {
|
||||||
|
GC_CELL(PullIntoDescriptor, GC::Cell);
|
||||||
|
GC_DECLARE_ALLOCATOR(PullIntoDescriptor);
|
||||||
|
|
||||||
// https://streams.spec.whatwg.org/#pull-into-descriptor-buffer
|
// https://streams.spec.whatwg.org/#pull-into-descriptor-buffer
|
||||||
// An ArrayBuffer
|
// An ArrayBuffer
|
||||||
GC::Ref<JS::ArrayBuffer> buffer;
|
GC::Ref<JS::ArrayBuffer> buffer;
|
||||||
|
@ -56,6 +60,24 @@ struct PullIntoDescriptor {
|
||||||
// https://streams.spec.whatwg.org/#pull-into-descriptor-reader-type
|
// https://streams.spec.whatwg.org/#pull-into-descriptor-reader-type
|
||||||
// Either "default" or "byob", indicating what type of readable stream reader initiated this request, or "none" if the initiating reader was released
|
// Either "default" or "byob", indicating what type of readable stream reader initiated this request, or "none" if the initiating reader was released
|
||||||
ReaderType reader_type;
|
ReaderType reader_type;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PullIntoDescriptor(GC::Ref<JS::ArrayBuffer> buffer, u64 buffer_byte_length, u64 byte_offset, u64 byte_length, u64 bytes_filled,
|
||||||
|
u64 minimum_fill, u64 element_size, GC::Ref<JS::NativeFunction> view_constructor, ReaderType reader_type)
|
||||||
|
: buffer(buffer)
|
||||||
|
, buffer_byte_length(buffer_byte_length)
|
||||||
|
, byte_offset(byte_offset)
|
||||||
|
, byte_length(byte_length)
|
||||||
|
, bytes_filled(bytes_filled)
|
||||||
|
, minimum_fill(minimum_fill)
|
||||||
|
, element_size(element_size)
|
||||||
|
, view_constructor(view_constructor)
|
||||||
|
, reader_type(reader_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://streams.spec.whatwg.org/#readable-byte-stream-queue-entry
|
// https://streams.spec.whatwg.org/#readable-byte-stream-queue-entry
|
||||||
|
@ -113,8 +135,8 @@ public:
|
||||||
bool pulling() const { return m_pulling; }
|
bool pulling() const { return m_pulling; }
|
||||||
void set_pulling(bool value) { m_pulling = value; }
|
void set_pulling(bool value) { m_pulling = value; }
|
||||||
|
|
||||||
SinglyLinkedList<PullIntoDescriptor>& pending_pull_intos() { return m_pending_pull_intos; }
|
SinglyLinkedList<GC::Ref<PullIntoDescriptor>>& pending_pull_intos() { return m_pending_pull_intos; }
|
||||||
SinglyLinkedList<PullIntoDescriptor> const& pending_pull_intos() const { return m_pending_pull_intos; }
|
SinglyLinkedList<GC::Ref<PullIntoDescriptor>> const& pending_pull_intos() const { return m_pending_pull_intos; }
|
||||||
|
|
||||||
SinglyLinkedList<ReadableByteStreamQueueEntry>& queue() { return m_queue; }
|
SinglyLinkedList<ReadableByteStreamQueueEntry>& queue() { return m_queue; }
|
||||||
|
|
||||||
|
@ -172,7 +194,7 @@ private:
|
||||||
|
|
||||||
// https://streams.spec.whatwg.org/#readablebytestreamcontroller-pendingpullintos
|
// https://streams.spec.whatwg.org/#readablebytestreamcontroller-pendingpullintos
|
||||||
// A list of pull-into descriptors
|
// A list of pull-into descriptors
|
||||||
SinglyLinkedList<PullIntoDescriptor> m_pending_pull_intos;
|
SinglyLinkedList<GC::Ref<PullIntoDescriptor>> m_pending_pull_intos;
|
||||||
|
|
||||||
// https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-queue
|
// https://streams.spec.whatwg.org/#readablestreamdefaultcontroller-queue
|
||||||
// A list representing the stream’s internal queue of chunks
|
// A list representing the stream’s internal queue of chunks
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue