From 92942647d2c08d7b18e237fd9185692e30a2aec3 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Wed, 24 Jun 2020 16:11:53 -0400 Subject: [PATCH 1/7] Pending abort request --- index.bs | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/index.bs b/index.bs index ff6140c27..b20a2ae93 100644 --- a/index.bs +++ b/index.bs @@ -3299,9 +3299,7 @@ table: prevent the {{WritableStreamDefaultWriter/abort()}} method from interrupting close \[[pendingAbortRequest]] - A Record containing the promise returned from - {{WritableStreamDefaultWriter/abort()}} and the reason passed to - {{WritableStreamDefaultWriter/abort()}} + A [=pending abort request=] \[[state]] A string containing the stream's current state, used internally; one of @@ -3329,6 +3327,17 @@ exclusive. Similarly, no element will be removed from \[[writeRequests]] while \[[inFlightWriteRequest]] is not undefined. Implementations can optimize storage for these slots based on these invariants. +A pending abort request is a [=struct=] used to track a request to abort the stream +before that request is finally processed. It has the following [=struct/items=]: + +: promise +:: A promise returned from [$WritableStreamAbort$] +: reason +:: A JavaScript value that was passed as the abort reason to [$WritableStreamAbort$] +: was already erroring +:: A boolean indicating whether or not the stream was in the "`erroring`" state when + [$WritableStreamAbort$] was called, which impacts the outcome of the abort request +

The underlying sink API

The {{WritableStream()}} constructor accepts as its first argument a JavaScript object representing @@ -3949,15 +3958,16 @@ are even meant to be generally useful by other specifications. 1. Let |state| be |stream|.\[[state]]. 1. If |state| is "`closed"` or `"errored`", return [=a promise resolved with=] undefined. 1. If |stream|.\[[pendingAbortRequest]] is not undefined, return - |stream|.\[[pendingAbortRequest]].\[[promise]]. + |stream|.\[[pendingAbortRequest]]'s [=pending abort request/promise=]. 1. Assert: |state| is "`writable"` or `"erroring`". 1. Let |wasAlreadyErroring| be false. 1. If |state| is "`erroring`", 1. Set |wasAlreadyErroring| to true. 1. Set |reason| to undefined. 1. Let |promise| be [=a new promise=]. - 1. Set |stream|.\[[pendingAbortRequest]] to Record {\[[promise]]: |promise|, \[[reason]]: |reason|, - \[[wasAlreadyErroring]]: |wasAlreadyErroring|}. + 1. Set |stream|.\[[pendingAbortRequest]] to a new [=pending abort request=] whose [=pending abort + request/promise=] is |promise|, [=pending abort request/reason=] is |reason|, and [=pending + abort request/was already erroring=] is |wasAlreadyErroring|. 1. If |wasAlreadyErroring| is false, perform ! [$WritableStreamStartErroring$](|stream|, |reason|). 1. Return |promise|. @@ -4064,17 +4074,18 @@ the {{WritableStream}}'s public API. 1. Return. 1. Let |abortRequest| be |stream|.\[[pendingAbortRequest]]. 1. Set |stream|.\[[pendingAbortRequest]] to undefined. - 1. If |abortRequest|.\[[wasAlreadyErroring]] is true, - 1. [=Reject=] |abortRequest|.\[[promise]] with |storedError|. + 1. If |abortRequest|'s [=pending abort request/was already erroring=] is true, + 1. [=Reject=] |abortRequest|'s [=pending abort request/promise=] with |storedError|. 1. Perform ! [$WritableStreamRejectCloseAndClosedPromiseIfNeeded$](|stream|). 1. Return. 1. Let |promise| be ! - stream.\[[writableStreamController]].\[[AbortSteps]](|abortRequest|.\[[reason]]). + stream.\[[writableStreamController]].\[[AbortSteps]](|abortRequest|'s [=pending abort + request/reason=]). 1. [=Upon fulfillment=] of |promise|, - 1. [=Resolve=] |abortRequest|.\[[promise]] with undefined. + 1. [=Resolve=] |abortRequest|'s [=pending abort request/promise=] with undefined. 1. Perform ! [$WritableStreamRejectCloseAndClosedPromiseIfNeeded$](|stream|). 1. [=Upon rejection=] of |promise| with reason |reason|, - 1. [=Reject=] |abortRequest|.\[[promise]] with |reason|. + 1. [=Reject=] |abortRequest|'s [=pending abort request/promise=] with |reason|. 1. Perform ! [$WritableStreamRejectCloseAndClosedPromiseIfNeeded$](|stream|). @@ -4091,7 +4102,8 @@ the {{WritableStream}}'s public API. 1. If |state| is "`erroring`", 1. Set |stream|.\[[storedError]] to undefined. 1. If |stream|.\[[pendingAbortRequest]] is not undefined, - 1. [=Resolve=] |stream|.\[[pendingAbortRequest]].\[[promise]] with undefined. + 1. [=Resolve=] |stream|.\[[pendingAbortRequest]]'s [=pending abort request/promise=] with + undefined. 1. Set |stream|.\[[pendingAbortRequest]] to undefined. 1. Set |stream|.\[[state]] to "`closed`". 1. Let |writer| be |stream|.\[[writer]]. @@ -4110,7 +4122,7 @@ the {{WritableStream}}'s public API. 1. Set |stream|.\[[inFlightCloseRequest]] to undefined. 1. Assert: |stream|.\[[state]] is "`writable"` or `"erroring`". 1. If |stream|.\[[pendingAbortRequest]] is not undefined, - 1. [=Reject=] |stream|.\[[pendingAbortRequest]].\[[promise]] with |error|. + 1. [=Reject=] |stream|.\[[pendingAbortRequest]]'s [=pending abort request/promise=] with |error|. 1. Set |stream|.\[[pendingAbortRequest]] to undefined. 1. Perform ! WritableStreamDealWithRejection(|stream|, |error|). From 3abc393b9ed96751aa120ace5765fdedaec593e6 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Wed, 24 Jun 2020 16:19:37 -0400 Subject: [PATCH 2/7] Special close value --- index.bs | 15 +++++++++------ .../lib/abstract-ops/writable-streams.js | 14 +++++++------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/index.bs b/index.bs index b20a2ae93..4e12028ce 100644 --- a/index.bs +++ b/index.bs @@ -3799,6 +3799,10 @@ the following table: write), which writes data to the [=underlying sink=] +The special close value is a unique value enqueued into \[[queue]], in lieu of a +[=chunk=], to signal that the stream is closed. It only used internally, and is never exposed to web +developers. +

Methods

@@ -4430,11 +4434,11 @@ The following abstract operations support the implementation of the 1. Perform ! [$WritableStreamFinishErroring$](|stream|). 1. Return. 1. If |controller|.\[[queue]] is empty, return. - 1. Let |writeRecord| be ! [$PeekQueueValue$](|controller|). - 1. If |writeRecord| is "`close`", perform ! + 1. Let |value| be ! [$PeekQueueValue$](|controller|). + 1. If |value| is the [=special close value=], perform ! [$WritableStreamDefaultControllerProcessClose$](|controller|). 1. Otherwise, perform ! [$WritableStreamDefaultControllerProcessWrite$](|controller|, - |writeRecord|.\[[chunk]]). + |value|).
@@ -4465,7 +4469,7 @@ The following abstract operations support the implementation of the id="writable-stream-default-controller-close">WritableStreamDefaultControllerClose(|controller|) performs the following steps: - 1. Perform ! [$EnqueueValueWithSize$](|controller|, "`close`", 0). + 1. Perform ! [$EnqueueValueWithSize$](|controller|, [=special close value=], 0). 1. Perform ! [$WritableStreamDefaultControllerAdvanceQueueIfNeeded$](|controller|).
@@ -4566,8 +4570,7 @@ The following abstract operations support the implementation of the id="writable-stream-default-controller-write">WritableStreamDefaultControllerWrite(|controller|, |chunk|, |chunkSize|) performs the following steps: - 1. Let |writeRecord| be Record {\[[chunk]]: |chunk|}. - 1. Let |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |writeRecord|, |chunkSize|). + 1. Let |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|). 1. If |enqueueResult| is an abrupt completion, 1. Perform ! [$WritableStreamDefaultControllerErrorIfNeeded$](|controller|, |enqueueResult|.\[[Value]]). diff --git a/reference-implementation/lib/abstract-ops/writable-streams.js b/reference-implementation/lib/abstract-ops/writable-streams.js index e77cb36d8..966a93c65 100644 --- a/reference-implementation/lib/abstract-ops/writable-streams.js +++ b/reference-implementation/lib/abstract-ops/writable-streams.js @@ -12,6 +12,8 @@ const WritableStream = require('../../generated/WritableStream.js'); const WritableStreamDefaultController = require('../../generated/WritableStreamDefaultController.js'); const WritableStreamDefaultWriter = require('../../generated/WritableStreamDefaultWriter.js'); +const specialCloseValue = Symbol('special close value'); + Object.assign(exports, { AcquireWritableStreamDefaultWriter, CreateWritableStream, @@ -616,11 +618,11 @@ function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) { return; } - const writeRecord = PeekQueueValue(controller); - if (writeRecord === 'close') { + const value = PeekQueueValue(controller); + if (value === specialCloseValue) { WritableStreamDefaultControllerProcessClose(controller); } else { - WritableStreamDefaultControllerProcessWrite(controller, writeRecord.chunk); + WritableStreamDefaultControllerProcessWrite(controller, value); } } @@ -632,7 +634,7 @@ function WritableStreamDefaultControllerClearAlgorithms(controller) { } function WritableStreamDefaultControllerClose(controller) { - EnqueueValueWithSize(controller, 'close', 0); + EnqueueValueWithSize(controller, specialCloseValue, 0); WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); } @@ -723,10 +725,8 @@ function WritableStreamDefaultControllerProcessWrite(controller, chunk) { } function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) { - const writeRecord = { chunk }; - try { - EnqueueValueWithSize(controller, writeRecord, chunkSize); + EnqueueValueWithSize(controller, chunk, chunkSize); } catch (enqueueE) { WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueE); return; From 9e7f363854229e1b60f9300ca7c2eb1b3e4041d2 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Wed, 24 Jun 2020 16:24:56 -0400 Subject: [PATCH 3/7] Queue with sizes --- index.bs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/index.bs b/index.bs index 4e12028ce..a030c2d32 100644 --- a/index.bs +++ b/index.bs @@ -5601,9 +5601,8 @@ and as such are not grouped under the major sections above. The streams in this specification use a "queue-with-sizes" data structure to store queued up values, along with their determined sizes. Various specification objects contain a queue-with-sizes, represented by the object having two paired internal slots, always named -\[[queue]] and \[[queueTotalSize]]. \[[queue]] is a [=list=] of Records with \[[value]] and -\[[size]] fields, and \[[queueTotalSize]] is a JavaScript {{Number}}, i.e. a double-precision -floating point number. +\[[queue]] and \[[queueTotalSize]]. \[[queue]] is a [=list=] of [=value-with-sizes=], and +\[[queueTotalSize]] is a JavaScript {{Number}}, i.e. a double-precision floating point number. The following abstract operations are used when operating on objects that contain queues-with-sizes, in order to ensure that the two internal slots stay synchronized. @@ -5614,18 +5613,22 @@ equivalent to adding up the size of all [=chunks=] in \[[queue]]. (However, this difference when there is a huge (~1015) variance in size between chunks, or when trillions of chunks are enqueued.) +In what follows, a value-with-size is a [=struct=] with the two [=struct/items=] value and size. +
DequeueValue(|container|) performs the following steps: 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots. 1. Assert: |container|.\[[queue]] is not [=list/is empty|empty=]. - 1. Let |pair| be |container|.\[[queue]][0]. - 1. [=list/Remove=] |pair| from |container|.\[[queue]]. - 1. Set |container|.\[[queueTotalSize]] to |container|.\[[queueTotalSize]] − |pair|.\[[size]]. + 1. Let |valueWithSize| be |container|.\[[queue]][0]. + 1. [=list/Remove=] |valueWithSize| from |container|.\[[queue]]. + 1. Set |container|.\[[queueTotalSize]] to |container|.\[[queueTotalSize]] − |valueWithSize|'s + [=value-with-size/size=]. 1. If |container|.\[[queueTotalSize]] < 0, set |container|.\[[queueTotalSize]] to 0. (This can occur due to rounding errors.) - 1. Return |pair|.\[[value]]. + 1. Return |valueWithSize|'s [=value-with-size/value=].
@@ -5636,7 +5639,8 @@ trillions of chunks are enqueued.) 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots. 1. If ! [$IsNonNegativeNumber$](|size|) is false, throw a {{RangeError}} exception. 1. If |size| is +∞, throw a {{RangeError}} exception. - 1. [=list/Append=] Record {\[[value]]: |value|, \[[size]]: |size|} to |container|.\[[queue]]. + 1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |value| and + [=value-with-size/size=] |size| to |container|.\[[queue]]. 1. Set |container|.\[[queueTotalSize]] to |container|.\[[queueTotalSize]] + |size|.
@@ -5646,8 +5650,8 @@ trillions of chunks are enqueued.) 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots. 1. Assert: |container|.\[[queue]] is not [=list/is empty|empty=]. - 1. Let |pair| be |container|.\[[queue]][0]. - 1. Return |pair|.\[[value]]. + 1. Let |valueWithSize| be |container|.\[[queue]][0]. + 1. Return |valueWithSize|'s [=value-with-size/value=].
From d71ac85fbb21498d6fc3e9ddacc9e4375e232a8b Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Wed, 24 Jun 2020 16:54:03 -0400 Subject: [PATCH 4/7] pull-into descriptors --- index.bs | 146 +++++++++++------- .../lib/ReadableByteStreamController-impl.js | 2 +- .../lib/abstract-ops/readable-streams.js | 4 +- 3 files changed, 96 insertions(+), 56 deletions(-) diff --git a/index.bs b/index.bs index a030c2d32..d680180ea 100644 --- a/index.bs +++ b/index.bs @@ -1502,7 +1502,7 @@ following table: calls \[[pendingPullIntos]] - A [=list=] of descriptors representing pending BYOB pull requests + A [=list=] of [=pull-into descriptors=] \[[queue]] A [=list=] representing the stream's internal queue of [=chunks=] @@ -1530,6 +1530,31 @@ following table:

This might be cleaned up in a future spec refactoring.

+A pull-into descriptor is a [=struct=] used to represent pending BYOB pull requests. It +has the following [=struct/items=]: + +: buffer +:: An {{ArrayBuffer}} +: byte offset +:: A nonnegative integer byte offset into the [=pull-into descriptor/buffer=] where the + [=underlying byte source=] will start writing +: byte length +:: A nonnegative integer number of bytes which can be written into the [=pull-into + descriptor/buffer=] +: bytes filled +:: A nonnegative integer number of bytes that have been written into the [=pull-into + descriptor/buffer=] so far +: element size +:: A positive integer representing the number of bytes that can be written into the [=pull-into + descriptor/buffer=] at a time, using views of the type described by the [=pull-into + descriptor/view constructor=] +: view constructor +:: A [=the typed array constructors table|typed array constructor=] or {{%DataView%}}, which will be + used for constructing a view with which to write into the [=pull-into descriptor/buffer=] +: reader type +:: Either "`default`" or "`byob`", indicating what type of [=readable stream reader=] initiated this + request +

Methods and properties

@@ -1568,9 +1593,10 @@ following table: 1. If [=this=].\[[byobRequest]] is null and [=this=].\[[pendingPullIntos]] is not [=list/is empty|empty=], 1. Let |firstDescriptor| be [=this=].\[[pendingPullIntos]][0]. - 1. Let |view| be ! [$Construct$]({{%Uint8Array%}}, « |firstDescriptor|.\[[buffer]], - |firstDescriptor|.\[[byteOffset]] + |firstDescriptor|.\[[bytesFilled]], - |firstDescriptor|.\[[byteLength]] − |firstDescriptor|.\[[bytesFilled]] »). + 1. Let |view| be ! [$Construct$]({{%Uint8Array%}}, « |firstDescriptor|'s [=pull-into + descriptor/buffer=], |firstDescriptor|'s [=pull-into descriptor/byte offset=] + + |firstDescriptor|'s [=pull-into descriptor/bytes filled=], |firstDescriptor|'s [=pull-into + descriptor/byte length=] − |firstDescriptor|'s [=pull-into descriptor/bytes filled=] »). 1. Let |byobRequest| be a [=new=] {{ReadableStreamBYOBRequest}}. 1. Set |byobRequest|.\[[controller]] to [=this=]. 1. Set |byobRequest|.\[[view]] to |view|. @@ -1628,7 +1654,7 @@ counterparts for default controllers, as discussed in [[#rs-abstract-ops-used-by 1. If [=this=].\[[pendingPullIntos]] is not [=list/is empty|empty=], 1. Let |firstDescriptor| be [=this=].\[[pendingPullIntos]][0]. - 1. Set |firstDescriptor|.\[[bytesFilled]] to 0. + 1. Set |firstDescriptor|'s [=pull-into descriptor/bytes filled=] to 0. 1. Perform ! [$ResetQueue$]([=this=]). 1. Let |result| be the result of performing [=this=].\[[cancelAlgorithm]], passing in |reason|. 1. Perform ! [$ReadableByteStreamControllerClearAlgorithms$]([=this=]). @@ -1656,9 +1682,11 @@ counterparts for default controllers, as discussed in [[#rs-abstract-ops-used-by 1. If |autoAllocateChunkSize| is not undefined, 1. Let |buffer| be [$Construct$]({{%ArrayBuffer%}}, « |autoAllocateChunkSize| »). 1. If |buffer| is an abrupt completion, return [=a promise rejected with=] |buffer|.\[[Value]]. - 1. Let |pullIntoDescriptor| be Record {\[[buffer]]: |buffer|.\[[Value]], \[[byteOffset]]: 0, - \[[byteLength]]: |autoAllocateChunkSize|, \[[bytesFilled]]: 0, \[[elementSize]]: 1, \[[ctor]]: - {{%Uint8Array%}}, \[[readerType]]: "`default`"}. + 1. Let |pullIntoDescriptor| be a new [=pull-into descriptor=] with [=pull-into descriptor/buffer=] + |buffer|.\[[Value]], [=pull-into descriptor/byte offset=] 0, [=pull-into descriptor/byte + length=] |autoAllocateChunkSize|, [=pull-into descriptor/bytes filled=] 0, [=pull-into + descriptor/element size=] 1, [=pull-into descriptor/view constructor=] {{%Uint8Array%}}, and + [=pull-into descriptor/reader type=] "`default`". 1. [=list/Append=] |pullIntoDescriptor| to [=this=].\[[pendingPullIntos]]. 1. Let |promise| be ! [$ReadableStreamAddReadRequest$](|stream|). 1. Perform ! [$ReadableByteStreamControllerCallPullIfNeeded$]([=this=]). @@ -2695,7 +2723,7 @@ The following abstract operations support the implementation of the 1. Return. 1. If |controller|.\[[pendingPullIntos]] is not empty, 1. Let |firstPendingPullInto| be |controller|.\[[pendingPullIntos]][0]. - 1. If |firstPendingPullInto|.\[[bytesFilled]] > 0, + 1. If |firstPendingPullInto|'s [=pull-into descriptor/bytes filled=] > 0, 1. Let |e| be a new {{TypeError}} exception. 1. Perform ! [$ReadableByteStreamControllerError$](|controller|, |e|). 1. Throw |e|. @@ -2711,14 +2739,14 @@ The following abstract operations support the implementation of the 1. Assert: |stream|.\[[state]] is not "`errored`". 1. Let |done| be false. 1. If |stream|.\[[state]] is "`closed`", - 1. Assert: |pullIntoDescriptor|.\[[bytesFilled]] is 0. + 1. Assert: |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] is 0. 1. Set |done| to true. 1. Let |filledView| be ! [$ReadableByteStreamControllerConvertPullIntoDescriptor$](|pullIntoDescriptor|). - 1. If |pullIntoDescriptor|.\[[readerType]] is "`default`", + 1. If |pullIntoDescriptor|'s [=pull-into descriptor/reader type=] is "`default`", 1. Perform ! [$ReadableStreamFulfillReadRequest$](|stream|, |filledView|, |done|). 1. Otherwise, - 1. Assert: |pullIntoDescriptor|.\[[readerType]] is "`byob`". + 1. Assert: |pullIntoDescriptor|'s [=pull-into descriptor/reader type=] is "`byob`". 1. Perform ! [$ReadableStreamFulfillReadIntoRequest$](|stream|, |filledView|, |done|). @@ -2727,12 +2755,13 @@ The following abstract operations support the implementation of the id="readable-byte-stream-controller-convert-pull-into-descriptor">ReadableByteStreamControllerConvertPullIntoDescriptor(|pullIntoDescriptor|) performs the following steps: - 1. Let |bytesFilled| be |pullIntoDescriptor|.\[[bytesFilled]]. - 1. Let |elementSize| be |pullIntoDescriptor|.\[[elementSize]]. - 1. Assert: |bytesFilled| ≤ |pullIntoDescriptor|.\[[byteLength]]. + 1. Let |bytesFilled| be |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=]. + 1. Let |elementSize| be |pullIntoDescriptor|'s [=pull-into descriptor/element size=]. + 1. Assert: |bytesFilled| ≤ |pullIntoDescriptor|'s [=pull-into descriptor/byte length=]. 1. Assert: |bytesFilled| mod |elementSize| is 0. - 1. Return ! [$Construct$](|pullIntoDescriptor|.\[[ctor]], « |pullIntoDescriptor|.\[[buffer]], - |pullIntoDescriptor|.\[[byteOffset]], |bytesFilled| ÷ |elementSize| »). + 1. Return ! [$Construct$](|pullIntoDescriptor|'s [=pull-into descriptor/view constructor=], « + |pullIntoDescriptor|'s [=pull-into descriptor/buffer=], |pullIntoDescriptor|'s [=pull-into + descriptor/byte offset=], |bytesFilled| ÷ |elementSize| »).
@@ -2797,7 +2826,8 @@ The following abstract operations support the implementation of the 1. Assert: either |controller|.\[[pendingPullIntos]] [=list/is empty=], or |controller|.\[[pendingPullIntos]][0] is |pullIntoDescriptor|. 1. Perform ! [$ReadableByteStreamControllerInvalidateBYOBRequest$](|controller|). - 1. Set |pullIntoDescriptor|.\[[bytesFilled]] to |pullIntoDescriptor|.\[[bytesFilled]] + |size|. + 1. Set |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] to [=pull-into + descriptor/bytes filled=] + |size|.
@@ -2806,26 +2836,28 @@ The following abstract operations support the implementation of the |pullIntoDescriptor|) performs the following steps: 1. Let |elementSize| be |pullIntoDescriptor|.\[[elementSize]]. - 1. Let |currentAlignedBytes| be |pullIntoDescriptor|.\[[bytesFilled]] − - (|pullIntoDescriptor|.\[[bytesFilled]] mod |elementSize|). - 1. Let |maxBytesToCopy| be min(|controller|.\[[queueTotalSize]], - |pullIntoDescriptor|.\[[byteLength]] − |pullIntoDescriptor|.\[[bytesFilled]]). - 1. Let |maxBytesFilled| be |pullIntoDescriptor|.\[[bytesFilled]] + |maxBytesToCopy|. + 1. Let |currentAlignedBytes| be |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] − + (|pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] mod |elementSize|). + 1. Let |maxBytesToCopy| be min(|controller|.\[[queueTotalSize]], |pullIntoDescriptor|'s [=pull-into + descriptor/byte length=] − |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=]). + 1. Let |maxBytesFilled| be |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] + + |maxBytesToCopy|. 1. Let |maxAlignedBytes| be |maxBytesFilled| − (|maxBytesFilled| mod |elementSize|). 1. Let |totalBytesToCopyRemaining| be |maxBytesToCopy|. 1. Let |ready| be false. 1. If |maxAlignedBytes| > |currentAlignedBytes|, - 1. Set |totalBytesToCopyRemaining| to |maxAlignedBytes| − |pullIntoDescriptor|.\[[bytesFilled]]. + 1. Set |totalBytesToCopyRemaining| to |maxAlignedBytes| − |pullIntoDescriptor|'s [=pull-into + descriptor/bytes filled=]. 1. Set |ready| to true. 1. Let |queue| be |controller|.\[[queue]]. 1. [=While=] |totalBytesToCopyRemaining| > 0, 1. Let |headOfQueue| be |queue|[0]. 1. Let |bytesToCopy| be min(|totalBytesToCopyRemaining|, |headOfQueue|.\[[byteLength]]). - 1. Let |destStart| be |pullIntoDescriptor|.\[[byteOffset]] + - |pullIntoDescriptor|.\[[bytesFilled]]. - 1. Perform ! [$CopyDataBlockBytes$](|pullIntoDescriptor|.\[[buffer]].\[[ArrayBufferData]], - |destStart|, |headOfQueue|.\[[buffer]].\[[ArrayBufferData]], |headOfQueue|.\[[byteOffset]], - |bytesToCopy|). + 1. Let |destStart| be |pullIntoDescriptor|'s [=pull-into descriptor/byte offset=] + + |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=]. + 1. Perform ! [$CopyDataBlockBytes$](|pullIntoDescriptor|'s [=pull-into + descriptor/buffer=].\[[ArrayBufferData]], |destStart|, + |headOfQueue|.\[[buffer]].\[[ArrayBufferData]], |headOfQueue|.\[[byteOffset]], |bytesToCopy|). 1. If |headOfQueue|.\[[byteLength]] is |bytesToCopy|, 1. [=list/Remove=] |queue|[0]. 1. Otherwise, @@ -2837,8 +2869,9 @@ The following abstract operations support the implementation of the 1. Set |totalBytesToCopyRemaining| to |totalBytesToCopyRemaining| − |bytesToCopy|. 1. If |ready| is false, 1. Assert: |controller|.\[[queueTotalSize]] is 0. - 1. Assert: |pullIntoDescriptor|.\[[bytesFilled]] > 0. - 1. Assert: |pullIntoDescriptor|.\[[bytesFilled]] < |pullIntoDescriptor|.\[[elementSize]]. + 1. Assert: |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] > 0. + 1. Assert: |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] < + |pullIntoDescriptor|'s [=pull-into descriptor/element size=]. 1. Return |ready|.
@@ -2911,15 +2944,17 @@ The following abstract operations support the implementation of the 1. Let |byteOffset| be |view|.\[[ByteOffset]]. 1. Let |byteLength| be |view|.\[[ByteLength]]. 1. Let |buffer| be ! [$TransferArrayBuffer$](|view|.\[[ViewedArrayBuffer]]). - 1. Let |pullIntoDescriptor| be Record {\[[buffer]]: |buffer|, \[[byteOffset]]: |byteOffset|, - \[[byteLength]]: |byteLength|, \[[bytesFilled]]: 0, \[[elementSize]]: |elementSize|, - \[[ctor]]: |ctor|, \[[readerType]]: "`byob`"}. + 1. Let |pullIntoDescriptor| be a new [=pull-into descriptor=] with [=pull-into descriptor/buffer=] + |buffer|, [=pull-into descriptor/byte offset=] |byteOffset|, [=pull-into descriptor/byte + length=] |byteLength|, [=pull-into descriptor/bytes filled=] 0, [=pull-into descriptor/element + size=] |elementSize|, [=pull-into descriptor/view constructor=] |ctor|, and [=pull-into + descriptor/reader type=] "`byob`". 1. If |controller|.\[[pendingPullIntos]] is not empty, 1. [=list/Append=] |pullIntoDescriptor| to |controller|.\[[pendingPullIntos]]. 1. Return ! [$ReadableStreamAddReadIntoRequest$](|stream|). 1. If |stream|.\[[state]] is "`closed`", - 1. Let |emptyView| be ! [$Construct$](|ctor|, « |pullIntoDescriptor|.\[[buffer]], - |pullIntoDescriptor|.\[[byteOffset]], 0 »). + 1. Let |emptyView| be ! [$Construct$](|ctor|, « |pullIntoDescriptor|'s [=pull-into + descriptor/buffer=], |pullIntoDescriptor|'s [=pull-into descriptor/byte offset=], 0 »). 1. Return [=a promise resolved with=] ! [$ReadableStreamCreateReadResult$](|emptyView|, true, |stream|.\[[reader]].\[[forAuthorCode]]). 1. If |controller|.\[[queueTotalSize]] > 0, @@ -2954,8 +2989,9 @@ The following abstract operations support the implementation of the id="readable-byte-stream-controller-respond-in-closed-state">ReadableByteStreamControllerRespondInClosedState(|controller|, |firstDescriptor|) performs the following steps: - 1. Set |firstDescriptor|.\[[buffer]] to ! [$TransferArrayBuffer$](|firstDescriptor|.\[[buffer]]). - 1. Assert: |firstDescriptor|.\[[bytesFilled]] is 0. + 1. Set |firstDescriptor|'s [=pull-into descriptor/buffer=] to ! + [$TransferArrayBuffer$](|firstDescriptor|'s [=pull-into descriptor/buffer=]). + 1. Assert: |firstDescriptor|'s [=pull-into descriptor/bytes filled=] is 0. 1. Let |stream| be |controller|.\[[controlledReadableStream]]. 1. If ! [$ReadableStreamHasBYOBReader$](|stream|) is true, 1. [=While=] ! [$ReadableStreamGetNumReadIntoRequests$](|stream|) > 0, @@ -2970,22 +3006,26 @@ The following abstract operations support the implementation of the id="readable-byte-stream-controller-respond-in-readable-state">ReadableByteStreamControllerRespondInReadableState(|controller|, |bytesWritten|, |pullIntoDescriptor|) performs the following steps: - 1. If |pullIntoDescriptor|.\[[bytesFilled]] + |bytesWritten| > - |pullIntoDescriptor|.\[[byteLength]], throw a {{RangeError}} exception. + 1. If |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] + |bytesWritten| > + |pullIntoDescriptor|'s [=pull-into descriptor/byte length=], throw a {{RangeError}} exception. 1. Perform ! [$ReadableByteStreamControllerFillHeadPullIntoDescriptor$](|controller|, |bytesWritten|, |pullIntoDescriptor|). - 1. If |pullIntoDescriptor|.\[[bytesFilled]] < |pullIntoDescriptor|.\[[elementSize]], return. + 1. If |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] < |pullIntoDescriptor|'s + [=pull-into descriptor/element size=], return. 1. Perform ! [$ReadableByteStreamControllerShiftPendingPullInto$](|controller|). - 1. Let |remainderSize| be |pullIntoDescriptor|.\[[bytesFilled]] mod - |pullIntoDescriptor|.\[[elementSize]]. + 1. Let |remainderSize| be |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] mod + |pullIntoDescriptor|'s [=pull-into descriptor/element size=]. 1. If |remainderSize| > 0, - 1. Let |end| be |pullIntoDescriptor|.\[[byteOffset]] + |pullIntoDescriptor|.\[[bytesFilled]]. - 1. Let |remainder| be ? [$CloneArrayBuffer$](|pullIntoDescriptor|.\[[buffer]], |end| − - |remainderSize|, |remainderSize|, {{%ArrayBuffer%}}). + 1. Let |end| be |pullIntoDescriptor|'s [=pull-into descriptor/byte offset=] + + |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=]. + 1. Let |remainder| be ? [$CloneArrayBuffer$](|pullIntoDescriptor|'s [=pull-into + descriptor/buffer=], |end| − |remainderSize|, |remainderSize|, {{%ArrayBuffer%}}). 1. Perform ! [$ReadableByteStreamControllerEnqueueChunkToQueue$](|controller|, |remainder|, 0, |remainder|.\[[ByteLength]]). - 1. Set |pullIntoDescriptor|.\[[buffer]] to ! [$TransferArrayBuffer$](|pullIntoDescriptor|.\[[buffer]]). - 1. Set |pullIntoDescriptor|.\[[bytesFilled]] to |pullIntoDescriptor|.\[[bytesFilled]] − |remainderSize|. + 1. Set |pullIntoDescriptor|'s [=pull-into descriptor/buffer=] to ! + [$TransferArrayBuffer$](|pullIntoDescriptor|'s [=pull-into descriptor/buffer=]). + 1. Set |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=] to |pullIntoDescriptor|'s + [=pull-into descriptor/bytes filled=] − |remainderSize|. 1. Perform ! [$ReadableByteStreamControllerCommitPullIntoDescriptor$](|controller|.\[[controlledReadableStream]], |pullIntoDescriptor|). @@ -3017,11 +3057,11 @@ The following abstract operations support the implementation of the 1. Assert: |controller|.\[[pendingPullIntos]] is not [=list/is empty|empty=]. 1. Let |firstDescriptor| be |controller|.\[[pendingPullIntos]][0]. - 1. If |firstDescriptor|.\[[byteOffset]] + |firstDescriptor|.\[[bytesFilled]] is not - |view|.\[[ByteOffset]], throw a {{RangeError}} exception. - 1. If |firstDescriptor|.\[[byteLength]] is not |view|.\[[ByteLength]], throw a {{RangeError}} - exception. - 1. Set |firstDescriptor|.\[[buffer]] to |view|.\[[ViewedArrayBuffer]]. + 1. If |firstDescriptor|'s [=pull-into descriptor/byte offset=] + |firstDescriptor|' [=pull-into + descriptor/bytes filled=] is not |view|.\[[ByteOffset]], throw a {{RangeError}} exception. + 1. If |firstDescriptor|'s [=pull-into descriptor/byte length=] is not |view|.\[[ByteLength]], throw + a {{RangeError}} exception. + 1. Set |firstDescriptor|'s [=pull-into descriptor/buffer=] to |view|.\[[ViewedArrayBuffer]]. 1. Perform ? [$ReadableByteStreamControllerRespondInternal$](|controller|, |view|.\[[ByteLength]]). diff --git a/reference-implementation/lib/ReadableByteStreamController-impl.js b/reference-implementation/lib/ReadableByteStreamController-impl.js index 3a6c52db8..d7e5184a8 100644 --- a/reference-implementation/lib/ReadableByteStreamController-impl.js +++ b/reference-implementation/lib/ReadableByteStreamController-impl.js @@ -111,7 +111,7 @@ exports.implementation = class ReadableByteStreamControllerImpl { byteLength: autoAllocateChunkSize, bytesFilled: 0, elementSize: 1, - ctor: Uint8Array, + viewConstructor: Uint8Array, readerType: 'default' }; diff --git a/reference-implementation/lib/abstract-ops/readable-streams.js b/reference-implementation/lib/abstract-ops/readable-streams.js index 9959abe97..78c55d5ec 100644 --- a/reference-implementation/lib/abstract-ops/readable-streams.js +++ b/reference-implementation/lib/abstract-ops/readable-streams.js @@ -990,7 +990,7 @@ function ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescripto assert(bytesFilled <= pullIntoDescriptor.byteLength); assert(bytesFilled % elementSize === 0); - return new pullIntoDescriptor.ctor( + return new pullIntoDescriptor.viewConstructor( pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, bytesFilled / elementSize); } @@ -1175,7 +1175,7 @@ function ReadableByteStreamControllerPullInto(controller, view) { byteLength: view.byteLength, bytesFilled: 0, elementSize, - ctor, + viewConstructor: ctor, readerType: 'byob' }; From 7bb9e30060ac0e1b9920b58336286208ab2e103f Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Wed, 24 Jun 2020 17:05:52 -0400 Subject: [PATCH 5/7] Readable byte stream queue entries --- index.bs | 51 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/index.bs b/index.bs index d680180ea..02ed17368 100644 --- a/index.bs +++ b/index.bs @@ -33,7 +33,6 @@ urlPrefix: https://tc39.es/ecma262/; spec: ECMASCRIPT text: abstract operation; url: #sec-algorithm-conventions-abstract-operations text: completion record; url: #sec-completion-record-specification-type text: internal slot; url: #sec-object-internal-methods-and-internal-slots - text: record; url: #sec-list-and-record-specification-type text: the current Realm; url: #current-realm text: the typed array constructors table; url: #table-49 text: typed array; url: #sec-typedarray-objects @@ -335,9 +334,9 @@ This specification uses the [=abstract operation=] concept from the JavaScript s internal algorithms. This includes treating their return values as [=completion records=], and the use of ! and ? prefixes for unwrapping those completion records. [[!ECMASCRIPT]] -This specification also uses the [=internal slot=] and [=record=] concepts and notation from the -JavaScript specification. (Although, the internal slots are on Web IDL [=platform objects=] instead -of on JavaScript objects.) +This specification also uses the [=internal slot=] concept and notation from the JavaScript +specification. (Although, the internal slots are on Web IDL [=platform objects=] instead of on +JavaScript objects.)

The reasons for the usage of these foreign JavaScript specification conventions are largely historical. We urge you to avoid following our example when writing your own web @@ -1505,7 +1504,8 @@ following table: A [=list=] of [=pull-into descriptors=] \[[queue]] - A [=list=] representing the stream's internal queue of [=chunks=] + A [=list=] of [=readable byte stream queue entry|readable byte stream + queue entries=] representing the stream's internal queue of [=chunks=] \[[queueTotalSize]] The total size, in bytes, of all the chunks stored in \[[queue]] (see @@ -1530,6 +1530,20 @@ following table:

This might be cleaned up in a future spec refactoring. +A readable byte stream queue entry is a [=struct=] encapsulating the important aspects of +a [=chunk=] for the specific case of [=readable byte streams=]. It has the following +[=struct/items=]: + +: buffer +:: An {{ArrayBuffer}}, which will be a transferred version of + the one originally supplied by the [=underlying byte source=] +: byte offset +:: A nonnegative integer number giving the byte offset derived from the view originally supplied by + the [=underlying byte source=] +: byte length +:: A nonnegative integer number giving the byte length derived from the view originally supplied by + the [=underlying byte source=] + A pull-into descriptor is a [=struct=] used to represent pending BYOB pull requests. It has the following [=struct/items=]: @@ -1672,10 +1686,12 @@ counterparts for default controllers, as discussed in [[#rs-abstract-ops-used-by 1. Assert: ! [$ReadableStreamGetNumReadRequests$](|stream|) is 0. 1. Let |entry| be [=this=].\[[queue]][0]. 1. [=list/Remove=] |entry| from [=this=].\[[queue]]. - 1. Set [=this=].\[[queueTotalSize]] to [=this=].\[[queueTotalSize]] − |entry|.\[[byteLength]]. + 1. Set [=this=].\[[queueTotalSize]] to [=this=].\[[queueTotalSize]] − |entry|'s [=readable byte + stream queue entry/byte length=]. 1. Perform ! [$ReadableByteStreamControllerHandleQueueDrain$]([=this=]). - 1. Let |view| be ! [$Construct$]({{%Uint8Array%}}, « |entry|.\[[buffer]], |entry|.\[[byteOffset]], - |entry|.\[[byteLength]] »). + 1. Let |view| be ! [$Construct$]({{%Uint8Array%}}, « |entry|'s [=readable byte stream queue + entry/buffer=], |entry|'s [=readable byte stream queue entry/byte offset=], |entry|'s + [=readable byte stream queue entry/byte length=] »). 1. Return [=a promise resolved with=] ! [$ReadableStreamCreateReadResult$](|view|, false, |stream|.\[[reader]].\[[forAuthorCode]]). 1. Let |autoAllocateChunkSize| be [=this=].\[[autoAllocateChunkSize]]. @@ -2800,8 +2816,9 @@ The following abstract operations support the implementation of the id="readable-byte-stream-controller-enqueue-chunk-to-queue">ReadableByteStreamControllerEnqueueChunkToQueue(|controller|, |buffer|, |byteOffset|, |byteLength|) performs the following steps: - 1. [=list/Append=] Record {\[[buffer]]: |buffer|, \[[byteOffset]]: |byteOffset|, \[[byteLength]]: - |byteLength|} to |controller|.\[[queue]]. + 1. [=list/Append=] a new [=readable byte stream queue entry=] with [=readable byte stream queue + entry/buffer=] |buffer|, [=readable byte stream queue entry/byte offset=] |byteOffset|, and + [=readable byte stream queue entry/byte length=] |byteLength| to |controller|.\[[queue]]. 1. Set |controller|.\[[queueTotalSize]] to |controller|.\[[queueTotalSize]] + |byteLength|. @@ -2852,17 +2869,21 @@ The following abstract operations support the implementation of the 1. Let |queue| be |controller|.\[[queue]]. 1. [=While=] |totalBytesToCopyRemaining| > 0, 1. Let |headOfQueue| be |queue|[0]. - 1. Let |bytesToCopy| be min(|totalBytesToCopyRemaining|, |headOfQueue|.\[[byteLength]]). + 1. Let |bytesToCopy| be min(|totalBytesToCopyRemaining|, |headOfQueue|'s [=readable byte stream + queue entry/byte length=]). 1. Let |destStart| be |pullIntoDescriptor|'s [=pull-into descriptor/byte offset=] + |pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=]. 1. Perform ! [$CopyDataBlockBytes$](|pullIntoDescriptor|'s [=pull-into descriptor/buffer=].\[[ArrayBufferData]], |destStart|, - |headOfQueue|.\[[buffer]].\[[ArrayBufferData]], |headOfQueue|.\[[byteOffset]], |bytesToCopy|). - 1. If |headOfQueue|.\[[byteLength]] is |bytesToCopy|, + |headOfQueue|'s [=readable byte stream queue entry/buffer=].\[[ArrayBufferData]], + |headOfQueue|'s [=readable byte stream queue entry/byte offset=], |bytesToCopy|). + 1. If |headOfQueue|'s [=readable byte stream queue entry/byte length=] is |bytesToCopy|, 1. [=list/Remove=] |queue|[0]. 1. Otherwise, - 1. Set |headOfQueue|.\[[byteOffset]] to |headOfQueue|.\[[byteOffset]] + |bytesToCopy|. - 1. Set |headOfQueue|.\[[byteLength]] to |headOfQueue|.\[[byteLength]] − |bytesToCopy|. + 1. Set |headOfQueue|'s [=readable byte stream queue entry/byte offset=] to |headOfQueue|'s + [=readable byte stream queue entry/byte offset=] + |bytesToCopy|. + 1. Set |headOfQueue|'s [=readable byte stream queue entry/byte length=] to |headOfQueue|'s + [=readable byte stream queue entry/byte length=] − |bytesToCopy|. 1. Set |controller|.\[[queueTotalSize]] to |controller|.\[[queueTotalSize]] − |bytesToCopy|. 1. Perform ! [$ReadableByteStreamControllerFillHeadPullIntoDescriptor$](|controller|, |bytesToCopy|, |pullIntoDescriptor|). From f83596241328243310110ce7e52c68e8f62c49f7 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Thu, 25 Jun 2020 11:16:17 -0400 Subject: [PATCH 6/7] Missing "is" Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 02ed17368..96fa75c2c 100644 --- a/index.bs +++ b/index.bs @@ -3861,7 +3861,7 @@ the following table: The special close value is a unique value enqueued into \[[queue]], in lieu of a -[=chunk=], to signal that the stream is closed. It only used internally, and is never exposed to web +[=chunk=], to signal that the stream is closed. It is only used internally, and is never exposed to web developers.

Methods

From fb4e77ff08ce72601e84131a10ace11822aa7e3b Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Thu, 25 Jun 2020 11:17:46 -0400 Subject: [PATCH 7/7] close sentinel!! --- index.bs | 8 ++++---- .../lib/abstract-ops/writable-streams.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/index.bs b/index.bs index 96fa75c2c..1f1d7fc64 100644 --- a/index.bs +++ b/index.bs @@ -3860,8 +3860,8 @@ the following table: write), which writes data to the [=underlying sink=] -The special close value is a unique value enqueued into \[[queue]], in lieu of a -[=chunk=], to signal that the stream is closed. It is only used internally, and is never exposed to web +The close sentinel is a unique value enqueued into \[[queue]], in lieu of a [=chunk=], to +signal that the stream is closed. It is only used internally, and is never exposed to web developers.

Methods

@@ -4496,7 +4496,7 @@ The following abstract operations support the implementation of the 1. Return. 1. If |controller|.\[[queue]] is empty, return. 1. Let |value| be ! [$PeekQueueValue$](|controller|). - 1. If |value| is the [=special close value=], perform ! + 1. If |value| is the [=close sentinel=], perform ! [$WritableStreamDefaultControllerProcessClose$](|controller|). 1. Otherwise, perform ! [$WritableStreamDefaultControllerProcessWrite$](|controller|, |value|). @@ -4530,7 +4530,7 @@ The following abstract operations support the implementation of the id="writable-stream-default-controller-close">WritableStreamDefaultControllerClose(|controller|) performs the following steps: - 1. Perform ! [$EnqueueValueWithSize$](|controller|, [=special close value=], 0). + 1. Perform ! [$EnqueueValueWithSize$](|controller|, [=close sentinel=], 0). 1. Perform ! [$WritableStreamDefaultControllerAdvanceQueueIfNeeded$](|controller|). diff --git a/reference-implementation/lib/abstract-ops/writable-streams.js b/reference-implementation/lib/abstract-ops/writable-streams.js index 966a93c65..01829edb4 100644 --- a/reference-implementation/lib/abstract-ops/writable-streams.js +++ b/reference-implementation/lib/abstract-ops/writable-streams.js @@ -12,7 +12,7 @@ const WritableStream = require('../../generated/WritableStream.js'); const WritableStreamDefaultController = require('../../generated/WritableStreamDefaultController.js'); const WritableStreamDefaultWriter = require('../../generated/WritableStreamDefaultWriter.js'); -const specialCloseValue = Symbol('special close value'); +const closeSentinel = Symbol('close sentinel'); Object.assign(exports, { AcquireWritableStreamDefaultWriter, @@ -619,7 +619,7 @@ function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) { } const value = PeekQueueValue(controller); - if (value === specialCloseValue) { + if (value === closeSentinel) { WritableStreamDefaultControllerProcessClose(controller); } else { WritableStreamDefaultControllerProcessWrite(controller, value); @@ -634,7 +634,7 @@ function WritableStreamDefaultControllerClearAlgorithms(controller) { } function WritableStreamDefaultControllerClose(controller) { - EnqueueValueWithSize(controller, specialCloseValue, 0); + EnqueueValueWithSize(controller, closeSentinel, 0); WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); }