From 9d67644847bb477b043e704cb5eec911dcf9d994 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Sun, 9 Apr 2023 13:58:50 +0200 Subject: [PATCH 01/36] Add support for a new ReadableStream "owning" type. --- index.bs | 101 ++++++++++++------ .../lib/ReadableStream-impl.js | 2 +- .../ReadableStreamDefaultController-impl.js | 5 +- .../ReadableStreamDefaultController.webidl | 6 +- .../lib/UnderlyingSource.webidl | 2 +- .../lib/abstract-ops/miscellaneous.js | 4 + .../lib/abstract-ops/queue-with-sizes.js | 20 +++- .../lib/abstract-ops/readable-streams.js | 32 ++++-- .../lib/abstract-ops/transform-streams.js | 2 +- .../lib/abstract-ops/writable-streams.js | 4 +- .../run-web-platform-tests.js | 2 +- 11 files changed, 127 insertions(+), 53 deletions(-) diff --git a/index.bs b/index.bs index 10162f30c..7035d3943 100644 --- a/index.bs +++ b/index.bs @@ -19,6 +19,7 @@ spec:infra; type:dfn; text:list spec:html; type:dfn; text:entangle spec:html; type:dfn; text:message port post message steps spec:html; type:dfn; text:port message queue +spec:html; type:dfn; text:transferable objects
@@ -571,7 +572,7 @@ callback UnderlyingSourceStartCallback = any (ReadableStreamController controlle
 callback UnderlyingSourcePullCallback = Promise (ReadableStreamController controller);
 callback UnderlyingSourceCancelCallback = Promise (optional any reason);
 
-enum ReadableStreamType { "bytes" };
+enum ReadableStreamType { "bytes", "owning" };
 
 
 
@@ -639,8 +640,7 @@ enum ReadableStreamType { "bytes" }; something more persistently wrong. -
type (byte streams - only)
+
type

Can be set to "bytes" to signal that the constructed {{ReadableStream}} is a readable byte stream. This ensures that the resulting @@ -651,8 +651,16 @@ enum ReadableStreamType { "bytes" };

For an example of how to set up a readable byte stream, including using the different controller interface, see [[#example-rbs-push]]. -

Setting any value other than "{{ReadableStreamType/bytes}}" or undefined will cause the - {{ReadableStream()}} constructor to throw an exception. +

Can be set to "owning" to signal that the + constructed {{ReadableStream}} will own chunks (via transfer or serialization) before enqueuing them. + This ensures that enqueued chunks are not mutable by the source. + Transferred or serialized chunks may have closing steps which are executed if + enqueued chunks are dequeued without being provided to the application, for instance when + a {{ReadableStream}} is errored. +

+ +

Setting any value other than "{{ReadableStreamType/bytes}}", "{{ReadableStreamType/owning}}" + or undefined will cause the {{ReadableStream()}} constructor to throw an exception.

autoAllocateChunkSize (byte streams only)
@@ -801,7 +809,8 @@ option. If {{UnderlyingSource/type}} is set to undefined (including via omission 1. Perform ? [$SetUpReadableByteStreamControllerFromUnderlyingSource$]([=this=], |underlyingSource|, |underlyingSourceDict|, |highWaterMark|). 1. Otherwise, - 1. Assert: |underlyingSourceDict|["{{UnderlyingSource/type}}"] does not [=map/exist=]. + 1. Assert: |underlyingSourceDict|["{{UnderlyingSource/type}}"] does not [=map/exist=] or + is "{{ReadableStreamType/owning}}". 1. Let |sizeAlgorithm| be ! [$ExtractSizeAlgorithm$](|strategy|). 1. Let |highWaterMark| be ? [$ExtractHighWaterMark$](|strategy|, 1). 1. Perform ? [$SetUpReadableStreamDefaultControllerFromUnderlyingSource$]([=this=], @@ -1461,7 +1470,7 @@ interface ReadableStreamDefaultController { readonly attribute unrestricted double? desiredSize; undefined close(); - undefined enqueue(optional any chunk); + undefined enqueue(optional any chunk, optional StructuredSerializeOptions options = { }); undefined error(optional any e); }; @@ -1523,6 +1532,10 @@ the following table: \[[stream]] The {{ReadableStream}} instance controlled + + \[[isOwning]] + A boolean flag indicating whether to take ownership of enqueued chunks + via transfer or serialization.

Methods and properties

@@ -1569,11 +1582,12 @@ the following table:
The enqueue(|chunk|) method steps are: + for="ReadableStreamDefaultController">enqueue(|chunk|, |options|) method steps are: + 1. Let |transferList| be |options|["transfer"]. 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$]([=this=]) is false, throw a {{TypeError}} exception. - 1. Perform ? [$ReadableStreamDefaultControllerEnqueue$]([=this=], |chunk|). + 1. Perform ? [$ReadableStreamDefaultControllerEnqueue$]([=this=], |chunk|, |transferList|).
@@ -2235,8 +2249,8 @@ create them does not matter. objects|transferring=] their [=chunks=]. However, it does introduce a noticeable asymmetry between the two branches, and limits the possible [=chunks=] to serializable ones. [[!HTML]] - If |stream| is a [=readable byte stream=], then |cloneForBranch2| is ignored and chunks are cloned - unconditionally. + If |stream| is a [=readable byte stream=], or if |stream| type is "{{ReadableStreamType/owning}}", + then |cloneForBranch2| is ignored and chunks are cloned unconditionally.

In this standard ReadableStreamTee is always called with |cloneForBranch2| set to false; other specifications pass true via the [=ReadableStream/tee=] wrapper algorithm. @@ -2247,6 +2261,8 @@ create them does not matter. 1. Assert: |cloneForBranch2| is a boolean. 1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}}, return ? [$ReadableByteStreamTee$](|stream|). + 1. If |stream|.[=ReadableStream/[[controller]]=].[=ReadableStreamDefaultController/[[isOwning]]=] + is true, return ? [$ReadableStreamDefaultTee$](|stream|, true). 1. Return ? [$ReadableStreamDefaultTee$](|stream|, |cloneForBranch2|).

@@ -2287,10 +2303,10 @@ create them does not matter. 1. Otherwise, set |chunk2| to |cloneResult|.\[[Value]]. 1. If |canceled1| is false, perform ! [$ReadableStreamDefaultControllerEnqueue$](|branch1|.[=ReadableStream/[[controller]]=], - |chunk1|). + |chunk1|, undefined). 1. If |canceled2| is false, perform ! [$ReadableStreamDefaultControllerEnqueue$](|branch2|.[=ReadableStream/[[controller]]=], - |chunk2|). + |chunk2|, undefined). 1. Set |reading| to false. 1. If |readAgain| is true, perform |pullAlgorithm|. @@ -2950,13 +2966,19 @@ The following abstract operations support the implementation of the
ReadableStreamDefaultControllerEnqueue(|controller|, - |chunk|) performs the following steps: + |chunk|, |transferList|) performs the following steps: 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$](|controller|) is false, return. 1. Let |stream| be |controller|.[=ReadableStreamDefaultController/[[stream]]=]. 1. If ! [$IsReadableStreamLocked$](|stream|) is true and ! - [$ReadableStreamGetNumReadRequests$](|stream|) > 0, perform ! - [$ReadableStreamFulfillReadRequest$](|stream|, |chunk|, false). + [$ReadableStreamGetNumReadRequests$](|stream|) > 0, perform the following steps: + 1. Let |internalChunk| be |chunk|. + 1. If |controller|.[=ReadableStreamDefaultController/[[isOwning]]=] is true, perform the following steps: + 1. Set |internalChunk| to [$StructuredTransferOrClone$](|chunk|, |transferList|). + 1. If |internalChunk| is an abrupt completion, + 1. Perform ! [$ReadableStreamDefaultControllerError$](|controller|, |internalChunk|.\[[Value]]). + 1. Return |internalChunk|. + 1. Perform ! [$ReadableStreamFulfillReadRequest$](|stream|, |internalChunk|, false). 1. Otherwise, 1. Let |result| be the result of performing |controller|.[=ReadableStreamDefaultController/[[strategySizeAlgorithm]]=], passing in |chunk|, @@ -2965,7 +2987,7 @@ The following abstract operations support the implementation of the 1. Perform ! [$ReadableStreamDefaultControllerError$](|controller|, |result|.\[[Value]]). 1. Return |result|. 1. Let |chunkSize| be |result|.\[[Value]]. - 1. Let |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|). + 1. Let |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|, |transferList|). 1. If |enqueueResult| is an abrupt completion, 1. Perform ! [$ReadableStreamDefaultControllerError$](|controller|, |enqueueResult|.\[[Value]]). 1. Return |enqueueResult|. @@ -3029,7 +3051,7 @@ The following abstract operations support the implementation of the SetUpReadableStreamDefaultController(|stream|, |controller|, |startAlgorithm|, |pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|, - |sizeAlgorithm|) performs the following steps: + |sizeAlgorithm|, |isOwning|) performs the following steps: 1. Assert: |stream|.[=ReadableStream/[[controller]]=] is undefined. 1. Set |controller|.[=ReadableStreamDefaultController/[[stream]]=] to |stream|. @@ -3039,8 +3061,9 @@ The following abstract operations support the implementation of the |controller|.[=ReadableStreamDefaultController/[[pullAgain]]=], and |controller|.[=ReadableStreamDefaultController/[[pulling]]=] to false. 1. Set |controller|.[=ReadableStreamDefaultController/[[strategySizeAlgorithm]]=] to - |sizeAlgorithm| and |controller|.[=ReadableStreamDefaultController/[[strategyHWM]]=] to - |highWaterMark|. + |sizeAlgorithm|, |controller|.[=ReadableStreamDefaultController/[[strategyHWM]]=] to + |highWaterMark| and |controller|.[=ReadableStreamDefaultController/[[isOwning]]=] to + |isOwning|. 1. Set |controller|.[=ReadableStreamDefaultController/[[pullAlgorithm]]=] to |pullAlgorithm|. 1. Set |controller|.[=ReadableStreamDefaultController/[[cancelAlgorithm]]=] to |cancelAlgorithm|. 1. Set |stream|.[=ReadableStream/[[controller]]=] to |controller|. @@ -3065,6 +3088,8 @@ The following abstract operations support the implementation of the 1. Let |startAlgorithm| be an algorithm that returns undefined. 1. Let |pullAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined. 1. Let |cancelAlgorithm| be an algorithm that returns [=a promise resolved with=] undefined. + 1. Let |isOwning| be true if |underlyingSourceDict|["{{UnderlyingSource/type}}"] is + "{{ReadableStreamType/owning}}" and false otherwise. 1. If |underlyingSourceDict|["{{UnderlyingSource/start}}"] [=map/exists=], then set |startAlgorithm| to an algorithm which returns the result of [=invoking=] |underlyingSourceDict|["{{UnderlyingSource/start}}"] with argument list @@ -3078,7 +3103,7 @@ The following abstract operations support the implementation of the [=invoking=] |underlyingSourceDict|["{{UnderlyingSource/cancel}}"] with argument list « |reason| » and [=callback this value=] |underlyingSource|. 1. Perform ? [$SetUpReadableStreamDefaultController$](|stream|, |controller|, |startAlgorithm|, - |pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|, |sizeAlgorithm|). + |pullAlgorithm|, |cancelAlgorithm|, |highWaterMark|, |sizeAlgorithm|, |isOwning|).

Byte stream controllers

@@ -5188,7 +5213,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 sentinel=], 0). + 1. Perform ! [$EnqueueValueWithSize$](|controller|, [=close sentinel=], 0, undefined). 1. Perform ! [$WritableStreamDefaultControllerAdvanceQueueIfNeeded$](|controller|). @@ -5292,7 +5317,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 |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|). + 1. Let |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|, undefined). 1. If |enqueueResult| is an abrupt completion, 1. Perform ! [$WritableStreamDefaultControllerErrorIfNeeded$](|controller|, |enqueueResult|.\[[Value]]). @@ -5886,7 +5911,7 @@ The following abstract operations support the implementaiton of the 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$](|readableController|) is false, throw a {{TypeError}} exception. 1. Let |enqueueResult| be [$ReadableStreamDefaultControllerEnqueue$](|readableController|, - |chunk|). + |chunk|, undefined). 1. If |enqueueResult| is an abrupt completion, 1. Perform ! [$TransformStreamErrorWritableAndUnblockWrite$](|stream|, |enqueueResult|.\[[Value]]). @@ -6364,13 +6389,17 @@ for="value-with-size">value and size.
EnqueueValueWithSize(|container|, |value|, |size|) performs the + id="enqueue-value-with-size">EnqueueValueWithSize(|container|, |value|, |size|, |transferList|) performs the following steps: 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=] a new [=value-with-size=] with [=value-with-size/value=] |value| and + 1. Let |enqueuedValue| be |value|. + 1. If |container| has a \[[isOwning]] internal slot whose value is true, perform the following steps: + 1. Set |enqueuedValue| to [$StructuredTransferOrClone$](|value|, |transferList|). + 1. If |enqueuedValue| is an abrupt completion, return |enqueuedValue|. + 1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |enqueuedValue| and [=value-with-size/size=] |size| to |container|.\[[queue]]. 1. Set |container|.\[[queueTotalSize]] to |container|.\[[queueTotalSize]] + |size|.
@@ -6390,6 +6419,10 @@ for="value-with-size">value and size. performs the following steps: 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots. + 1. If |container| has a \[[isOwning]] internal slot whose value is true, perform the following steps until |container|.\[[queue]] + is [=list/is empty|empty=]: + 1. Let |chunk| be ! [$DequeueValue$]([=this=]). + 1. If |chunk| has [=closing steps=], perform the [=closing steps=] given |chunk|. 1. Set |container|.\[[queue]] to a new empty [=list=]. 1. Set |container|.\[[queueTotalSize]] to 0. @@ -6449,7 +6482,7 @@ abstract operations are used to implement these "cross-realm transforms". 1. Let |value| be ! [$Get$](|data|, "`value`"). 1. Assert: [$Type$](|type|) is String. 1. If |type| is "`chunk`", - 1. Perform ! [$ReadableStreamDefaultControllerEnqueue$](|controller|, |value|). + 1. Perform ! [$ReadableStreamDefaultControllerEnqueue$](|controller|, |value|, undefined). 1. Otherwise, if |type| is "`close`", 1. Perform ! [$ReadableStreamDefaultControllerClose$](|controller|). 1. Disentangle |port|. @@ -6598,6 +6631,14 @@ The following abstract operations are a grab-bag of utilities. 1. Return ? [$StructuredDeserialize$](|serialized|, [=the current Realm=]). +
+ StructuredTransferOrClone(|value|, |transferList|) + performs the following steps: + 1. Let |serialized| be ! [$StructuredSerializeWithTransfer$](|value|, |transferList|). + 1. Let |deserialized| be ! [$StructuredDeserializeWithTransfer$](|serialized|, [=the current Realm=]). + 1. return |deserialized|.\[[Deserialized]]. +
+

Using streams in other specifications

Much of this standard concerns itself with the internal machinery of streams. Other specifications @@ -6729,13 +6770,13 @@ mark=] is greater than zero.
- To enqueue the JavaScript value |chunk| into a - {{ReadableStream}} |stream|: + To enqueue the JavaScript value |chunk| + with an optional |transferList| into a {{ReadableStream}} |stream|: 1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableStreamDefaultController}}, 1. Perform ! [$ReadableStreamDefaultControllerEnqueue$](|stream|.[=ReadableStream/[[controller]]=], - |chunk|). + |chunk|, |transferList|). 1. Otherwise, 1. Assert: |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableByteStreamController}}. diff --git a/reference-implementation/lib/ReadableStream-impl.js b/reference-implementation/lib/ReadableStream-impl.js index 1eda283e1..47bde08b7 100644 --- a/reference-implementation/lib/ReadableStream-impl.js +++ b/reference-implementation/lib/ReadableStream-impl.js @@ -29,7 +29,7 @@ exports.implementation = class ReadableStreamImpl { this, underlyingSource, underlyingSourceDict, highWaterMark ); } else { - assert(!('type' in underlyingSourceDict)); + assert(!('type' in underlyingSourceDict) || underlyingSourceDict.type === 'owning'); const sizeAlgorithm = ExtractSizeAlgorithm(strategy); const highWaterMark = ExtractHighWaterMark(strategy, 1); aos.SetUpReadableStreamDefaultControllerFromUnderlyingSource( diff --git a/reference-implementation/lib/ReadableStreamDefaultController-impl.js b/reference-implementation/lib/ReadableStreamDefaultController-impl.js index 5c7ec7033..d29faf89b 100644 --- a/reference-implementation/lib/ReadableStreamDefaultController-impl.js +++ b/reference-implementation/lib/ReadableStreamDefaultController-impl.js @@ -17,12 +17,13 @@ exports.implementation = class ReadableStreamDefaultControllerImpl { aos.ReadableStreamDefaultControllerClose(this); } - enqueue(chunk) { + enqueue(chunk, options) { + const transferList = options ? options.transfer : undefined; if (aos.ReadableStreamDefaultControllerCanCloseOrEnqueue(this) === false) { throw new TypeError('The stream is not in a state that permits enqueue'); } - return aos.ReadableStreamDefaultControllerEnqueue(this, chunk); + return aos.ReadableStreamDefaultControllerEnqueue(this, chunk, transferList); } error(e) { diff --git a/reference-implementation/lib/ReadableStreamDefaultController.webidl b/reference-implementation/lib/ReadableStreamDefaultController.webidl index aeea7249f..dc02cfdcb 100644 --- a/reference-implementation/lib/ReadableStreamDefaultController.webidl +++ b/reference-implementation/lib/ReadableStreamDefaultController.webidl @@ -1,8 +1,12 @@ +dictionary StructuredSerializeOptions { + sequence transfer = []; +}; + [Exposed=(Window,Worker,Worklet)] interface ReadableStreamDefaultController { readonly attribute unrestricted double? desiredSize; undefined close(); - undefined enqueue(optional any chunk); + undefined enqueue(optional any chunk, optional StructuredSerializeOptions options = { }); undefined error(optional any e); }; diff --git a/reference-implementation/lib/UnderlyingSource.webidl b/reference-implementation/lib/UnderlyingSource.webidl index 7a0047638..b8aa75b70 100644 --- a/reference-implementation/lib/UnderlyingSource.webidl +++ b/reference-implementation/lib/UnderlyingSource.webidl @@ -12,4 +12,4 @@ callback UnderlyingSourceStartCallback = any (ReadableStreamController controlle callback UnderlyingSourcePullCallback = Promise (ReadableStreamController controller); callback UnderlyingSourceCancelCallback = Promise (optional any reason); -enum ReadableStreamType { "bytes" }; +enum ReadableStreamType { "bytes", "owning" }; diff --git a/reference-implementation/lib/abstract-ops/miscellaneous.js b/reference-implementation/lib/abstract-ops/miscellaneous.js index 08589a740..ed55a5268 100644 --- a/reference-implementation/lib/abstract-ops/miscellaneous.js +++ b/reference-implementation/lib/abstract-ops/miscellaneous.js @@ -20,3 +20,7 @@ exports.CloneAsUint8Array = O => { const buffer = O.buffer.slice(O.byteOffset, O.byteOffset + O.byteLength); return new Uint8Array(buffer); }; + +exports.StructuredTransferOrClone = (value, transferList) => { + return globalThis.structuredClone(value, { transfer: transferList }); +}; diff --git a/reference-implementation/lib/abstract-ops/queue-with-sizes.js b/reference-implementation/lib/abstract-ops/queue-with-sizes.js index 22086caa5..8580f8021 100644 --- a/reference-implementation/lib/abstract-ops/queue-with-sizes.js +++ b/reference-implementation/lib/abstract-ops/queue-with-sizes.js @@ -1,6 +1,6 @@ 'use strict'; const assert = require('assert'); -const { IsNonNegativeNumber } = require('./miscellaneous.js'); +const { IsNonNegativeNumber, StructuredTransferOrClone } = require('./miscellaneous.js'); exports.DequeueValue = container => { assert('_queue' in container && '_queueTotalSize' in container); @@ -15,7 +15,7 @@ exports.DequeueValue = container => { return pair.value; }; -exports.EnqueueValueWithSize = (container, value, size) => { +exports.EnqueueValueWithSize = (container, value, size, transferList) => { assert('_queue' in container && '_queueTotalSize' in container); if (!IsNonNegativeNumber(size)) { @@ -24,7 +24,9 @@ exports.EnqueueValueWithSize = (container, value, size) => { if (size === Infinity) { throw new RangeError('Size must be a finite, non-NaN, non-negative number.'); } - + if (container.isOwning) { + value = StructuredTransferOrClone(value, transferList); + } container._queue.push({ value, size }); container._queueTotalSize += size; }; @@ -40,6 +42,18 @@ exports.PeekQueueValue = container => { exports.ResetQueue = container => { assert('_queue' in container && '_queueTotalSize' in container); + if (container.isOwning) { + while (container._queue.length > 0) { + const value = exports.DequeueValue(container); + if (typeof value.close === 'function') { + try { + value.close(); + } catch (closeException) { + // Nothing to do. + } + } + } + } container._queue = []; container._queueTotalSize = 0; }; diff --git a/reference-implementation/lib/abstract-ops/readable-streams.js b/reference-implementation/lib/abstract-ops/readable-streams.js index db1da4c73..9080724ae 100644 --- a/reference-implementation/lib/abstract-ops/readable-streams.js +++ b/reference-implementation/lib/abstract-ops/readable-streams.js @@ -6,7 +6,7 @@ const { promiseResolvedWith, promiseRejectedWith, newPromise, resolvePromise, re require('../helpers/webidl.js'); const { CanTransferArrayBuffer, CopyDataBlockBytes, CreateArrayFromList, IsDetachedBuffer, TransferArrayBuffer } = require('./ecmascript.js'); -const { CloneAsUint8Array, IsNonNegativeNumber } = require('./miscellaneous.js'); +const { CloneAsUint8Array, IsNonNegativeNumber, StructuredTransferOrClone } = require('./miscellaneous.js'); const { EnqueueValueWithSize, ResetQueue } = require('./queue-with-sizes.js'); const { AcquireWritableStreamDefaultWriter, IsWritableStreamLocked, WritableStreamAbort, WritableStreamDefaultWriterCloseWithErrorPropagation, WritableStreamDefaultWriterRelease, @@ -89,7 +89,7 @@ function CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, hi const controller = ReadableStreamDefaultController.new(globalThis); SetUpReadableStreamDefaultController( - stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm + stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm, false ); return stream; @@ -340,7 +340,7 @@ function ReadableStreamTee(stream, cloneForBranch2) { if (ReadableByteStreamController.isImpl(stream._controller)) { return ReadableByteStreamTee(stream); } - return ReadableStreamDefaultTee(stream, cloneForBranch2); + return ReadableStreamDefaultTee(stream, stream._controller.isOwning ? true : cloneForBranch2); } function ReadableStreamDefaultTee(stream, cloneForBranch2) { @@ -392,10 +392,10 @@ function ReadableStreamDefaultTee(stream, cloneForBranch2) { // } if (canceled1 === false) { - ReadableStreamDefaultControllerEnqueue(branch1._controller, chunk1); + ReadableStreamDefaultControllerEnqueue(branch1._controller, chunk1, undefined); } if (canceled2 === false) { - ReadableStreamDefaultControllerEnqueue(branch2._controller, chunk2); + ReadableStreamDefaultControllerEnqueue(branch2._controller, chunk2, undefined); } reading = false; @@ -1074,7 +1074,7 @@ function ReadableStreamDefaultControllerClose(controller) { } } -function ReadableStreamDefaultControllerEnqueue(controller, chunk) { +function ReadableStreamDefaultControllerEnqueue(controller, chunk, transferList) { if (ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) === false) { return; } @@ -1082,6 +1082,14 @@ function ReadableStreamDefaultControllerEnqueue(controller, chunk) { const stream = controller._stream; if (IsReadableStreamLocked(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) { + if (controller.isOwning) { + try { + chunk = StructuredTransferOrClone(chunk, transferList); + } catch (chunkCloneError) { + ReadableStreamDefaultControllerError(controller, chunkCloneError); + throw chunkCloneError; + } + } ReadableStreamFulfillReadRequest(stream, chunk, false); } else { let chunkSize; @@ -1093,7 +1101,7 @@ function ReadableStreamDefaultControllerEnqueue(controller, chunk) { } try { - EnqueueValueWithSize(controller, chunk, chunkSize); + EnqueueValueWithSize(controller, chunk, chunkSize, transferList); } catch (enqueueE) { ReadableStreamDefaultControllerError(controller, enqueueE); throw enqueueE; @@ -1148,7 +1156,7 @@ function ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) { } function SetUpReadableStreamDefaultController( - stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm) { + stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm, isOwning) { assert(stream._controller === undefined); controller._stream = stream; @@ -1169,6 +1177,8 @@ function SetUpReadableStreamDefaultController( controller._pullAlgorithm = pullAlgorithm; controller._cancelAlgorithm = cancelAlgorithm; + controller.isOwning = isOwning; + stream._controller = controller; const startResult = startAlgorithm(); @@ -1195,7 +1205,7 @@ function SetUpReadableStreamDefaultControllerFromUnderlyingSource( let startAlgorithm = () => undefined; let pullAlgorithm = () => promiseResolvedWith(undefined); let cancelAlgorithm = () => promiseResolvedWith(undefined); - + const isOwning = underlyingSourceDict.type === 'owning'; if ('start' in underlyingSourceDict) { startAlgorithm = () => underlyingSourceDict.start.call(underlyingSource, controller); } @@ -1207,8 +1217,8 @@ function SetUpReadableStreamDefaultControllerFromUnderlyingSource( } SetUpReadableStreamDefaultController( - stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm - ); + stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm, + isOwning); } // Byte stream controllers diff --git a/reference-implementation/lib/abstract-ops/transform-streams.js b/reference-implementation/lib/abstract-ops/transform-streams.js index 8e3f5fcc3..1152d19de 100644 --- a/reference-implementation/lib/abstract-ops/transform-streams.js +++ b/reference-implementation/lib/abstract-ops/transform-streams.js @@ -155,7 +155,7 @@ function TransformStreamDefaultControllerEnqueue(controller, chunk) { // accept TransformStreamDefaultControllerEnqueue() calls. try { - ReadableStreamDefaultControllerEnqueue(readableController, chunk); + ReadableStreamDefaultControllerEnqueue(readableController, chunk, undefined); } catch (e) { // This happens when readableStrategy.size() throws. TransformStreamErrorWritableAndUnblockWrite(stream, e); diff --git a/reference-implementation/lib/abstract-ops/writable-streams.js b/reference-implementation/lib/abstract-ops/writable-streams.js index cf303bfe7..88eef0888 100644 --- a/reference-implementation/lib/abstract-ops/writable-streams.js +++ b/reference-implementation/lib/abstract-ops/writable-streams.js @@ -637,7 +637,7 @@ function WritableStreamDefaultControllerClearAlgorithms(controller) { } function WritableStreamDefaultControllerClose(controller) { - EnqueueValueWithSize(controller, closeSentinel, 0); + EnqueueValueWithSize(controller, closeSentinel, 0, undefined); WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); } @@ -729,7 +729,7 @@ function WritableStreamDefaultControllerProcessWrite(controller, chunk) { function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) { try { - EnqueueValueWithSize(controller, chunk, chunkSize); + EnqueueValueWithSize(controller, chunk, chunkSize, undefined); } catch (enqueueE) { WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueE); return; diff --git a/reference-implementation/run-web-platform-tests.js b/reference-implementation/run-web-platform-tests.js index 0f685159e..5cc9021af 100644 --- a/reference-implementation/run-web-platform-tests.js +++ b/reference-implementation/run-web-platform-tests.js @@ -39,7 +39,6 @@ async function main() { 'readable-byte-streams/non-transferable-buffers.any.html', 'readable-streams/owning-type-message-port.any.html', // disabled due to MessagePort use. 'readable-streams/owning-type-video-frame.any.html', // disabled due to VideoFrame use. - 'readable-streams/owning-type.any.html', // FIXME: reenable this test once owning type PR lands. 'transferable/transform-stream-members.any.html' // FIXME: reenable if structuredClone is aligned. ]; const anyTestPattern = /\.any\.html$/; @@ -64,6 +63,7 @@ async function main() { } }; }; + window.structuredClone = globalThis.structuredClone; window.eval(bundledJS); }, filter(testPath) { From e05ba576f279f196baa3e5c65760082f5c937fb6 Mon Sep 17 00:00:00 2001 From: youennf Date: Fri, 14 Apr 2023 14:43:00 +0200 Subject: [PATCH 02/36] Update index.bs Co-authored-by: Adam Rice --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 7035d3943..74ac5b661 100644 --- a/index.bs +++ b/index.bs @@ -6636,7 +6636,7 @@ The following abstract operations are a grab-bag of utilities. performs the following steps: 1. Let |serialized| be ! [$StructuredSerializeWithTransfer$](|value|, |transferList|). 1. Let |deserialized| be ! [$StructuredDeserializeWithTransfer$](|serialized|, [=the current Realm=]). - 1. return |deserialized|.\[[Deserialized]]. + 1. Return |deserialized|.\[[Deserialized]].

Using streams in other specifications

From 6ef5db9e0b3809e6f0bdafb2318f9e06e271af1f Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Fri, 14 Apr 2023 14:47:08 +0200 Subject: [PATCH 03/36] Use _isOwning private slot --- .../lib/abstract-ops/queue-with-sizes.js | 4 ++-- .../lib/abstract-ops/readable-streams.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/reference-implementation/lib/abstract-ops/queue-with-sizes.js b/reference-implementation/lib/abstract-ops/queue-with-sizes.js index 8580f8021..4cc668e57 100644 --- a/reference-implementation/lib/abstract-ops/queue-with-sizes.js +++ b/reference-implementation/lib/abstract-ops/queue-with-sizes.js @@ -24,7 +24,7 @@ exports.EnqueueValueWithSize = (container, value, size, transferList) => { if (size === Infinity) { throw new RangeError('Size must be a finite, non-NaN, non-negative number.'); } - if (container.isOwning) { + if (container._isOwning) { value = StructuredTransferOrClone(value, transferList); } container._queue.push({ value, size }); @@ -42,7 +42,7 @@ exports.PeekQueueValue = container => { exports.ResetQueue = container => { assert('_queue' in container && '_queueTotalSize' in container); - if (container.isOwning) { + if (container._isOwning) { while (container._queue.length > 0) { const value = exports.DequeueValue(container); if (typeof value.close === 'function') { diff --git a/reference-implementation/lib/abstract-ops/readable-streams.js b/reference-implementation/lib/abstract-ops/readable-streams.js index 9080724ae..98b0a35fb 100644 --- a/reference-implementation/lib/abstract-ops/readable-streams.js +++ b/reference-implementation/lib/abstract-ops/readable-streams.js @@ -340,7 +340,7 @@ function ReadableStreamTee(stream, cloneForBranch2) { if (ReadableByteStreamController.isImpl(stream._controller)) { return ReadableByteStreamTee(stream); } - return ReadableStreamDefaultTee(stream, stream._controller.isOwning ? true : cloneForBranch2); + return ReadableStreamDefaultTee(stream, stream._controller._isOwning ? true : cloneForBranch2); } function ReadableStreamDefaultTee(stream, cloneForBranch2) { @@ -1082,7 +1082,7 @@ function ReadableStreamDefaultControllerEnqueue(controller, chunk, transferList) const stream = controller._stream; if (IsReadableStreamLocked(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) { - if (controller.isOwning) { + if (controller._isOwning) { try { chunk = StructuredTransferOrClone(chunk, transferList); } catch (chunkCloneError) { @@ -1177,7 +1177,7 @@ function SetUpReadableStreamDefaultController( controller._pullAlgorithm = pullAlgorithm; controller._cancelAlgorithm = cancelAlgorithm; - controller.isOwning = isOwning; + controller._isOwning = isOwning; stream._controller = controller; From 13da7339a33c961f2bd21031e046e7bd1e7c29c3 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Mon, 17 Apr 2023 18:46:25 +0200 Subject: [PATCH 04/36] Ensure transferList is empty for non owning readable streams --- index.bs | 2 ++ .../lib/ReadableStreamDefaultController-impl.js | 3 +++ 2 files changed, 5 insertions(+) diff --git a/index.bs b/index.bs index 74ac5b661..da1cf17f0 100644 --- a/index.bs +++ b/index.bs @@ -1585,6 +1585,8 @@ the following table: for="ReadableStreamDefaultController">enqueue(|chunk|, |options|) method steps are: 1. Let |transferList| be |options|["transfer"]. + 1. If |transferList| is not [=list/is empty|empty=] and [=this=].[=ReadableStreamDefaultController/[[isOwning]]=] is false + throw a {{TypeError}} exception. 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$]([=this=]) is false, throw a {{TypeError}} exception. 1. Perform ? [$ReadableStreamDefaultControllerEnqueue$]([=this=], |chunk|, |transferList|). diff --git a/reference-implementation/lib/ReadableStreamDefaultController-impl.js b/reference-implementation/lib/ReadableStreamDefaultController-impl.js index d29faf89b..399f6f3d6 100644 --- a/reference-implementation/lib/ReadableStreamDefaultController-impl.js +++ b/reference-implementation/lib/ReadableStreamDefaultController-impl.js @@ -19,6 +19,9 @@ exports.implementation = class ReadableStreamDefaultControllerImpl { enqueue(chunk, options) { const transferList = options ? options.transfer : undefined; + if (transferList.length && !this._isOwning) { + throw new TypeError('The stream is not an owning stream and cannot make use of options'); + } if (aos.ReadableStreamDefaultControllerCanCloseOrEnqueue(this) === false) { throw new TypeError('The stream is not in a state that permits enqueue'); } From 65b6f2ad0987de5761067206d5838d4bbe5c8881 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Tue, 18 Apr 2023 11:28:55 +0200 Subject: [PATCH 05/36] Update ReadableStreamDefaultController enqueue non normative description --- index.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.bs b/index.bs index da1cf17f0..dae7598de 100644 --- a/index.bs +++ b/index.bs @@ -1554,9 +1554,10 @@ the following table: previously-enqueued [=chunks=] from the stream, but once those are read, the stream will become closed. -
controller.{{ReadableStreamDefaultController/enqueue()|enqueue}}(chunk) +
controller.{{ReadableStreamDefaultController/enqueue()|enqueue}}(chunk, options)
-

Enqueues the given [=chunk=] chunk in the controlled readable stream. +

Enqueues the given [=chunk=] chunk in the controlled readable stream, + with options.

controller.{{ReadableStreamDefaultController/error()|error}}(e)
From 38d0bc022eeb88b59b59532f5e98b77570c064b5 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 09:13:26 +0200 Subject: [PATCH 06/36] Update index.bs Co-authored-by: Adam Rice --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index dae7598de..f0ba36621 100644 --- a/index.bs +++ b/index.bs @@ -6398,7 +6398,7 @@ for="value-with-size">value and size. 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. Let |enqueuedValue| be |value|. + 1. If |container| has an \[[isOwning]] internal slot whose value is true, perform the following steps: 1. If |container| has a \[[isOwning]] internal slot whose value is true, perform the following steps: 1. Set |enqueuedValue| to [$StructuredTransferOrClone$](|value|, |transferList|). 1. If |enqueuedValue| is an abrupt completion, return |enqueuedValue|. From b8ee3455e6bfa5d014c2dee276c168c1c206c94a Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 09:13:42 +0200 Subject: [PATCH 07/36] Update index.bs Co-authored-by: Adam Rice --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index f0ba36621..e8c5d91ca 100644 --- a/index.bs +++ b/index.bs @@ -6422,7 +6422,7 @@ for="value-with-size">value and size. performs the following steps: 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots. - 1. If |container| has a \[[isOwning]] internal slot whose value is true, perform the following steps until |container|.\[[queue]] + 1. If |container| has an \[[isOwning]] internal slot whose value is true, perform the following steps until |container|.\[[queue]] is [=list/is empty|empty=]: 1. Let |chunk| be ! [$DequeueValue$]([=this=]). 1. If |chunk| has [=closing steps=], perform the [=closing steps=] given |chunk|. From 8057ea127d0aecc6c3fd1a50228889620da49799 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Thu, 20 Apr 2023 09:15:48 +0200 Subject: [PATCH 08/36] fix StructuredTransferOrClone --- index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.bs b/index.bs index e8c5d91ca..ec5d3e3f3 100644 --- a/index.bs +++ b/index.bs @@ -6637,8 +6637,8 @@ The following abstract operations are a grab-bag of utilities.
StructuredTransferOrClone(|value|, |transferList|) performs the following steps: - 1. Let |serialized| be ! [$StructuredSerializeWithTransfer$](|value|, |transferList|). - 1. Let |deserialized| be ! [$StructuredDeserializeWithTransfer$](|serialized|, [=the current Realm=]). + 1. Let |serialized| be ? [$StructuredSerializeWithTransfer$](|value|, |transferList|). + 1. Let |deserialized| be ? [$StructuredDeserializeWithTransfer$](|serialized|, [=the current Realm=]). 1. Return |deserialized|.\[[Deserialized]].
From 8633ef6c5bd8f31bec9a7e3456f7d0a9f83fb89a Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Thu, 20 Apr 2023 09:17:50 +0200 Subject: [PATCH 09/36] Add Assert to ReadableStreamDefaultControllerEnqueue --- index.bs | 1 + 1 file changed, 1 insertion(+) diff --git a/index.bs b/index.bs index ec5d3e3f3..81435c213 100644 --- a/index.bs +++ b/index.bs @@ -2971,6 +2971,7 @@ The following abstract operations support the implementation of the id="readable-stream-default-controller-enqueue">ReadableStreamDefaultControllerEnqueue(|controller|, |chunk|, |transferList|) performs the following steps: + 1. Assert: |transferList| is empty or |controller|.[=ReadableStreamDefaultController/[[isOwning]]=] is true. 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$](|controller|) is false, return. 1. Let |stream| be |controller|.[=ReadableStreamDefaultController/[[stream]]=]. 1. If ! [$IsReadableStreamLocked$](|stream|) is true and ! From 2f924f48fa5a6ce7f3462bca5c4d678660fae6b1 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Thu, 20 Apr 2023 09:20:40 +0200 Subject: [PATCH 10/36] Use Symbol for closing steps --- .../lib/abstract-ops/queue-with-sizes.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/reference-implementation/lib/abstract-ops/queue-with-sizes.js b/reference-implementation/lib/abstract-ops/queue-with-sizes.js index 4cc668e57..a25084be0 100644 --- a/reference-implementation/lib/abstract-ops/queue-with-sizes.js +++ b/reference-implementation/lib/abstract-ops/queue-with-sizes.js @@ -39,15 +39,17 @@ exports.PeekQueueValue = container => { return pair.value; }; +const closingStepsSymbol = Symbol('closing-steps'); + exports.ResetQueue = container => { assert('_queue' in container && '_queueTotalSize' in container); if (container._isOwning) { while (container._queue.length > 0) { const value = exports.DequeueValue(container); - if (typeof value.close === 'function') { + if (typeof value[closingStepsSymbol] === 'function') { try { - value.close(); + value[closingStepsSymbol](); } catch (closeException) { // Nothing to do. } From 48ebb436439d6133262f8f652fe3a33484d09130 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:20:53 +0200 Subject: [PATCH 11/36] Update index.bs 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 81435c213..b86a58bf4 100644 --- a/index.bs +++ b/index.bs @@ -1586,7 +1586,7 @@ the following table: for="ReadableStreamDefaultController">enqueue(|chunk|, |options|) method steps are: 1. Let |transferList| be |options|["transfer"]. - 1. If |transferList| is not [=list/is empty|empty=] and [=this=].[=ReadableStreamDefaultController/[[isOwning]]=] is false + 1. If |transferList| is not [=list/is empty|empty=] and [=this=].[=ReadableStreamDefaultController/[[isOwning]]=] is false, throw a {{TypeError}} exception. 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$]([=this=]) is false, throw a {{TypeError}} exception. From 611aae5b0ccd8cbe7d25e6163349a2c66b1489c6 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:21:40 +0200 Subject: [PATCH 12/36] Update index.bs 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 b86a58bf4..ef4727928 100644 --- a/index.bs +++ b/index.bs @@ -2306,7 +2306,7 @@ create them does not matter. 1. Otherwise, set |chunk2| to |cloneResult|.\[[Value]]. 1. If |canceled1| is false, perform ! [$ReadableStreamDefaultControllerEnqueue$](|branch1|.[=ReadableStream/[[controller]]=], - |chunk1|, undefined). + |chunk1|, « »). 1. If |canceled2| is false, perform ! [$ReadableStreamDefaultControllerEnqueue$](|branch2|.[=ReadableStream/[[controller]]=], |chunk2|, undefined). From 6d3284f2a023cd93e0d612e92002f531b150abd6 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:21:56 +0200 Subject: [PATCH 13/36] Update index.bs 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 ef4727928..aff1a7238 100644 --- a/index.bs +++ b/index.bs @@ -2309,7 +2309,7 @@ create them does not matter. |chunk1|, « »). 1. If |canceled2| is false, perform ! [$ReadableStreamDefaultControllerEnqueue$](|branch2|.[=ReadableStream/[[controller]]=], - |chunk2|, undefined). + |chunk2|, « »). 1. Set |reading| to false. 1. If |readAgain| is true, perform |pullAlgorithm|. From 24698ddf6fe67f159cb02ec8d6cb86ed852c8e5b Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:22:10 +0200 Subject: [PATCH 14/36] Update index.bs 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 aff1a7238..84c9980aa 100644 --- a/index.bs +++ b/index.bs @@ -2971,7 +2971,7 @@ The following abstract operations support the implementation of the id="readable-stream-default-controller-enqueue">ReadableStreamDefaultControllerEnqueue(|controller|, |chunk|, |transferList|) performs the following steps: - 1. Assert: |transferList| is empty or |controller|.[=ReadableStreamDefaultController/[[isOwning]]=] is true. + 1. Assert: |transferList| [=list/is empty=] or |controller|.[=ReadableStreamDefaultController/[[isOwning]]=] is true. 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$](|controller|) is false, return. 1. Let |stream| be |controller|.[=ReadableStreamDefaultController/[[stream]]=]. 1. If ! [$IsReadableStreamLocked$](|stream|) is true and ! From f5d32b582af90c3e6d363506ca7742e72e8b2bb8 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:24:00 +0200 Subject: [PATCH 15/36] Update index.bs Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- index.bs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index 84c9980aa..8673e8920 100644 --- a/index.bs +++ b/index.bs @@ -2978,10 +2978,11 @@ The following abstract operations support the implementation of the [$ReadableStreamGetNumReadRequests$](|stream|) > 0, perform the following steps: 1. Let |internalChunk| be |chunk|. 1. If |controller|.[=ReadableStreamDefaultController/[[isOwning]]=] is true, perform the following steps: - 1. Set |internalChunk| to [$StructuredTransferOrClone$](|chunk|, |transferList|). - 1. If |internalChunk| is an abrupt completion, - 1. Perform ! [$ReadableStreamDefaultControllerError$](|controller|, |internalChunk|.\[[Value]]). - 1. Return |internalChunk|. + 1. Let |result| be [$StructuredTransferOrClone$](|chunk|, |transferList|). + 1. If |result| is an abrupt completion, + 1. Perform ! [$ReadableStreamDefaultControllerError$](|controller|, |result|.\[[Value]]). + 1. Return |result|. + 1. Set |internalChunk| to |result|.\[[Value]]. 1. Perform ! [$ReadableStreamFulfillReadRequest$](|stream|, |internalChunk|, false). 1. Otherwise, 1. Let |result| be the result of performing From d4b8b846cc6ae0fd76a716d4d67e1bcaadbb95ae Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:24:13 +0200 Subject: [PATCH 16/36] Update index.bs 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 8673e8920..a18df49f3 100644 --- a/index.bs +++ b/index.bs @@ -5916,7 +5916,7 @@ The following abstract operations support the implementaiton of the 1. If ! [$ReadableStreamDefaultControllerCanCloseOrEnqueue$](|readableController|) is false, throw a {{TypeError}} exception. 1. Let |enqueueResult| be [$ReadableStreamDefaultControllerEnqueue$](|readableController|, - |chunk|, undefined). + |chunk|, « »). 1. If |enqueueResult| is an abrupt completion, 1. Perform ! [$TransformStreamErrorWritableAndUnblockWrite$](|stream|, |enqueueResult|.\[[Value]]). From 52a01169110a8983afd8c285adff03a8f16e5169 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:24:54 +0200 Subject: [PATCH 17/36] Update index.bs Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- index.bs | 1 - 1 file changed, 1 deletion(-) diff --git a/index.bs b/index.bs index a18df49f3..a21ef9cb4 100644 --- a/index.bs +++ b/index.bs @@ -6401,7 +6401,6 @@ for="value-with-size">value and size. 1. If ! [$IsNonNegativeNumber$](|size|) is false, throw a {{RangeError}} exception. 1. If |size| is +∞, throw a {{RangeError}} exception. 1. If |container| has an \[[isOwning]] internal slot whose value is true, perform the following steps: - 1. If |container| has a \[[isOwning]] internal slot whose value is true, perform the following steps: 1. Set |enqueuedValue| to [$StructuredTransferOrClone$](|value|, |transferList|). 1. If |enqueuedValue| is an abrupt completion, return |enqueuedValue|. 1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |enqueuedValue| and From 21237a6e4a6c8c4ca7627137a0d1b60c7ae34eb7 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:25:38 +0200 Subject: [PATCH 18/36] Update index.bs 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 a21ef9cb4..ce072ea38 100644 --- a/index.bs +++ b/index.bs @@ -6486,7 +6486,7 @@ abstract operations are used to implement these "cross-realm transforms". 1. Let |value| be ! [$Get$](|data|, "`value`"). 1. Assert: [$Type$](|type|) is String. 1. If |type| is "`chunk`", - 1. Perform ! [$ReadableStreamDefaultControllerEnqueue$](|controller|, |value|, undefined). + 1. Perform ! [$ReadableStreamDefaultControllerEnqueue$](|controller|, |value|, « »). 1. Otherwise, if |type| is "`close`", 1. Perform ! [$ReadableStreamDefaultControllerClose$](|controller|). 1. Disentangle |port|. From 9f0bb24112be8a2c383d764e0ee6a8e22715827b Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:26:53 +0200 Subject: [PATCH 19/36] Update index.bs Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- index.bs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.bs b/index.bs index ce072ea38..462ddf25c 100644 --- a/index.bs +++ b/index.bs @@ -6401,8 +6401,7 @@ for="value-with-size">value and size. 1. If ! [$IsNonNegativeNumber$](|size|) is false, throw a {{RangeError}} exception. 1. If |size| is +∞, throw a {{RangeError}} exception. 1. If |container| has an \[[isOwning]] internal slot whose value is true, perform the following steps: - 1. Set |enqueuedValue| to [$StructuredTransferOrClone$](|value|, |transferList|). - 1. If |enqueuedValue| is an abrupt completion, return |enqueuedValue|. + 1. Set |enqueuedValue| to ? [$StructuredTransferOrClone$](|value|, |transferList|). 1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |enqueuedValue| and [=value-with-size/size=] |size| to |container|.\[[queue]]. 1. Set |container|.\[[queueTotalSize]] to |container|.\[[queueTotalSize]] + |size|. From 9db7cc2ba01292d6c1a460c2e1a9ad314915b8b4 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 13:27:14 +0200 Subject: [PATCH 20/36] Update reference-implementation/lib/ReadableStreamDefaultController-impl.js Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- .../lib/ReadableStreamDefaultController-impl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference-implementation/lib/ReadableStreamDefaultController-impl.js b/reference-implementation/lib/ReadableStreamDefaultController-impl.js index 399f6f3d6..35375ca6f 100644 --- a/reference-implementation/lib/ReadableStreamDefaultController-impl.js +++ b/reference-implementation/lib/ReadableStreamDefaultController-impl.js @@ -18,7 +18,7 @@ exports.implementation = class ReadableStreamDefaultControllerImpl { } enqueue(chunk, options) { - const transferList = options ? options.transfer : undefined; + const transferList = options ? options.transfer : []; if (transferList.length && !this._isOwning) { throw new TypeError('The stream is not an owning stream and cannot make use of options'); } From cc6a6b72c70e938ee36d0338a1a454aba5ecd7ba Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 14:21:28 +0200 Subject: [PATCH 21/36] Update reference-implementation/lib/abstract-ops/transform-streams.js Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- reference-implementation/lib/abstract-ops/transform-streams.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference-implementation/lib/abstract-ops/transform-streams.js b/reference-implementation/lib/abstract-ops/transform-streams.js index 1152d19de..484ddebd3 100644 --- a/reference-implementation/lib/abstract-ops/transform-streams.js +++ b/reference-implementation/lib/abstract-ops/transform-streams.js @@ -155,7 +155,7 @@ function TransformStreamDefaultControllerEnqueue(controller, chunk) { // accept TransformStreamDefaultControllerEnqueue() calls. try { - ReadableStreamDefaultControllerEnqueue(readableController, chunk, undefined); + ReadableStreamDefaultControllerEnqueue(readableController, chunk, []); } catch (e) { // This happens when readableStrategy.size() throws. TransformStreamErrorWritableAndUnblockWrite(stream, e); From ddbed601cf7c05490855af8e8e025fdb258a1cf6 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 14:21:41 +0200 Subject: [PATCH 22/36] Update reference-implementation/lib/abstract-ops/readable-streams.js Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- reference-implementation/lib/abstract-ops/readable-streams.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference-implementation/lib/abstract-ops/readable-streams.js b/reference-implementation/lib/abstract-ops/readable-streams.js index 98b0a35fb..333456d38 100644 --- a/reference-implementation/lib/abstract-ops/readable-streams.js +++ b/reference-implementation/lib/abstract-ops/readable-streams.js @@ -395,7 +395,7 @@ function ReadableStreamDefaultTee(stream, cloneForBranch2) { ReadableStreamDefaultControllerEnqueue(branch1._controller, chunk1, undefined); } if (canceled2 === false) { - ReadableStreamDefaultControllerEnqueue(branch2._controller, chunk2, undefined); + ReadableStreamDefaultControllerEnqueue(branch2._controller, chunk2, []); } reading = false; From 0ab0d72c135ff2b6bf47f3b99d8460768bc3e392 Mon Sep 17 00:00:00 2001 From: youennf Date: Thu, 20 Apr 2023 14:21:52 +0200 Subject: [PATCH 23/36] Update reference-implementation/lib/abstract-ops/readable-streams.js Co-authored-by: Mattias Buelens <649348+MattiasBuelens@users.noreply.github.com> --- reference-implementation/lib/abstract-ops/readable-streams.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference-implementation/lib/abstract-ops/readable-streams.js b/reference-implementation/lib/abstract-ops/readable-streams.js index 333456d38..ea5d7da95 100644 --- a/reference-implementation/lib/abstract-ops/readable-streams.js +++ b/reference-implementation/lib/abstract-ops/readable-streams.js @@ -392,7 +392,7 @@ function ReadableStreamDefaultTee(stream, cloneForBranch2) { // } if (canceled1 === false) { - ReadableStreamDefaultControllerEnqueue(branch1._controller, chunk1, undefined); + ReadableStreamDefaultControllerEnqueue(branch1._controller, chunk1, []); } if (canceled2 === false) { ReadableStreamDefaultControllerEnqueue(branch2._controller, chunk2, []); From 77450a85f244939e9d8182cbcb23f28ef7efe189 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Thu, 20 Apr 2023 16:34:03 +0200 Subject: [PATCH 24/36] Rename closing steps to dispose steps --- index.bs | 4 ++-- .../lib/abstract-ops/queue-with-sizes.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/index.bs b/index.bs index 462ddf25c..7e0c44718 100644 --- a/index.bs +++ b/index.bs @@ -654,7 +654,7 @@ enum ReadableStreamType { "bytes", "owning" };

Can be set to "owning" to signal that the constructed {{ReadableStream}} will own chunks (via transfer or serialization) before enqueuing them. This ensures that enqueued chunks are not mutable by the source. - Transferred or serialized chunks may have closing steps which are executed if + Transferred or serialized chunks may have dispose steps which are executed if enqueued chunks are dequeued without being provided to the application, for instance when a {{ReadableStream}} is errored.

@@ -6425,7 +6425,7 @@ for="value-with-size">value and size. 1. If |container| has an \[[isOwning]] internal slot whose value is true, perform the following steps until |container|.\[[queue]] is [=list/is empty|empty=]: 1. Let |chunk| be ! [$DequeueValue$]([=this=]). - 1. If |chunk| has [=closing steps=], perform the [=closing steps=] given |chunk|. + 1. If |chunk| has [=dispose steps=], perform the [=dispose steps=] given |chunk|. 1. Set |container|.\[[queue]] to a new empty [=list=]. 1. Set |container|.\[[queueTotalSize]] to 0. diff --git a/reference-implementation/lib/abstract-ops/queue-with-sizes.js b/reference-implementation/lib/abstract-ops/queue-with-sizes.js index a25084be0..ed2834374 100644 --- a/reference-implementation/lib/abstract-ops/queue-with-sizes.js +++ b/reference-implementation/lib/abstract-ops/queue-with-sizes.js @@ -39,7 +39,7 @@ exports.PeekQueueValue = container => { return pair.value; }; -const closingStepsSymbol = Symbol('closing-steps'); +const disposeStepsSymbol = Symbol('dispose-steps'); exports.ResetQueue = container => { assert('_queue' in container && '_queueTotalSize' in container); @@ -47,9 +47,9 @@ exports.ResetQueue = container => { if (container._isOwning) { while (container._queue.length > 0) { const value = exports.DequeueValue(container); - if (typeof value[closingStepsSymbol] === 'function') { + if (typeof value[disposeStepsSymbol] === 'function') { try { - value[closingStepsSymbol](); + value[disposeStepsSymbol](); } catch (closeException) { // Nothing to do. } From 46718bbdd37465cfcd9e707e8125d96aa349092f Mon Sep 17 00:00:00 2001 From: youennf Date: Wed, 10 May 2023 08:30:58 +0200 Subject: [PATCH 25/36] Update index.bs Co-authored-by: Kagami Sascha Rosylight --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 7e0c44718..31bfe022b 100644 --- a/index.bs +++ b/index.bs @@ -1470,7 +1470,7 @@ interface ReadableStreamDefaultController { readonly attribute unrestricted double? desiredSize; undefined close(); - undefined enqueue(optional any chunk, optional StructuredSerializeOptions options = { }); + undefined enqueue(optional any chunk, optional StructuredSerializeOptions options = {}); undefined error(optional any e); }; From db7658b75d38135b1b46f60514474cb8ca80f77b Mon Sep 17 00:00:00 2001 From: youennf Date: Wed, 10 May 2023 08:32:11 +0200 Subject: [PATCH 26/36] Update index.bs Co-authored-by: Kagami Sascha Rosylight --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 31bfe022b..48b6a529e 100644 --- a/index.bs +++ b/index.bs @@ -1554,7 +1554,7 @@ the following table: previously-enqueued [=chunks=] from the stream, but once those are read, the stream will become closed. -
controller.{{ReadableStreamDefaultController/enqueue()|enqueue}}(chunk, options) +
controller.{{ReadableStreamDefaultController/enqueue()|enqueue}}(|chunk|, |options|)

Enqueues the given [=chunk=] chunk in the controlled readable stream, with options. From 94ebacf227663a43aba899226f32c353e0db26d2 Mon Sep 17 00:00:00 2001 From: youennf Date: Wed, 10 May 2023 08:32:26 +0200 Subject: [PATCH 27/36] Update index.bs Co-authored-by: Kagami Sascha Rosylight --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 48b6a529e..717ddb0c5 100644 --- a/index.bs +++ b/index.bs @@ -1556,7 +1556,7 @@ the following table:

controller.{{ReadableStreamDefaultController/enqueue()|enqueue}}(|chunk|, |options|)
-

Enqueues the given [=chunk=] chunk in the controlled readable stream, +

Enqueues the given [=chunk=] |chunk| in the controlled readable stream, with options.

controller.{{ReadableStreamDefaultController/error()|error}}(e) From bf7404bdb3b17c7175dec87f05c06ea5ae4185c4 Mon Sep 17 00:00:00 2001 From: youennf Date: Wed, 10 May 2023 08:32:41 +0200 Subject: [PATCH 28/36] Update index.bs Co-authored-by: Kagami Sascha Rosylight --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 717ddb0c5..4337d4c00 100644 --- a/index.bs +++ b/index.bs @@ -1557,7 +1557,7 @@ the following table:
controller.{{ReadableStreamDefaultController/enqueue()|enqueue}}(|chunk|, |options|)

Enqueues the given [=chunk=] |chunk| in the controlled readable stream, - with options. + with |options|.

controller.{{ReadableStreamDefaultController/error()|error}}(e)
From 657e85f6e8c53b526fe86675c3c1cd49dfbbf1e5 Mon Sep 17 00:00:00 2001 From: youennf Date: Wed, 10 May 2023 08:36:46 +0200 Subject: [PATCH 29/36] Update index.bs Co-authored-by: Kagami Sascha Rosylight --- index.bs | 1 + 1 file changed, 1 insertion(+) diff --git a/index.bs b/index.bs index 4337d4c00..545d953c2 100644 --- a/index.bs +++ b/index.bs @@ -6400,6 +6400,7 @@ for="value-with-size">value and size. 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. Let |enqueuedValue| be |value|. 1. If |container| has an \[[isOwning]] internal slot whose value is true, perform the following steps: 1. Set |enqueuedValue| to ? [$StructuredTransferOrClone$](|value|, |transferList|). 1. [=list/Append=] a new [=value-with-size=] with [=value-with-size/value=] |enqueuedValue| and From a1c6573e3e299587857589898044454d0bbc4918 Mon Sep 17 00:00:00 2001 From: youennf Date: Wed, 10 May 2023 08:37:34 +0200 Subject: [PATCH 30/36] Update index.bs Co-authored-by: Jan-Ivar Bruaroey --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 545d953c2..9e437f4ca 100644 --- a/index.bs +++ b/index.bs @@ -6424,7 +6424,7 @@ for="value-with-size">value and size. 1. Assert: |container| has \[[queue]] and \[[queueTotalSize]] internal slots. 1. If |container| has an \[[isOwning]] internal slot whose value is true, perform the following steps until |container|.\[[queue]] - is [=list/is empty|empty=]: + [=list/is empty=]: 1. Let |chunk| be ! [$DequeueValue$]([=this=]). 1. If |chunk| has [=dispose steps=], perform the [=dispose steps=] given |chunk|. 1. Set |container|.\[[queue]] to a new empty [=list=]. From 4c80708082049e1f7c21edf692bf305ee10e2e84 Mon Sep 17 00:00:00 2001 From: youennf Date: Wed, 10 May 2023 08:41:24 +0200 Subject: [PATCH 31/36] Update reference-implementation/lib/ReadableStreamDefaultController.webidl Co-authored-by: Kagami Sascha Rosylight --- .../lib/ReadableStreamDefaultController.webidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference-implementation/lib/ReadableStreamDefaultController.webidl b/reference-implementation/lib/ReadableStreamDefaultController.webidl index dc02cfdcb..041c731c5 100644 --- a/reference-implementation/lib/ReadableStreamDefaultController.webidl +++ b/reference-implementation/lib/ReadableStreamDefaultController.webidl @@ -7,6 +7,6 @@ interface ReadableStreamDefaultController { readonly attribute unrestricted double? desiredSize; undefined close(); - undefined enqueue(optional any chunk, optional StructuredSerializeOptions options = { }); + undefined enqueue(optional any chunk, optional StructuredSerializeOptions options = {}); undefined error(optional any e); }; From 91461111cf16e11b5f54a965061ea4710503b707 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Wed, 10 May 2023 08:43:10 +0200 Subject: [PATCH 32/36] Update according review --- .github/workflows/test.yml | 2 +- index.bs | 6 +++--- .../lib/ReadableStreamDefaultController-impl.js | 2 +- reference-implementation/web-platform-tests | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7102b5b04..58c4b8177 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: true - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v3 with: node-version: 18 - run: npm install diff --git a/index.bs b/index.bs index 9e437f4ca..c116490b4 100644 --- a/index.bs +++ b/index.bs @@ -654,9 +654,8 @@ enum ReadableStreamType { "bytes", "owning" };

Can be set to "owning" to signal that the constructed {{ReadableStream}} will own chunks (via transfer or serialization) before enqueuing them. This ensures that enqueued chunks are not mutable by the source. - Transferred or serialized chunks may have dispose steps which are executed if - enqueued chunks are dequeued without being provided to the application, for instance when - a {{ReadableStream}} is errored. + Chunks may have dispose steps which are executed if enqueued chunks are dequeued + without being provided to the application, for instance when a {{ReadableStream}} is errored.

Setting any value other than "{{ReadableStreamType/bytes}}", "{{ReadableStreamType/owning}}" @@ -6779,6 +6778,7 @@ mark=] is greater than zero. 1. If |stream|.[=ReadableStream/[[controller]]=] [=implements=] {{ReadableStreamDefaultController}}, + 1. If |transferList| is null, set |transferList| to an [=list/is empty|empty=] list. 1. Perform ! [$ReadableStreamDefaultControllerEnqueue$](|stream|.[=ReadableStream/[[controller]]=], |chunk|, |transferList|). 1. Otherwise, diff --git a/reference-implementation/lib/ReadableStreamDefaultController-impl.js b/reference-implementation/lib/ReadableStreamDefaultController-impl.js index 35375ca6f..8abc763d0 100644 --- a/reference-implementation/lib/ReadableStreamDefaultController-impl.js +++ b/reference-implementation/lib/ReadableStreamDefaultController-impl.js @@ -18,7 +18,7 @@ exports.implementation = class ReadableStreamDefaultControllerImpl { } enqueue(chunk, options) { - const transferList = options ? options.transfer : []; + const transferList = options.transfer; if (transferList.length && !this._isOwning) { throw new TypeError('The stream is not an owning stream and cannot make use of options'); } diff --git a/reference-implementation/web-platform-tests b/reference-implementation/web-platform-tests index 789685889..72dd137bb 160000 --- a/reference-implementation/web-platform-tests +++ b/reference-implementation/web-platform-tests @@ -1 +1 @@ -Subproject commit 789685889a4bcc0898acac96960d060daf6c8e66 +Subproject commit 72dd137bbe082c661eaefc022bcaa272065c466b From 002587a7a6ee9f20b3bb416004e048f354cb1b9a Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Wed, 7 Jun 2023 22:15:31 +0200 Subject: [PATCH 33/36] Reverting WPT module change --- reference-implementation/web-platform-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference-implementation/web-platform-tests b/reference-implementation/web-platform-tests index 72dd137bb..789685889 160000 --- a/reference-implementation/web-platform-tests +++ b/reference-implementation/web-platform-tests @@ -1 +1 @@ -Subproject commit 72dd137bbe082c661eaefc022bcaa272065c466b +Subproject commit 789685889a4bcc0898acac96960d060daf6c8e66 From 767925174e08e7647e38a08a52d1f7e512215334 Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Mon, 12 Jun 2023 13:54:53 +0200 Subject: [PATCH 34/36] Update according saschanaz review --- index.bs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.bs b/index.bs index c116490b4..d337cef93 100644 --- a/index.bs +++ b/index.bs @@ -653,7 +653,7 @@ enum ReadableStreamType { "bytes", "owning" };

Can be set to "owning" to signal that the constructed {{ReadableStream}} will own chunks (via transfer or serialization) before enqueuing them. - This ensures that enqueued chunks are not mutable by the source. + This ensures that enqueued chunks cannot be mutated by the source. Chunks may have dispose steps which are executed if enqueued chunks are dequeued without being provided to the application, for instance when a {{ReadableStream}} is errored.

@@ -5217,7 +5217,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 sentinel=], 0, undefined). + 1. Perform ! [$EnqueueValueWithSize$](|controller|, [=close sentinel=], 0, « »). 1. Perform ! [$WritableStreamDefaultControllerAdvanceQueueIfNeeded$](|controller|). @@ -5321,7 +5321,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 |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|, undefined). + 1. Let |enqueueResult| be [$EnqueueValueWithSize$](|controller|, |chunk|, |chunkSize|, « »). 1. If |enqueueResult| is an abrupt completion, 1. Perform ! [$WritableStreamDefaultControllerErrorIfNeeded$](|controller|, |enqueueResult|.\[[Value]]). From fa981ae1911f3e22e826d519129f7abb454aa75f Mon Sep 17 00:00:00 2001 From: Youenn Fablet Date: Mon, 12 Jun 2023 16:15:38 +0200 Subject: [PATCH 35/36] Move isOwning tab entry --- index.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index 0fb8da852..cd9ac1b58 100644 --- a/index.bs +++ b/index.bs @@ -1509,6 +1509,10 @@ the following table: A boolean flag indicating whether the stream has been closed by its [=underlying source=], but still has [=chunks=] in its internal queue that have not yet been read + + \[[isOwning]] + A boolean flag indicating whether to take ownership of enqueued chunks + via transfer or serialization. \[[pullAgain]] A boolean flag set to true if the stream's mechanisms requested a call @@ -1546,10 +1550,6 @@ the following table: \[[stream]] The {{ReadableStream}} instance controlled - - \[[isOwning]] - A boolean flag indicating whether to take ownership of enqueued chunks - via transfer or serialization.

Methods and properties

From 81411c57a060957b7a5272bed218e4351b9928a0 Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Fri, 3 May 2024 18:51:02 +0200 Subject: [PATCH 36/36] Update Enqueue() call inside ReadableStreamFromIterable --- index.bs | 2 +- reference-implementation/lib/abstract-ops/readable-streams.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.bs b/index.bs index 28872ae36..a41a6431a 100644 --- a/index.bs +++ b/index.bs @@ -2216,7 +2216,7 @@ The following abstract operations operate on {{ReadableStream}} instances at a h 1. Otherwise: 1. Let |value| be ? [$IteratorValue$](|iterResult|). 1. Perform ! [$ReadableStreamDefaultControllerEnqueue$](|stream|.[=ReadableStream/[[controller]]=], - |value|). + |value|, « »). 1. Let |cancelAlgorithm| be the following steps, given |reason|: diff --git a/reference-implementation/lib/abstract-ops/readable-streams.js b/reference-implementation/lib/abstract-ops/readable-streams.js index 6528dd7c2..b40fd5a55 100644 --- a/reference-implementation/lib/abstract-ops/readable-streams.js +++ b/reference-implementation/lib/abstract-ops/readable-streams.js @@ -1921,7 +1921,7 @@ function ReadableStreamFromIterable(asyncIterable) { ReadableStreamDefaultControllerClose(stream._controller); } else { const value = IteratorValue(iterResult); - ReadableStreamDefaultControllerEnqueue(stream._controller, value); + ReadableStreamDefaultControllerEnqueue(stream._controller, value, []); } }); }