Re: [whatwg/streams] Support transferable streams (postMessage) (#1053)

@domenic commented on this pull request.



> @@ -36,16 +41,20 @@ urlPrefix: https://tc39.es/ecma262/; spec: ECMASCRIPT
   text: the typed array constructors table; url: #table-49
   text: typed array; url: #sec-typedarray-objects
   text: Number type; url: #sec-ecmascript-language-types-number-type
+  text: %ObjectPrototype%; url: #sec-properties-of-the-object-prototype-object

These days they would like us to move to `%Object.prototype%`. (Same URL, just change the linking text here and below.)

> @@ -965,6 +977,44 @@ default-reader-asynciterator-prototype-internal-slots">Asynchronous iteration</h
  1. Return [=a promise resolved with=] undefined.
 </div>
 
+<h4 id="rs-transfer">Transfer via postMessage()</h4>
+
+<dl class="domintro">
+ <dt><code>worker.postMessage(rs, [rs]);</code>

```suggestion
 <dt><code>destination.postMessage(rs, [rs]);</code>
```

> @@ -965,6 +977,44 @@ default-reader-asynciterator-prototype-internal-slots">Asynchronous iteration</h
  1. Return [=a promise resolved with=] undefined.
 </div>
 
+<h4 id="rs-transfer">Transfer via postMessage()</h4>
+
+<dl class="domintro">
+ <dt><code>worker.postMessage(rs, [rs]);</code>
+ <dd>
+  <p>Sends a ReadableStream to another frame, window or worker.

```suggestion
  <p>Sends a {{ReadableStream}} to another frame, window, or worker.
```

> @@ -965,6 +977,44 @@ default-reader-asynciterator-prototype-internal-slots">Asynchronous iteration</h
  1. Return [=a promise resolved with=] undefined.
 </div>
 
+<h4 id="rs-transfer">Transfer via postMessage()</h4>

```suggestion
<h4 id="rs-transfer">Transfer via `postMessage()`</h4>
```

> + </dd>
+</dl>
+
+<div algorithm="ReadableStream transfer steps">
+{{ReadableStream}} objects are [=transferable objects=]. Their [=transfer steps=], given |value| and
+|dataHolder|, are:
+
+ 1. If ! [$IsReadableStreamLocked$](|value|) is true, throw a "{{DataCloneError}}" {{DOMException}}.
+ 1. Let |port1| be a [=new=] {{MessagePort}} in [=the current Realm=].
+ 1. Let |port2| be a [=new=] {{MessagePort}} in [=the current Realm=].
+ 1. [=Entangle=] |port1| and |port2|.
+ 1. Let |writable| be a [=new=] {{WritableStream}}.
+ 1. Perform ! [$SetUpCrossRealmTransformWritable$](|writable|, |port1|).
+ 1. Let |promise| be ! [$ReadableStreamPipeTo$](|value|, |writable|, false, false, false).
+ 1. Set |promise|.\[[PromiseIsHandled]] to true.
+ 1. Set |dataHolder|.\[[port]] to ! [$StructuredSerializeWithTransfer$](port2, « |port2| »).

```suggestion
 1. Set |dataHolder|.\[[port]] to ! [$StructuredSerializeWithTransfer$](|port2|, « |port2| »).
```

> @@ -3625,6 +3678,44 @@ as seen for example in [[#example-ws-no-backpressure]].
  1. Return ? [$AcquireWritableStreamDefaultWriter$]([=this=]).
 </div>
 
+<h4 id="ws-transfer">Transfer via postMessage()</h4>

I'd suggest basically the same editorial tweaks for this section.

> @@ -965,6 +977,44 @@ default-reader-asynciterator-prototype-internal-slots">Asynchronous iteration</h
  1. Return [=a promise resolved with=] undefined.
 </div>
 
+<h4 id="rs-transfer">Transfer via postMessage()</h4>
+
+<dl class="domintro">
+ <dt><code>worker.postMessage(rs, [rs]);</code>

Alternately you could make it a bit more obvious with the new overload: `destination.postMessage(rs, { transfer: [rs] })`

> @@ -4937,6 +5031,50 @@ side=], or to terminate or error the stream.
  1. Return [=this=].\[[writable]].
 </div>
 
+<h4 id="ts-transfer">Transfer via postMessage()</h4>

Same editorial tweaks here

> +  <p>The transferred stream can be used exactly like the original. The original will become locked
+  and no longer directly usable.
+ </dd>
+</dl>
+
+<div algorithm="TransformStream transfer steps">
+{{TransformStream}} objects are [=transferable objects=]. Their [=transfer steps=], given |value|
+and |dataHolder|, are:
+
+<!-- Find a way to linkify ReadableStream transfer steps and WritableStream transfer steps -->
+
+ 1. If ! [$IsReadableStreamLocked$](|value|.\[[readable]]) is true, throw a "{{DataCloneError}}"
+    {{DOMException}}.
+ 1. If ! [$IsWritableStreamLocked$](|value|.\[[writable]]) is true, throw a "{{DataCloneError}}"
+    {{DOMException}}.
+ 1. Perform {{ReadableStream}} transfer steps with |value|.\[[readable]] and |readableDataHolder|.

I think you can just do the same thing you did for MessagePort, passing them into StructuredSerializeWithTransfer.

> @@ -5740,6 +5878,152 @@ for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
  1. Set |container|.\[[queueTotalSize]] to 0.
 </div>
 
+<h3 id="transferrable-streams">Transferable streams</h3>
+
+Transferable streams are implemented using a special kind of identity transform which has the
+[=writable side=] in one realm and the [=readable side=] in another realm. The following abstract
+operations are used to implement these "cross-realm transforms".
+
+<div algorithm>
+ <dfn abstract-op lt="PackAndPostMessage">PackAndPostMessage(|port|, |type|, |value|)</dfn> performs
+ the following steps:
+
+ 1. Let |message| be [$OrdinaryObjectCreate$](null).

```suggestion
 1. Let |message| be ! [$OrdinaryObjectCreate$](null).
```

> @@ -5740,6 +5878,152 @@ for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
  1. Set |container|.\[[queueTotalSize]] to 0.
 </div>
 
+<h3 id="transferrable-streams">Transferable streams</h3>
+
+Transferable streams are implemented using a special kind of identity transform which has the
+[=writable side=] in one realm and the [=readable side=] in another realm. The following abstract
+operations are used to implement these "cross-realm transforms".
+
+<div algorithm>
+ <dfn abstract-op lt="PackAndPostMessage">PackAndPostMessage(|port|, |type|, |value|)</dfn> performs
+ the following steps:
+
+ 1. Let |message| be [$OrdinaryObjectCreate$](null).
+ 1. Perform ! [$CreateDataProperty$](|message|, "`type`", |type|).
+ 1. Perform ! [$CreateDataProperty$](|message|, "`value`", |value|).
+ 1. Let |targetPort| be the port with which |port| is entangled, if any; otherwise let it be null.
+ 1. Let |options| be «[ "`transfer`" → « » ]».

TODO for me to make these linkable

> +<h3 id="transferrable-streams">Transferable streams</h3>
+
+Transferable streams are implemented using a special kind of identity transform which has the
+[=writable side=] in one realm and the [=readable side=] in another realm. The following abstract
+operations are used to implement these "cross-realm transforms".
+
+<div algorithm>
+ <dfn abstract-op lt="PackAndPostMessage">PackAndPostMessage(|port|, |type|, |value|)</dfn> performs
+ the following steps:
+
+ 1. Let |message| be [$OrdinaryObjectCreate$](null).
+ 1. Perform ! [$CreateDataProperty$](|message|, "`type`", |type|).
+ 1. Perform ! [$CreateDataProperty$](|message|, "`value`", |value|).
+ 1. Let |targetPort| be the port with which |port| is entangled, if any; otherwise let it be null.
+ 1. Let |options| be «[ "`transfer`" → « » ]».
+ 1. Run the [=message port post message steps=] providing |targetPort|, |message| and |options|.

```suggestion
 1. Run the [=message port post message steps=] providing |targetPort|, |message|, and |options|.
```

> @@ -5740,6 +5878,152 @@ for="value-with-size">value</dfn> and <dfn for="value-with-size">size</dfn>.
  1. Set |container|.\[[queueTotalSize]] to 0.
 </div>
 
+<h3 id="transferrable-streams">Transferable streams</h3>

Alphabetize the abstract ops in this section

> +    1. Set |backpressurePromise| to undefined.
+ 1. Add a handler for |port|'s {{MessagePort/messageerror}} event with the following steps:
+  1. Let |error| be a new "{{DataCloneError}}" {{DOMException}}.
+  1. Perform ! [$CrossRealmTransformSendError$](|port|, |error|).
+  1. Perform ! [$WritableStreamDefaultControllerError$](|controller|, |error|).
+  1. Disentangle |port|.
+ 1. Enable |port|'s [=port message queue=].
+ 1. Let |startAlgorithm| be an algorithm that returns undefined.
+ 1. Let |writeAlgorithm| be the following steps, taking a |chunk| argument:
+  1. If |backpressurePromise| is undefined, set |backpressurePromise| to
+     [=a promise resolved with=] undefined.
+  1. Return the result of [=reacting=] to |backpressurePromise| with the following
+     fulfillment steps:
+   1. Set |backpressurePromise| to [=a new promise=].
+   1. Let |promise| be ! [$PackAndPostMessageHandlingError$](|port|, "`chunk`", |chunk|).
+   1. If |promise| is a rejected promise, disentangle |port|.

This is pretty unusual. Can we do an "upon rejection" instead?

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/streams/pull/1053#pullrequestreview-447508345

Received on Monday, 13 July 2020 18:56:01 UTC