Skip to content

Commit 7197836

Browse files
author
isonmad
committed
Try to implement cloning in readablestream
Lacking an obvious way to actually postMessage a stream in this polyfill, this adds the 'cloning' stream type for testing purposes. Byte streams to come.
1 parent 4c7ae68 commit 7197836

File tree

4 files changed

+68
-13
lines changed

4 files changed

+68
-13
lines changed

index.bs

+28-7
Original file line numberDiff line numberDiff line change
@@ -461,10 +461,10 @@ ReadableStream(<var>underlyingSource</var> = {}, { <var>size</var>, <var>highWat
461461
1. If _highWaterMark_ is *undefined*, let _highWaterMark_ be *0*.
462462
1. Set *this*.[[readableStreamController]] to ? Construct(`<a idl>ReadableByteStreamController</a>`, « *this*,
463463
_underlyingSource_, _highWaterMark_ »).
464-
1. Otherwise, if _type_ is *undefined*,
464+
1. Otherwise, if _type_ is *undefined* or _type_ is `"cloning"`,
465465
1. If _highWaterMark_ is *undefined*, let _highWaterMark_ be *1*.
466466
1. Set *this*.[[readableStreamController]] to ? Construct(`<a idl>ReadableStreamDefaultController</a>`, « *this*,
467-
_underlyingSource_, _size_, _highWaterMark_ »).
467+
_underlyingSource_, _size_, _highWaterMark_, _type_ »).
468468
1. Otherwise, throw a *RangeError* exception.
469469
</emu-alg>
470470

@@ -747,12 +747,17 @@ The following internal method is implemented by each {{ReadableStream}} instance
747747
<emu-alg>
748748
1. If ! IsReadableStreamLocked(*this*) is *true*, throw a *TypeError* exception.
749749
1. If *this*.[[state]] is `"errored"`, throw a *TypeError* exception.
750+
1. Let _controller_ be *this*.[[readableStreamController]].
751+
1. If _controller_.[[targetRealm]] is *undefined*, throw a *TypeError* exception.
750752
1. Let _that_ be a new instance of <a idl>ReadableStream</a> in _targetRealm_.
751753
1. Set _that_.[[state]] to *this*.[[state]].
752754
1. Set _that_.[[disturbed]] to *this*.[[disturbed]].
753-
1. Let _controller_ be *this*.[[readableStreamController]].
754755
1. Set _controller_.[[controlledReadableStream]] to _that_.
755756
1. Set _that_.[[readableStreamController]] to _controller_.
757+
1. Let _queue_ be _controller_.[[queue]].
758+
1. Repeat for each Record {[[value]], [[size]]} _pair_ that is an element of _queue_,
759+
1. Set _pair_.[[value]] to ! <a abstract-op>StructuredClone</a>(_pair_.[[value]], _targetRealm_).
760+
1. Set _controller_.[[targetRealm]] to _targetRealm_.
756761
1. Set _this_.[[Detached]] to *true*.
757762
1. Return _that_.
758763
</emu-alg>
@@ -1472,6 +1477,12 @@ Instances of {{ReadableStreamDefaultController}} are created with the internal s
14721477
<th>Description (<em>non-normative</em>)</th>
14731478
</tr>
14741479
</thead>
1480+
<tr>
1481+
<td>\[[targetRealm]]
1482+
<td>Either *undefined*, or a Realm Record. If set to a Realm Record, ReadableStreamDefaultControllerEnqueue
1483+
will perform the structured clone algorithm on passed chunks, cloning them into the targetRealm
1484+
and enqueueing the result.
1485+
</tr>
14751486
<tr>
14761487
<td>\[[closeRequested]]
14771488
<td>A boolean flag indicating whether the stream has been closed by its <a>underlying source</a>, but still has
@@ -1520,7 +1531,7 @@ Instances of {{ReadableStreamDefaultController}} are created with the internal s
15201531
<h4 id="rs-default-controller-constructor" constructor for="ReadableStreamDefaultController"
15211532
lt="ReadableStreamDefaultController(stream, underlyingSource, size, highWaterMark)">new
15221533
ReadableStreamDefaultController(<var>stream</var>, <var>underlyingSource</var>, <var>size</var>,
1523-
<var>highWaterMark</var>)</h4>
1534+
<var>highWaterMark</var>, <var>type</var>)</h4>
15241535

15251536
<div class="note">
15261537
The <code>ReadableStreamDefaultController</code> constructor cannot be used directly; it only works on a
@@ -1534,6 +1545,8 @@ ReadableStreamDefaultController(<var>stream</var>, <var>underlyingSource</var>,
15341545
1. Set *this*.[[underlyingSource]] to _underlyingSource_.
15351546
1. Set *this*.[[queue]] to a new empty List.
15361547
1. Set *this*.[[started]], *this*.[[closeRequested]], *this*.[[pullAgain]], and *this*.[[pulling]] to *false*.
1548+
1. Set *this*.[[targetRealm]] to *undefined*.
1549+
1. If _type_ is `"cloning"`, set *this*.[[targetRealm]] to the current Realm Record.
15371550
1. Let _normalizedStrategy_ be ? ValidateAndNormalizeQueuingStrategy(_size_, _highWaterMark_).
15381551
1. Set *this*.[[strategySize]] to _normalizedStrategy_.[[size]] and *this*.[[strategyHWM]] to
15391552
_normalizedStrategy_.[[highWaterMark]].
@@ -1707,8 +1720,14 @@ asserts).
17071720
1. Let _stream_ be _controller_.[[controlledReadableStream]].
17081721
1. Assert: _controller_.[[closeRequested]] is *false*.
17091722
1. Assert: _stream_.[[state]] is `"readable"`.
1710-
1. If ! IsReadableStreamLocked(_stream_) is *true* and ! ReadableStreamGetNumReadRequests(_stream_) > *0*, perform
1711-
! ReadableStreamFulfillReadRequest(_stream_, _chunk_, *false*).
1723+
1. If ! IsReadableStreamLocked(_stream_) is *true* and ! ReadableStreamGetNumReadRequests(_stream_) > *0*,
1724+
1. If _controller_.[[targetRealm]] is not *undefined*,
1725+
1. Let _chunk_ be <a abstract-op>StructuredClone</a>(_chunk_, _controller_.[[targetRealm]]).
1726+
1. If _chunk_ is an abrupt completion,
1727+
1. Perform ! ReadableStreamDefaultControllerErrorIfNeeded(_controller_, _chunk_.[[Value]]).
1728+
1. Return _chunk_.
1729+
1. Let _chunk_ be _chunk_.[[Value]].
1730+
1. Perform ! ReadableStreamFulfillReadRequest(_stream_, _chunk_, *false*).
17121731
1. Otherwise,
17131732
1. Let _chunkSize_ be *1*.
17141733
1. If _controller_.[[strategySize]] is not *undefined*,
@@ -3615,11 +3634,13 @@ throughout the rest of this standard.
36153634
</emu-alg>
36163635

36173636
<h4 id="enqueue-value-with-size" aoid="EnqueueValueWithSize" throws>EnqueueValueWithSize ( <var>queue</var>,
3618-
<var>value</var>, <var>size</var> )</h4>
3637+
<var>value</var>, <var>size</var>, <var>targetRealm</var> )</h4>
36193638

36203639
<emu-alg>
3640+
1. If _targetRealm_ was not passed, let _targetRealm_ be *undefined*.
36213641
1. Let _size_ be ? ToNumber(_size_).
36223642
1. If ! IsFiniteNonNegativeNumber(_size_) is *false*, throw a *RangeError* exception.
3643+
1. If _targetRealm_ is not *undefined*, let _value_ be the result of ? StructuredClone(_value_, _targetRealm_).
36233644
1. Append Record {[[value]]: _value_, [[size]]: _size_} as the last element of _queue_.
36243645
</emu-alg>
36253646

reference-implementation/lib/queue-with-sizes.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22
const assert = require('assert');
3+
/* structured clone is impossible to truly polyfill, but closest match */
4+
const StructuredClone = require('realistic-structured-clone');
35
const { IsFiniteNonNegativeNumber } = require('./helpers.js');
46

57
exports.DequeueValue = queue => {
@@ -11,12 +13,16 @@ exports.DequeueValue = queue => {
1113
return pair.value;
1214
};
1315

14-
exports.EnqueueValueWithSize = (queue, value, size) => {
16+
exports.EnqueueValueWithSize = (queue, value, size, targetRealm) => {
1517
size = Number(size);
1618
if (!IsFiniteNonNegativeNumber(size)) {
1719
throw new RangeError('Size must be a finite, non-NaN, non-negative number.');
1820
}
1921

22+
if (targetRealm !== undefined) {
23+
value = StructuredClone(value/* , targetRealm*/);
24+
}
25+
2026
queue.push({ value, size });
2127

2228
if (queue._totalSize === undefined) {

reference-implementation/lib/readable-stream.js

+30-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22
const assert = require('assert');
3+
/* structured clone is impossible to truly polyfill, but closest match */
4+
const StructuredClone = require('realistic-structured-clone');
35
const { ArrayBufferCopy, CreateIterResultObject, IsFiniteNonNegativeNumber, InvokeOrNoop, PromiseInvokeOrNoop,
46
SameRealmTransfer, ValidateAndNormalizeQueuingStrategy, ValidateAndNormalizeHighWaterMark } =
57
require('./helpers.js');
@@ -35,11 +37,13 @@ class ReadableStream {
3537
highWaterMark = 0;
3638
}
3739
this._readableStreamController = new ReadableByteStreamController(this, underlyingSource, highWaterMark);
38-
} else if (type === undefined) {
40+
} else if (type === undefined || type === 'cloning') {
3941
if (highWaterMark === undefined) {
4042
highWaterMark = 1;
4143
}
42-
this._readableStreamController = new ReadableStreamDefaultController(this, underlyingSource, size, highWaterMark);
44+
45+
this._readableStreamController =
46+
new ReadableStreamDefaultController(this, underlyingSource, size, highWaterMark, type);
4347
} else {
4448
throw new RangeError('Invalid type is specified');
4549
}
@@ -261,14 +265,21 @@ class ReadableStream {
261265
if (this._state === 'errored') {
262266
throw new TypeError('Cannot transfer an errored stream');
263267
}
268+
const controller = this._readableStreamController;
269+
if (controller._targetRealm === undefined) {
270+
throw new TypeError('Only cloning streams are transferable');
271+
}
264272
/* can't exactly polyfill realm-transfer */
265273
const that = new ReadableStream();
266274
that._state = this._state;
267275
that._disturbed = this._disturbed;
268276

269-
const controller = this._readableStreamController;
270277
controller._controlledReadableStream = that;
271278
that._readableStreamController = controller;
279+
for (const pair of controller._queue) {
280+
pair.value = StructuredClone(pair.value/* , targetRealm*/);
281+
}
282+
controller._targetRealm = that; // controller.[[targetRealm]] = targetRealm
272283
this._Detached = true;
273284

274285
return that;
@@ -866,7 +877,7 @@ function ReadableStreamDefaultReaderRead(reader) {
866877
// Controllers
867878

868879
class ReadableStreamDefaultController {
869-
constructor(stream, underlyingSource, size, highWaterMark) {
880+
constructor(stream, underlyingSource, size, highWaterMark, type) {
870881
if (IsReadableStream(stream) === false) {
871882
throw new TypeError('ReadableStreamDefaultController can only be constructed with a ReadableStream instance');
872883
}
@@ -885,6 +896,12 @@ class ReadableStreamDefaultController {
885896
this._closeRequested = false;
886897
this._pullAgain = false;
887898
this._pulling = false;
899+
this._targetRealm = undefined;
900+
901+
if (type === 'cloning') {
902+
/* no way to represent a Realm Record in polyfill */
903+
this._targetRealm = stream; // set this.[[targetRealm]] to current Realm Record
904+
}
888905

889906
const normalizedStrategy = ValidateAndNormalizeQueuingStrategy(size, highWaterMark);
890907
this._strategySize = normalizedStrategy.size;
@@ -1089,6 +1106,14 @@ function ReadableStreamDefaultControllerEnqueue(controller, chunk) {
10891106
assert(stream._state === 'readable');
10901107

10911108
if (IsReadableStreamLocked(stream) === true && ReadableStreamGetNumReadRequests(stream) > 0) {
1109+
if (controller._targetRealm !== undefined) {
1110+
try {
1111+
chunk = StructuredClone(chunk/* , controller._targetRealm */);
1112+
} catch (cloneE) {
1113+
ReadableStreamDefaultControllerErrorIfNeeded(controller, cloneE);
1114+
throw cloneE;
1115+
}
1116+
}
10921117
ReadableStreamFulfillReadRequest(stream, chunk, false);
10931118
} else {
10941119
let chunkSize = 1;
@@ -1103,7 +1128,7 @@ function ReadableStreamDefaultControllerEnqueue(controller, chunk) {
11031128
}
11041129

11051130
try {
1106-
EnqueueValueWithSize(controller._queue, chunk, chunkSize);
1131+
EnqueueValueWithSize(controller._queue, chunk, chunkSize, controller._targetRealm);
11071132
} catch (enqueueE) {
11081133
ReadableStreamDefaultControllerErrorIfNeeded(controller, enqueueE);
11091134
throw enqueueE;

reference-implementation/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
"Takeshi Yoshino <[email protected]>"
1616
],
1717
"license": "(CC0-1.0 OR MIT)",
18+
"dependencies": {
19+
"realistic-structured-clone": "^0.0.3"
20+
},
1821
"devDependencies": {
1922
"eslint": "^3.2.2",
2023
"glob": "^7.0.3",

0 commit comments

Comments
 (0)