-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Specify ReadableStream.[[Transfer]] #623
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -375,6 +375,10 @@ Instances of {{ReadableStream}} are created with the internal slots described in | |
<th>Description (<em>non-normative</em>)</th> | ||
</tr> | ||
</thead> | ||
<tr> | ||
<td>{{[[Detached]]}} | ||
<td>A boolean flag set to <emu-val>true</emu-val> when the stream has been transferred away to another realm | ||
</tr> | ||
<tr> | ||
<td>\[[disturbed]] | ||
<td>A boolean flag set to <emu-val>true</emu-val> when the stream has been read from or canceled | ||
|
@@ -448,6 +452,7 @@ ReadableStream(<var>underlyingSource</var> = {}, { <var>size</var>, <var>highWat | |
<emu-alg> | ||
1. Set *this*.[[state]] to `"readable"`. | ||
1. Set *this*.[[reader]] and *this*.[[storedError]] to *undefined*. | ||
1. Set *this*.<a idl>[[Detached]]</a> to *false*. | ||
1. Set *this*.[[disturbed]] to *false*. | ||
1. Set *this*.[[readableStreamController]] to *undefined*. | ||
1. Let _type_ be ? GetV(_underlyingSource_, `"type"`). | ||
|
@@ -456,10 +461,10 @@ ReadableStream(<var>underlyingSource</var> = {}, { <var>size</var>, <var>highWat | |
1. If _highWaterMark_ is *undefined*, let _highWaterMark_ be *0*. | ||
1. Set *this*.[[readableStreamController]] to ? Construct(`<a idl>ReadableByteStreamController</a>`, « *this*, | ||
_underlyingSource_, _highWaterMark_ »). | ||
1. Otherwise, if _type_ is *undefined*, | ||
1. Otherwise, if _type_ is *undefined* or _type_ is `"cloning"`, | ||
1. If _highWaterMark_ is *undefined*, let _highWaterMark_ be *1*. | ||
1. Set *this*.[[readableStreamController]] to ? Construct(`<a idl>ReadableStreamDefaultController</a>`, « *this*, | ||
_underlyingSource_, _size_, _highWaterMark_ »). | ||
_underlyingSource_, _size_, _highWaterMark_, _type_ »). | ||
1. Otherwise, throw a *RangeError* exception. | ||
</emu-alg> | ||
|
||
|
@@ -733,6 +738,30 @@ ReadableStream(<var>underlyingSource</var> = {}, { <var>size</var>, <var>highWat | |
</code></pre> | ||
</div> | ||
|
||
<h4 id="rs-internal-methods">Readable Stream Internal Methods</h4> | ||
|
||
The following internal method is implemented by each {{ReadableStream}} instance. | ||
|
||
<h5 id="rs-internal-method-transfer"><a idl lt="[[Transfer]]()">\[[Transfer]](<var>targetRealm</var>)</a></h5> | ||
|
||
<emu-alg> | ||
1. If ! IsReadableStreamLocked(*this*) is *true*, throw a *TypeError* exception. | ||
1. If *this*.[[state]] is `"errored"`, throw a *TypeError* exception. | ||
1. Let _controller_ be *this*.[[readableStreamController]]. | ||
1. If _controller_.[[targetRealm]] is *undefined*, throw a *TypeError* exception. | ||
1. Let _that_ be a new instance of <a idl>ReadableStream</a> in _targetRealm_. | ||
1. Set _that_.[[state]] to *this*.[[state]]. | ||
1. Set _that_.[[disturbed]] to *this*.[[disturbed]]. | ||
1. Set _controller_.[[controlledReadableStream]] to _that_. | ||
1. Set _that_.[[readableStreamController]] to _controller_. | ||
1. Let _queue_ be _controller_.[[queue]]. | ||
1. Repeat for each Record {[[value]], [[size]]} _pair_ that is an element of _queue_, | ||
1. Set _pair_.[[value]] to ! <a abstract-op>StructuredClone</a>(_pair_.[[value]], _targetRealm_). | ||
1. Set _controller_.[[targetRealm]] to _targetRealm_. | ||
1. Set _this_.<a idl>[[Detached]]</a> to *true*. | ||
1. Return _that_. | ||
</emu-alg> | ||
|
||
<h3 id="rs-abstract-ops">General Readable Stream Abstract Operations</h3> | ||
|
||
The following abstract operations, unlike most in this specification, are meant to be generally useful by other | ||
|
@@ -781,10 +810,11 @@ readable stream has ever been read from or canceled. | |
<var>stream</var> )</h4> | ||
|
||
This abstract operation is meant to be called from other specifications that may wish to query whether or not a | ||
readable stream is <a>locked to a reader</a>. | ||
readable stream is <a>locked to a reader</a> or has been transferred away to another realm. | ||
|
||
<emu-alg> | ||
1. Assert: ! IsReadableStream(_stream_) is *true*. | ||
1. If _stream_.<a idl>[[Detached]]</a> is *true*, return *true*. | ||
1. If _stream_.[[reader]] is *undefined*, return *false*. | ||
1. Return *true*. | ||
</emu-alg> | ||
|
@@ -940,6 +970,7 @@ nothrow>ReadableStreamAddReadIntoRequest ( <var>stream</var> )</h4> | |
<var>reason</var> )</h4> | ||
|
||
<emu-alg> | ||
1. Assert: _stream_.<a idl>[[Detached]]</a> is *false*. | ||
1. Set _stream_.[[disturbed]] to *true*. | ||
1. If _stream_.[[state]] is `"closed"`, return <a>a promise resolved with</a> *undefined*. | ||
1. If _stream_.[[state]] is `"errored"`, return <a>a promise rejected with</a> _stream_.[[storedError]]. | ||
|
@@ -1446,6 +1477,12 @@ Instances of {{ReadableStreamDefaultController}} are created with the internal s | |
<th>Description (<em>non-normative</em>)</th> | ||
</tr> | ||
</thead> | ||
<tr> | ||
<td>\[[targetRealm]] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This approach, of using the ReadableStreamDefaultController in one realm instead of just creating a new stream + controller pair, is very interesting, but not what I expected. Why did you choose that? To be specific, I would have expected the algorithm to be something like const reader = this.getReader();
const that = new ReadableStream({
pull(c) {
return reader.read().then(
({ value, done }) => {
if (done) {
c.close();
return;
}
c.enqueue(StructuredClone(value));
},
e => c.error(e)
);
},
// probably more
}); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This approach just seemed intuitive to me.
|
||
<td>Either *undefined*, or a Realm Record. If set to a Realm Record, ReadableStreamDefaultControllerEnqueue | ||
will perform the structured clone algorithm on passed chunks, cloning them into the targetRealm | ||
and enqueueing the result. | ||
</tr> | ||
<tr> | ||
<td>\[[closeRequested]] | ||
<td>A boolean flag indicating whether the stream has been closed by its <a>underlying source</a>, but still has | ||
|
@@ -1494,7 +1531,7 @@ Instances of {{ReadableStreamDefaultController}} are created with the internal s | |
<h4 id="rs-default-controller-constructor" constructor for="ReadableStreamDefaultController" | ||
lt="ReadableStreamDefaultController(stream, underlyingSource, size, highWaterMark)">new | ||
ReadableStreamDefaultController(<var>stream</var>, <var>underlyingSource</var>, <var>size</var>, | ||
<var>highWaterMark</var>)</h4> | ||
<var>highWaterMark</var>, <var>type</var>)</h4> | ||
|
||
<div class="note"> | ||
The <code>ReadableStreamDefaultController</code> constructor cannot be used directly; it only works on a | ||
|
@@ -1508,6 +1545,8 @@ ReadableStreamDefaultController(<var>stream</var>, <var>underlyingSource</var>, | |
1. Set *this*.[[underlyingSource]] to _underlyingSource_. | ||
1. Set *this*.[[queue]] to a new empty List. | ||
1. Set *this*.[[started]], *this*.[[closeRequested]], *this*.[[pullAgain]], and *this*.[[pulling]] to *false*. | ||
1. Set *this*.[[targetRealm]] to *undefined*. | ||
1. If _type_ is `"cloning"`, set *this*.[[targetRealm]] to the current Realm Record. | ||
1. Let _normalizedStrategy_ be ? ValidateAndNormalizeQueuingStrategy(_size_, _highWaterMark_). | ||
1. Set *this*.[[strategySize]] to _normalizedStrategy_.[[size]] and *this*.[[strategyHWM]] to | ||
_normalizedStrategy_.[[highWaterMark]]. | ||
|
@@ -1681,8 +1720,14 @@ asserts). | |
1. Let _stream_ be _controller_.[[controlledReadableStream]]. | ||
1. Assert: _controller_.[[closeRequested]] is *false*. | ||
1. Assert: _stream_.[[state]] is `"readable"`. | ||
1. If ! IsReadableStreamLocked(_stream_) is *true* and ! ReadableStreamGetNumReadRequests(_stream_) > *0*, perform | ||
! ReadableStreamFulfillReadRequest(_stream_, _chunk_, *false*). | ||
1. If ! IsReadableStreamLocked(_stream_) is *true* and ! ReadableStreamGetNumReadRequests(_stream_) > *0*, | ||
1. If _controller_.[[targetRealm]] is not *undefined*, | ||
1. Let _chunk_ be <a abstract-op>StructuredClone</a>(_chunk_, _controller_.[[targetRealm]]). | ||
1. If _chunk_ is an abrupt completion, | ||
1. Perform ! ReadableStreamDefaultControllerErrorIfNeeded(_controller_, _chunk_.[[Value]]). | ||
1. Return _chunk_. | ||
1. Let _chunk_ be _chunk_.[[Value]]. | ||
1. Perform ! ReadableStreamFulfillReadRequest(_stream_, _chunk_, *false*). | ||
1. Otherwise, | ||
1. Let _chunkSize_ be *1*. | ||
1. If _controller_.[[strategySize]] is not *undefined*, | ||
|
@@ -3589,11 +3634,13 @@ throughout the rest of this standard. | |
</emu-alg> | ||
|
||
<h4 id="enqueue-value-with-size" aoid="EnqueueValueWithSize" throws>EnqueueValueWithSize ( <var>queue</var>, | ||
<var>value</var>, <var>size</var> )</h4> | ||
<var>value</var>, <var>size</var>, <var>targetRealm</var> )</h4> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems nicer to me if EnqueueValueWithSize stays as a naive implementation of the queue-with-sizes data structure, and the structured cloning happens elsewhere. Is doing that much uglier? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well for one, it would mean reimplementing the same code for WritableStream eventually, so I guess there could be a wrapper both RS and WS use to enqueue? But then there would be...a single user of EnqueueValueWithSize that didn't use the wrapper, WritableStreamDefaultControllerClose. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, right, I also had a preference for throwing the |
||
|
||
<emu-alg> | ||
1. If _targetRealm_ was not passed, let _targetRealm_ be *undefined*. | ||
1. Let _size_ be ? ToNumber(_size_). | ||
1. If ! IsFiniteNonNegativeNumber(_size_) is *false*, throw a *RangeError* exception. | ||
1. If _targetRealm_ is not *undefined*, let _value_ be the result of ? StructuredClone(_value_, _targetRealm_). | ||
1. Append Record {[[value]]: _value_, [[size]]: _size_} as the last element of _queue_. | ||
</emu-alg> | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,9 @@ | |
"Takeshi Yoshino <[email protected]>" | ||
], | ||
"license": "(CC0-1.0 OR MIT)", | ||
"dependencies": { | ||
"realistic-structured-clone": "^0.0.3" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^3.2.2", | ||
"glob": "^7.0.3", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use ?, not !, as StructuredClone could throw
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually can't at this point, because every
[[value]]
here is already the result of a previous StructuredClone call. The only call to StructuredClone that could throw is at enqueue time.