From 480b2a364e7f2d2532befed032f597719d05cfa5 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Wed, 5 Apr 2017 14:55:44 +0900 Subject: [PATCH] Update for changes to HTML's structured cloning/transferring Closes #701. --- index.bs | 48 ++++++++++++------- reference-implementation/lib/helpers.js | 2 +- .../lib/readable-stream.js | 16 +++---- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/index.bs b/index.bs index 4e591b91e..219e612d8 100644 --- a/index.bs +++ b/index.bs @@ -331,7 +331,7 @@ copies. An important thing to note here is that the final buffer value is different from the startingAB, but it (and all intermediate buffers) shares the same backing memory allocation. At each - step, the buffer is transferred to a new {{ArrayBuffer}} object. The + step, the buffer is transferred to a new {{ArrayBuffer}} object. The newView is a new {{Uint8Array}}, with that {{ArrayBuffer}} object as its buffer property, the offset that bytes were written to as its byteOffset property, and the number of bytes that were written as its byteLength property. @@ -706,9 +706,7 @@ ReadableStream(underlyingSource = {}, { size, highWat cancellation reason will then be propagated to the stream's underlying source. Note that the chunks seen in each branch will be the same object. If the chunks are not immutable, this could - allow interference between the two branches. (Let us know - if you think we ought to add an option to tee that creates structured - clones of the chunks for each branch.) + allow interference between the two branches. @@ -797,11 +795,14 @@ readable stream is locked to a reader. This abstract operation is meant to be called from other specifications that may wish to tee a given readable stream. -The second argument, cloneForBranch2, governs whether or not the data from the original stream will be structured cloned before appearing in the second of the returned branches. This is +The second argument, cloneForBranch2, governs whether or not the data from the original stream will be cloned +(using HTML's serializable objects framework) before appearing in the second of the returned branches. This is useful for scenarios where both branches are to be consumed in such a way that they might otherwise interfere with each -other, such as by transfering their chunks. However, it does introduce a -noticable asymmetry between the two branches. [[!HTML]] +other, such as by transferring their chunks. However, it does introduce a +noticable asymmetry between the two branches, and limits the possible chunks to serializable ones. [[!HTML]] + +

In this standard ReadableStreamTee is always called with cloneForBranch2 set to +false; other specifications pass true.

1. Assert: ! IsReadableStream(_stream_) is *true*. @@ -857,7 +858,8 @@ a ReadableStreamTee pull function F is called, it performs the follow 1. If _teeState_.[[closedOrErrored]] is *true*, return. 1. Let _value1_ and _value2_ be _value_. 1. If _teeState_.[[canceled2]] is *false* and _cloneForBranch2_ is *true*, set _value2_ to ? StructuredClone(_value2_). + abstract-op>StructuredDeserialize(StructuredSerialize(_value2_), the current Realm + Record). 1. If _teeState_.[[canceled1]] is *false*, perform ? ReadableStreamDefaultControllerEnqueue(_branch1_, _value1_). 1. If _teeState_.[[canceled2]] is *false*, perform ? ReadableStreamDefaultControllerEnqueue(_branch2_, _value2_). @@ -2201,7 +2203,7 @@ nothrow>ReadableByteStreamControllerEnqueue ( controller, chunk< 1. Let _buffer_ be _chunk_.[[ViewedArrayBuffer]]. 1. Let _byteOffset_ be _chunk_.[[ByteOffset]]. 1. Let _byteLength_ be _chunk_.[[ByteLength]]. - 1. Let _transferredBuffer_ be ! Transfer(_buffer_, the current Realm Record). + 1. Let _transferredBuffer_ be ! TransferArrayBuffer(_buffer_). 1. If ! ReadableStreamHasDefaultReader(_stream_) is *true* 1. If ! ReadableStreamGetNumReadRequests(_stream_) is *0*, 1. Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(_controller_, _transferredBuffer_, _byteOffset_, @@ -2360,8 +2362,7 @@ nothrow>ReadableByteStreamControllerPullInto ( controller, view< _view_.[[ByteOffset]], [[byteLength]]: _view_.[[ByteLength]], [[bytesFilled]]: *0*, [[elementSize]]: _elementSize_, [[ctor]]: _ctor_, [[readerType]]: `"byob"`}. 1. If _controller_.[[pendingPullIntos]] is not empty, - 1. Set _pullIntoDescriptor_.[[buffer]] to ! Transfer(_pullIntoDescriptor_.[[buffer]], the current - Realm Record). + 1. Set _pullIntoDescriptor_.[[buffer]] to ! TransferArrayBuffer(_pullIntoDescriptor_.[[buffer]]). 1. Append _pullIntoDescriptor_ as the last element of _controller_.[[pendingPullIntos]]. 1. Return ! ReadableStreamAddReadIntoRequest(_stream_). 1. If _stream_.[[state]] is `"closed"`, @@ -2376,8 +2377,7 @@ nothrow>ReadableByteStreamControllerPullInto ( controller, view< 1. Let _e_ be a *TypeError* exception. 1. Perform ! ReadableByteStreamControllerError(_controller_, _e_). 1. Return a promise rejected with _e_. - 1. Set _pullIntoDescriptor_.[[buffer]] to ! Transfer(_pullIntoDescriptor_.[[buffer]], the current - Realm Record). + 1. Set _pullIntoDescriptor_.[[buffer]] to ! TransferArrayBuffer(_pullIntoDescriptor_.[[buffer]]). 1. Append _pullIntoDescriptor_ as the last element of _controller_.[[pendingPullIntos]]. 1. Let _promise_ be ! ReadableStreamAddReadIntoRequest(_stream_). 1. Perform ! ReadableByteStreamControllerCallPullIfNeeded(_controller_). @@ -2399,8 +2399,7 @@ throws>ReadableByteStreamControllerRespond ( controller, bytesWr nothrow>ReadableByteStreamControllerRespondInClosedState ( controller, firstDescriptor ) - 1. Set _firstDescriptor_.[[buffer]] to ! Transfer(_firstDescriptor_.[[buffer]], the current Realm - Record). + 1. Set _firstDescriptor_.[[buffer]] to ! TransferArrayBuffer(_firstDescriptor_.[[buffer]]). 1. Assert: _firstDescriptor_.[[bytesFilled]] is *0*. 1. Let _stream_ be _controller_.[[controlledReadableStream]]. 1. Repeat the following steps while ! ReadableStreamGetNumReadIntoRequests(_stream_) > *0*, @@ -2426,8 +2425,7 @@ aoid="ReadableByteStreamControllerRespondInReadableState" throws>ReadableByteStr %ArrayBuffer%). 1. Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(_controller_, _remainder_, *0*, _remainder_.[[ByteLength]]). - 1. Set _pullIntoDescriptor_.[[buffer]] to ! Transfer(_pullIntoDescriptor_.[[buffer]], the current - Realm Record). + 1. Set _pullIntoDescriptor_.[[buffer]] to ! TransferArrayBuffer(_pullIntoDescriptor_.[[buffer]]). 1. Set _pullIntoDescriptor_.[[bytesFilled]] to _pullIntoDescriptor_.[[bytesFilled]] − _remainderSize_. 1. Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(_controller_.[[controlledReadableStream]], _pullIntoDescriptor_). @@ -3924,6 +3922,20 @@ A few abstract operations are used in this specification for utility purposes. W 1. Otherwise, return a promise resolved with _returnValue_.[[Value]]. +

TransferArrayBuffer ( O )

+ + + 1. Assert: Type(_O_) is Object. + 1. Assert: _O_ has an [[ArrayBufferData]] internal slot. + 1. Assert: ! IsDetachedBuffer(_O_) is *false*. + 1. Let _arrayBufferData_ be _O_.[[ArrayBufferData]]. + 1. Let _arrayBufferByteLength_ be _O_.[[ArrayBufferByteLength]]. + 1. Perform ! DetachArrayBuffer(_O_). + 1. Return a new ArrayBuffer object (created in the current Realm Record) whose + [[ArrayBufferData]] internal slot value is _arrayBufferData_ and whose [[ArrayBufferByteLength]] internal slot + value is _arrayBufferByteLength_. + +

ValidateAndNormalizeHighWaterMark ( highWaterMark )

diff --git a/reference-implementation/lib/helpers.js b/reference-implementation/lib/helpers.js index 5f48df1c4..b7a15b544 100644 --- a/reference-implementation/lib/helpers.js +++ b/reference-implementation/lib/helpers.js @@ -101,7 +101,7 @@ exports.PromiseInvokeOrPerformFallback = (O, P, args, F, argsF) => { }; // Not implemented correctly -exports.SameRealmTransfer = O => O; +exports.TransferArrayBuffer = O => O.slice(); exports.ValidateAndNormalizeHighWaterMark = highWaterMark => { highWaterMark = Number(highWaterMark); diff --git a/reference-implementation/lib/readable-stream.js b/reference-implementation/lib/readable-stream.js index b4a3b7609..b96e117c3 100644 --- a/reference-implementation/lib/readable-stream.js +++ b/reference-implementation/lib/readable-stream.js @@ -1,7 +1,7 @@ 'use strict'; const assert = require('assert'); const { ArrayBufferCopy, CreateIterResultObject, IsFiniteNonNegativeNumber, InvokeOrNoop, PromiseInvokeOrNoop, - SameRealmTransfer, ValidateAndNormalizeQueuingStrategy, ValidateAndNormalizeHighWaterMark } = + TransferArrayBuffer, ValidateAndNormalizeQueuingStrategy, ValidateAndNormalizeHighWaterMark } = require('./helpers.js'); const { createArrayFromList, createDataProperty, typeIsObject } = require('./helpers.js'); const { rethrowAssertionErrorRejection } = require('./utils.js'); @@ -383,9 +383,9 @@ function create_ReadableStreamTeePullFunction() { const value2 = value; // There is no way to access the cloning code right now in the reference implementation. - // If we add one then we'll need an implementation for StructuredClone. + // If we add one then we'll need an implementation for serializable objects. // if (teeState.canceled2 === false && cloneForBranch2 === true) { - // value2 = StructuredClone(value2); + // value2 = StructuredDeserialize(StructuredSerialize(value2)); // } if (teeState.canceled1 === false) { @@ -1581,7 +1581,7 @@ function ReadableByteStreamControllerPullInto(controller, view) { }; if (controller._pendingPullIntos.length > 0) { - pullIntoDescriptor.buffer = SameRealmTransfer(pullIntoDescriptor.buffer); + pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer); controller._pendingPullIntos.push(pullIntoDescriptor); // No ReadableByteStreamControllerCallPullIfNeeded() call since: @@ -1613,7 +1613,7 @@ function ReadableByteStreamControllerPullInto(controller, view) { } } - pullIntoDescriptor.buffer = SameRealmTransfer(pullIntoDescriptor.buffer); + pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer); controller._pendingPullIntos.push(pullIntoDescriptor); const promise = ReadableStreamAddReadIntoRequest(stream); @@ -1624,7 +1624,7 @@ function ReadableByteStreamControllerPullInto(controller, view) { } function ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor) { - firstDescriptor.buffer = SameRealmTransfer(firstDescriptor.buffer); + firstDescriptor.buffer = TransferArrayBuffer(firstDescriptor.buffer); assert(firstDescriptor.bytesFilled === 0, 'bytesFilled must be 0'); @@ -1658,7 +1658,7 @@ function ReadableByteStreamControllerRespondInReadableState(controller, bytesWri ReadableByteStreamControllerEnqueueChunkToQueue(controller, remainder, 0, remainder.byteLength); } - pullIntoDescriptor.buffer = SameRealmTransfer(pullIntoDescriptor.buffer); + pullIntoDescriptor.buffer = TransferArrayBuffer(pullIntoDescriptor.buffer); pullIntoDescriptor.bytesFilled -= remainderSize; ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableStream, pullIntoDescriptor); @@ -1755,7 +1755,7 @@ function ReadableByteStreamControllerEnqueue(controller, chunk) { const buffer = chunk.buffer; const byteOffset = chunk.byteOffset; const byteLength = chunk.byteLength; - const transferredBuffer = SameRealmTransfer(buffer); + const transferredBuffer = TransferArrayBuffer(buffer); if (ReadableStreamHasDefaultReader(stream) === true) { if (ReadableStreamGetNumReadRequests(stream) === 0) {