Commit e33ce4df authored by Adam Rice's avatar Adam Rice Committed by Commit Bot

Update TransformStream to latest standard version

Update TransformStream to match the standard version
https://github.com/whatwg/streams/commit/7b8dffe8ec270bb918340e011349e2c1b5521a15.

Significant changes:

Method lookups are now cached at construction time. Changing
underlyingSink.write after construction will no longer do anything. This
is https://github.com/whatwg/streams/pull/860.

When the readable side is cancelled, the stored error on the writable side will
be set to the |reason| that was passed to cancel(), rather than a TypeError.
This is https://github.com/whatwg/streams/pull/903.

Bug: 780689, 820387

Change-Id: I33c59541eea1bfc5f96722bce750ba4fa7ff96ee
Reviewed-on: https://chromium-review.googlesource.com/964112Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Commit-Queue: Adam Rice <ricea@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545038}
parent 9e767b2f
This is a testharness.js-based test.
PASS TransformStream errors thrown in transform put the writable and readable in an errored state
PASS TransformStream errors thrown in flush put the writable and readable in an errored state
PASS errored TransformStream should not enqueue new chunks
PASS TransformStream transformer.start() rejected promise should error the stream
PASS when controller.error is followed by a rejection, the error reason should come from controller.error
PASS TransformStream constructor should throw when start does
PASS when strategy.size throws inside start(), the constructor should throw the same error
PASS when strategy.size calls controller.error() then throws, the constructor should throw the first error
PASS cancelling the readable side should error the writable
PASS it should be possible to error the readable between close requested and complete
PASS an exception from transform() should error the stream if terminate has been requested but not completed
PASS abort should set the close reason for the writable when it happens before cancel during start, but cancel should still succeed
PASS abort should set the close reason for the writable when it happens before cancel during underlying sink write, but cancel should still succeed
PASS controller.error() should do nothing the second time it is called
PASS controller.error() should do nothing after readable.cancel()
PASS controller.error() should do nothing after writable.abort() has completed
PASS controller.error() should do nothing after a transformer method has thrown an exception
PASS erroring during write with backpressure should result in the write failing
FAIL a write() that was waiting for backpressure should reject if the writable is aborted assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1")
FAIL the readable should be errored with the reason passed to the writable abort() method assert_throws: read() should reject with thrownError function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1")
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS TransformStream errors thrown in transform put the writable and readable in an errored state
PASS TransformStream errors thrown in flush put the writable and readable in an errored state
PASS errored TransformStream should not enqueue new chunks
PASS TransformStream transformer.start() rejected promise should error the stream
PASS when controller.error is followed by a rejection, the error reason should come from controller.error
PASS TransformStream constructor should throw when start does
PASS when strategy.size throws inside start(), the constructor should throw the same error
PASS when strategy.size calls controller.error() then throws, the constructor should throw the first error
PASS cancelling the readable side should error the writable
PASS it should be possible to error the readable between close requested and complete
PASS an exception from transform() should error the stream if terminate has been requested but not completed
PASS abort should set the close reason for the writable when it happens before cancel during start, but cancel should still succeed
PASS abort should set the close reason for the writable when it happens before cancel during underlying sink write, but cancel should still succeed
PASS controller.error() should do nothing the second time it is called
PASS controller.error() should do nothing after readable.cancel()
PASS controller.error() should do nothing after writable.abort() has completed
PASS controller.error() should do nothing after a transformer method has thrown an exception
PASS erroring during write with backpressure should result in the write failing
FAIL a write() that was waiting for backpressure should reject if the writable is aborted assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1")
FAIL the readable should be errored with the reason passed to the writable abort() method assert_throws: read() should reject with thrownError function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1")
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS Service worker test setup
PASS TransformStream errors thrown in transform put the writable and readable in an errored state
PASS TransformStream errors thrown in flush put the writable and readable in an errored state
PASS errored TransformStream should not enqueue new chunks
PASS TransformStream transformer.start() rejected promise should error the stream
PASS when controller.error is followed by a rejection, the error reason should come from controller.error
PASS TransformStream constructor should throw when start does
PASS when strategy.size throws inside start(), the constructor should throw the same error
PASS when strategy.size calls controller.error() then throws, the constructor should throw the first error
PASS cancelling the readable side should error the writable
PASS it should be possible to error the readable between close requested and complete
PASS an exception from transform() should error the stream if terminate has been requested but not completed
PASS abort should set the close reason for the writable when it happens before cancel during start, but cancel should still succeed
PASS abort should set the close reason for the writable when it happens before cancel during underlying sink write, but cancel should still succeed
PASS controller.error() should do nothing the second time it is called
PASS controller.error() should do nothing after readable.cancel()
PASS controller.error() should do nothing after writable.abort() has completed
PASS controller.error() should do nothing after a transformer method has thrown an exception
PASS erroring during write with backpressure should result in the write failing
FAIL a write() that was waiting for backpressure should reject if the writable is aborted assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1")
FAIL the readable should be errored with the reason passed to the writable abort() method assert_throws: read() should reject with thrownError function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1")
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS TransformStream errors thrown in transform put the writable and readable in an errored state
PASS TransformStream errors thrown in flush put the writable and readable in an errored state
PASS errored TransformStream should not enqueue new chunks
PASS TransformStream transformer.start() rejected promise should error the stream
PASS when controller.error is followed by a rejection, the error reason should come from controller.error
PASS TransformStream constructor should throw when start does
PASS when strategy.size throws inside start(), the constructor should throw the same error
PASS when strategy.size calls controller.error() then throws, the constructor should throw the first error
PASS cancelling the readable side should error the writable
PASS it should be possible to error the readable between close requested and complete
PASS an exception from transform() should error the stream if terminate has been requested but not completed
PASS abort should set the close reason for the writable when it happens before cancel during start, but cancel should still succeed
PASS abort should set the close reason for the writable when it happens before cancel during underlying sink write, but cancel should still succeed
PASS controller.error() should do nothing the second time it is called
PASS controller.error() should do nothing after readable.cancel()
PASS controller.error() should do nothing after writable.abort() has completed
PASS controller.error() should do nothing after a transformer method has thrown an exception
PASS erroring during write with backpressure should result in the write failing
FAIL a write() that was waiting for backpressure should reject if the writable is aborted assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1")
FAIL the readable should be errored with the reason passed to the writable abort() method assert_throws: read() should reject with thrownError function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: bad things are happening!" ("error1")
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS TransformStreamDefaultController should not be exported on the global object
PASS TransformStream.prototype.constructor should have standard properties
PASS TransformStream.prototype.constructor should be a constructor
PASS TransformStream.prototype.readable should have standard properties
PASS TransformStream.prototype.readable should be a getter
PASS TransformStream.prototype.writable should have standard properties
PASS TransformStream.prototype.writable should be a getter
PASS TransformStream.prototype should have exactly the expected properties
PASS TransformStreamDefaultController.prototype.constructor should have standard properties
FAIL TransformStreamDefaultController.prototype.constructor should be a constructor assert_equals: constructor should take 0 arguments expected 0 but got 1
PASS TransformStreamDefaultController.prototype.desiredSize should have standard properties
PASS TransformStreamDefaultController.prototype.desiredSize should be a getter
PASS TransformStreamDefaultController.prototype.enqueue should have standard properties
PASS TransformStreamDefaultController.prototype.enqueue should be a method
PASS TransformStreamDefaultController.prototype.error should have standard properties
PASS TransformStreamDefaultController.prototype.error should be a method
PASS TransformStreamDefaultController.prototype.terminate should have standard properties
PASS TransformStreamDefaultController.prototype.terminate should be a method
PASS TransformStreamDefaultController.prototype should have exactly the expected properties
PASS transformer method start should be called with the right number of arguments
PASS transformer method start should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method start assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
PASS transformer method transform should be called with the right number of arguments
PASS transformer method transform should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method transform assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
PASS transformer method flush should be called with the right number of arguments
PASS transformer method flush should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method flush assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS TransformStreamDefaultController should not be exported on the global object
PASS TransformStream.prototype.constructor should have standard properties
PASS TransformStream.prototype.constructor should be a constructor
PASS TransformStream.prototype.readable should have standard properties
PASS TransformStream.prototype.readable should be a getter
PASS TransformStream.prototype.writable should have standard properties
PASS TransformStream.prototype.writable should be a getter
PASS TransformStream.prototype should have exactly the expected properties
PASS TransformStreamDefaultController.prototype.constructor should have standard properties
FAIL TransformStreamDefaultController.prototype.constructor should be a constructor assert_equals: constructor should take 0 arguments expected 0 but got 1
PASS TransformStreamDefaultController.prototype.desiredSize should have standard properties
PASS TransformStreamDefaultController.prototype.desiredSize should be a getter
PASS TransformStreamDefaultController.prototype.enqueue should have standard properties
PASS TransformStreamDefaultController.prototype.enqueue should be a method
PASS TransformStreamDefaultController.prototype.error should have standard properties
PASS TransformStreamDefaultController.prototype.error should be a method
PASS TransformStreamDefaultController.prototype.terminate should have standard properties
PASS TransformStreamDefaultController.prototype.terminate should be a method
PASS TransformStreamDefaultController.prototype should have exactly the expected properties
PASS transformer method start should be called with the right number of arguments
PASS transformer method start should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method start assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
PASS transformer method transform should be called with the right number of arguments
PASS transformer method transform should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method transform assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
PASS transformer method flush should be called with the right number of arguments
PASS transformer method flush should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method flush assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS Service worker test setup
PASS TransformStreamDefaultController should not be exported on the global object
PASS TransformStream.prototype.constructor should have standard properties
PASS TransformStream.prototype.constructor should be a constructor
PASS TransformStream.prototype.readable should have standard properties
PASS TransformStream.prototype.readable should be a getter
PASS TransformStream.prototype.writable should have standard properties
PASS TransformStream.prototype.writable should be a getter
PASS TransformStream.prototype should have exactly the expected properties
PASS TransformStreamDefaultController.prototype.constructor should have standard properties
FAIL TransformStreamDefaultController.prototype.constructor should be a constructor assert_equals: constructor should take 0 arguments expected 0 but got 1
PASS TransformStreamDefaultController.prototype.desiredSize should have standard properties
PASS TransformStreamDefaultController.prototype.desiredSize should be a getter
PASS TransformStreamDefaultController.prototype.enqueue should have standard properties
PASS TransformStreamDefaultController.prototype.enqueue should be a method
PASS TransformStreamDefaultController.prototype.error should have standard properties
PASS TransformStreamDefaultController.prototype.error should be a method
PASS TransformStreamDefaultController.prototype.terminate should have standard properties
PASS TransformStreamDefaultController.prototype.terminate should be a method
PASS TransformStreamDefaultController.prototype should have exactly the expected properties
PASS transformer method start should be called with the right number of arguments
PASS transformer method start should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method start assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
PASS transformer method transform should be called with the right number of arguments
PASS transformer method transform should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method transform assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
PASS transformer method flush should be called with the right number of arguments
PASS transformer method flush should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method flush assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS TransformStreamDefaultController should not be exported on the global object
PASS TransformStream.prototype.constructor should have standard properties
PASS TransformStream.prototype.constructor should be a constructor
PASS TransformStream.prototype.readable should have standard properties
PASS TransformStream.prototype.readable should be a getter
PASS TransformStream.prototype.writable should have standard properties
PASS TransformStream.prototype.writable should be a getter
PASS TransformStream.prototype should have exactly the expected properties
PASS TransformStreamDefaultController.prototype.constructor should have standard properties
FAIL TransformStreamDefaultController.prototype.constructor should be a constructor assert_equals: constructor should take 0 arguments expected 0 but got 1
PASS TransformStreamDefaultController.prototype.desiredSize should have standard properties
PASS TransformStreamDefaultController.prototype.desiredSize should be a getter
PASS TransformStreamDefaultController.prototype.enqueue should have standard properties
PASS TransformStreamDefaultController.prototype.enqueue should be a method
PASS TransformStreamDefaultController.prototype.error should have standard properties
PASS TransformStreamDefaultController.prototype.error should be a method
PASS TransformStreamDefaultController.prototype.terminate should have standard properties
PASS TransformStreamDefaultController.prototype.terminate should be a method
PASS TransformStreamDefaultController.prototype should have exactly the expected properties
PASS transformer method start should be called with the right number of arguments
PASS transformer method start should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method start assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
PASS transformer method transform should be called with the right number of arguments
PASS transformer method transform should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method transform assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
PASS transformer method flush should be called with the right number of arguments
PASS transformer method flush should be called even when it's located on the prototype chain
FAIL unexpected properties should not be accessed when calling transformer method flush assert_array_equals: expected properties should be got lengths differ, expected 5 got 3
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS enqueue() inside size() should work
PASS terminate() inside size() should work
PASS error() inside size() should work
PASS desiredSize inside size() should work
PASS readable cancel() inside size() should work
PASS pipeTo() inside size() should work
PASS read() inside of size() should work
PASS writer.write() inside size() should work
PASS synchronous writer.write() inside size() should work
PASS writer.close() inside size() should work
FAIL writer.abort() inside size() should work assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: error1" ("error1")
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS enqueue() inside size() should work
PASS terminate() inside size() should work
PASS error() inside size() should work
PASS desiredSize inside size() should work
PASS readable cancel() inside size() should work
PASS pipeTo() inside size() should work
PASS read() inside of size() should work
PASS writer.write() inside size() should work
PASS synchronous writer.write() inside size() should work
PASS writer.close() inside size() should work
FAIL writer.abort() inside size() should work assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: error1" ("error1")
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS Service worker test setup
PASS enqueue() inside size() should work
PASS terminate() inside size() should work
PASS error() inside size() should work
PASS desiredSize inside size() should work
PASS readable cancel() inside size() should work
PASS pipeTo() inside size() should work
PASS read() inside of size() should work
PASS writer.write() inside size() should work
PASS synchronous writer.write() inside size() should work
PASS writer.close() inside size() should work
FAIL writer.abort() inside size() should work assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: error1" ("error1")
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS enqueue() inside size() should work
PASS terminate() inside size() should work
PASS error() inside size() should work
PASS desiredSize inside size() should work
PASS readable cancel() inside size() should work
PASS pipeTo() inside size() should work
PASS read() inside of size() should work
PASS writer.write() inside size() should work
PASS synchronous writer.write() inside size() should work
PASS writer.close() inside size() should work
FAIL writer.abort() inside size() should work assert_throws: read() should reject function "function() { throw e }" threw object "TypeError: The writable stream has been aborted" ("TypeError") expected object "error1: error1" ("error1")
Harness: the test ran to completion.
...@@ -216,10 +216,11 @@ ...@@ -216,10 +216,11 @@
// '! IsPropertyKey(methodName) is true.'); // '! IsPropertyKey(methodName) is true.');
// assert(algoArgCount === 0 || algoArgCount === 1, // assert(algoArgCount === 0 || algoArgCount === 1,
// 'algoArgCount is 0 or 1.'); // 'algoArgCount is 0 or 1.');
// assert(typeof methodNameForError === 'string', // assert(
// typeof methodNameForError === 'string',
// 'methodNameForError is a string'); // 'methodNameForError is a string');
const method = resolveMethod(underlyingObject, methodName, const method =
methodNameForError); resolveMethod(underlyingObject, methodName, methodNameForError);
// The implementation uses bound functions rather than lambdas where // The implementation uses bound functions rather than lambdas where
// possible to give the compiler the maximum opportunity to optimise. // possible to give the compiler the maximum opportunity to optimise.
if (method === undefined) { if (method === undefined) {
...@@ -242,19 +243,19 @@ ...@@ -242,19 +243,19 @@
// '! IsPropertyKey(methodName) is true.'); // '! IsPropertyKey(methodName) is true.');
// assert(algoArgCount === 0 || algoArgCount === 1, // assert(algoArgCount === 0 || algoArgCount === 1,
// 'algoArgCount is 0 or 1.'); // 'algoArgCount is 0 or 1.');
// assert(typeof controller === 'object' // assert(typeof controller === 'object', 'controller is an object');
// 'controller is an object'); // assert(
// assert(typeof methodNameForError === 'string', // typeof methodNameForError === 'string',
// 'methodNameForError is a string'); // 'methodNameForError is a string');
const method = resolveMethod(underlyingObject, methodName, const method =
methodNameForError); resolveMethod(underlyingObject, methodName, methodNameForError);
if (method === undefined) { if (method === undefined) {
return () => Promise_resolve(); return () => Promise_resolve();
} }
if (algoArgCount === 0) { if (algoArgCount === 0) {
return Function_bind(PromiseCall1, undefined, method, underlyingObject, return Function_bind(
controller); PromiseCall1, undefined, method, underlyingObject, controller);
} }
return arg => PromiseCall2(method, underlyingObject, arg, controller); return arg => PromiseCall2(method, underlyingObject, arg, controller);
...@@ -280,10 +281,8 @@ ...@@ -280,10 +281,8 @@
} }
function PromiseCall0(F, V) { function PromiseCall0(F, V) {
// assert(typeof F === 'function', // assert(typeof F === 'function', 'IsCallable(F) is true.');
// 'IsCallable(F) is true.'); // assert(V !== undefined, 'V is not undefined.');
// assert(V !== undefined,
// 'V is not undefined.');
try { try {
return Promise_resolve(callFunction(F, V)); return Promise_resolve(callFunction(F, V));
} catch (e) { } catch (e) {
...@@ -292,10 +291,8 @@ ...@@ -292,10 +291,8 @@
} }
function PromiseCall1(F, V, arg0) { function PromiseCall1(F, V, arg0) {
// assert(typeof F === 'function', // assert(typeof F === 'function', 'IsCallable(F) is true.');
// 'IsCallable(F) is true.'); // assert(V !== undefined, 'V is not undefined.');
// assert(V !== undefined,
// 'V is not undefined.');
try { try {
return Promise_resolve(callFunction(F, V, arg0)); return Promise_resolve(callFunction(F, V, arg0));
} catch (e) { } catch (e) {
...@@ -304,10 +301,8 @@ ...@@ -304,10 +301,8 @@
} }
function PromiseCall2(F, V, arg0, arg1) { function PromiseCall2(F, V, arg0, arg1) {
// assert(typeof F === 'function', // assert(typeof F === 'function', 'IsCallable(F) is true.');
// 'IsCallable(F) is true.'); // assert(V !== undefined, 'V is not undefined.');
// assert(V !== undefined,
// 'V is not undefined.');
try { try {
return Promise_resolve(callFunction(F, V, arg0, arg1)); return Promise_resolve(callFunction(F, V, arg0, arg1));
} catch (e) { } catch (e) {
...@@ -334,5 +329,6 @@ ...@@ -334,5 +329,6 @@
MakeSizeAlgorithmFromSizeFunction, MakeSizeAlgorithmFromSizeFunction,
CallOrNoop1, CallOrNoop1,
PromiseCallOrNoop1, PromiseCallOrNoop1,
PromiseCall2
}; };
}); });
...@@ -18,23 +18,20 @@ ...@@ -18,23 +18,20 @@
const _backpressureChangePromise = const _backpressureChangePromise =
v8.createPrivateSymbol('[[backpressureChangePromise]]'); v8.createPrivateSymbol('[[backpressureChangePromise]]');
const _readable = v8.createPrivateSymbol('[[readable]]'); const _readable = v8.createPrivateSymbol('[[readable]]');
const _transformer = v8.createPrivateSymbol('[[transformer]]');
const _transformStreamController = const _transformStreamController =
v8.createPrivateSymbol('[[transformStreamController]]'); v8.createPrivateSymbol('[[transformStreamController]]');
const _writable = v8.createPrivateSymbol('[[writable]]'); const _writable = v8.createPrivateSymbol('[[writable]]');
const _controlledTransformStream = const _controlledTransformStream =
v8.createPrivateSymbol('[[controlledTransformStream]]'); v8.createPrivateSymbol('[[controlledTransformStream]]');
const _ownerTransformStream = const _flushAlgorithm = v8.createPrivateSymbol('[[flushAlgorithm]]');
v8.createPrivateSymbol('[[ownerTransformStream]]'); const _transformAlgorithm = v8.createPrivateSymbol('[[transformAlgorithm]]');
const _startPromise = v8.createPrivateSymbol('[[startPromise]]');
// Javascript functions. It is important to use these copies, as the ones on // Javascript functions. It is important to use these copies, as the ones on
// the global object may have been overwritten. See "V8 Extras Design Doc", // the global object may have been overwritten. See "V8 Extras Design Doc",
// section "Security Considerations". // section "Security Considerations".
// https://docs.google.com/document/d/1AT5-T0aHGp7Lt29vPWFr2-qG8r3l9CByyvKwEuA8Ec0/edit#heading=h.9yixony1a18r // https://docs.google.com/document/d/1AT5-T0aHGp7Lt29vPWFr2-qG8r3l9CByyvKwEuA8Ec0/edit#heading=h.9yixony1a18r
const defineProperty = global.Object.defineProperty; const defineProperty = global.Object.defineProperty;
const ObjectCreate = global.Object.create;
const Function_call = v8.uncurryThis(global.Function.prototype.call);
const TypeError = global.TypeError; const TypeError = global.TypeError;
const RangeError = global.RangeError; const RangeError = global.RangeError;
...@@ -48,22 +45,20 @@ ...@@ -48,22 +45,20 @@
const { const {
hasOwnPropertyNoThrow, hasOwnPropertyNoThrow,
resolvePromise, resolvePromise,
CreateAlgorithmFromUnderlyingMethodPassingController,
CallOrNoop1, CallOrNoop1,
PromiseCallOrNoop1 MakeSizeAlgorithmFromSizeFunction,
PromiseCall2,
ValidateAndNormalizeHighWaterMark
} = binding.streamOperations; } = binding.streamOperations;
// User-visible strings. // User-visible strings.
const streamErrors = binding.streamErrors; const streamErrors = binding.streamErrors;
const errWritableStreamAborted = 'The writable stream has been aborted';
const errStreamTerminated = 'The transform stream has been terminated'; const errStreamTerminated = 'The transform stream has been terminated';
const templateErrorIsNotAFunction = f => `${f} is not a function`;
class TransformStream { class TransformStream {
constructor( constructor(transformer = {},
transformer = {}, writableStrategy = undefined, writableStrategy = {}, readableStrategy = {}) {
{size, highWaterMark = 0} = {}) {
this[_transformer] = transformer;
// readable and writableType are extension points for future byte streams. // readable and writableType are extension points for future byte streams.
const readableType = transformer.readableType; const readableType = transformer.readableType;
if (readableType !== undefined) { if (readableType !== undefined) {
...@@ -75,24 +70,33 @@ ...@@ -75,24 +70,33 @@
throw new RangeError(streamErrors.invalidType); throw new RangeError(streamErrors.invalidType);
} }
this[_transformStreamController] = undefined; const writableSizeFunction = writableStrategy.size;
const controller = new TransformStreamDefaultController(this); const writableSizeAlgorithm =
this[_transformStreamController] = controller; MakeSizeAlgorithmFromSizeFunction(writableSizeFunction);
let writableHighWaterMark = writableStrategy.highWaterMark;
const startPromise = v8.createPromise(); if (writableHighWaterMark === undefined) {
const source = new TransformStreamDefaultSource(this, startPromise); writableHighWaterMark = 1;
const readableStrategy = {size, highWaterMark}; }
this[_readable] = new binding.ReadableStream(source, readableStrategy); writableHighWaterMark =
ValidateAndNormalizeHighWaterMark(writableHighWaterMark);
const sink = new TransformStreamDefaultSink(this, startPromise);
this[_writable] = new binding.WritableStream(sink, writableStrategy);
// this[_backpressure] and this[_backpressureChangePromise]] are already const readableSizeFunction = readableStrategy.size;
// undefined, so save a tiny amount of code by not setting them const readableSizeAlgorithm =
// explicitly. MakeSizeAlgorithmFromSizeFunction(readableSizeFunction);
TransformStreamSetBackpressure(this, true); let readableHighWaterMark = readableStrategy.highWaterMark;
if (readableHighWaterMark === undefined) {
readableHighWaterMark = 0;
}
readableHighWaterMark =
ValidateAndNormalizeHighWaterMark(readableHighWaterMark);
const startResult = CallOrNoop1(transformer, 'start', controller, const startPromise = v8.createPromise();
InitializeTransformStream(
this, startPromise, writableHighWaterMark, writableSizeAlgorithm,
readableHighWaterMark, readableSizeAlgorithm);
SetUpTransformStreamDefaultControllerFromTransformer(this, transformer);
const startResult = CallOrNoop1(
transformer, 'start', this[_transformStreamController],
'transformer.start'); 'transformer.start');
resolvePromise(startPromise, startResult); resolvePromise(startPromise, startResult);
} }
...@@ -114,12 +118,82 @@ ...@@ -114,12 +118,82 @@
} }
} }
const TransformStream_prototype = TransformStream.prototype;
function CreateTransformStream(
startAlgorithm, transformAlgorithm, flushAlgorithm, writableHighWaterMark,
writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm) {
if (writableHighWaterMark === undefined) {
writableHighWaterMark = 1;
}
if (writableSizeAlgorithm === undefined) {
writableSizeAlgorithm = () => 1;
}
if (readableHighWaterMark === undefined) {
readableHighWaterMark = 0;
}
if (readableSizeAlgorithm === undefined) {
readableSizeAlgorithm = () => 1;
}
// assert(
// typeof writableHighWaterMark === 'number' &&
// writableHighWaterMark >= 0,
// '! IsNonNegativeNumber(_writableHighWaterMark_) is *true*');
// assert(
// typeof readableHighWaterMark === 'number' &&
// readableHighWaterMark >= 0,
// '! IsNonNegativeNumber(_readableHighWaterMark_) is true');
const stream = ObjectCreate(TransformStream_prototype);
const startPromise = v8.createPromise();
InitializeTransformStream(
stream, startPromise, writableHighWaterMark, writableSizeAlgorithm,
readableHighWaterMark, readableSizeAlgorithm);
const controller = ObjectCreate(TransformStreamDefaultController_prototype);
SetUpTransformStreamDefaultController(
stream, controller, transformAlgorithm, flushAlgorithm);
const startResult = startAlgorithm();
resolvePromise(startPromise, startResult);
return stream;
}
function InitializeTransformStream(
stream, startPromise, writableHighWaterMark, writableSizeAlgorithm,
readableHighWaterMark, readableSizeAlgorithm) {
const startAlgorithm = () => startPromise;
const writeAlgorithm = chunk =>
TransformStreamDefaultSinkWriteAlgorithm(stream, chunk);
const abortAlgorithm = reason =>
TransformStreamDefaultSinkAbortAlgorithm(stream, reason);
const closeAlgorithm = () =>
TransformStreamDefaultSinkCloseAlgorithm(stream);
stream[_writable] = binding.CreateWritableStream(
startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm,
writableHighWaterMark, writableSizeAlgorithm);
// TODO(ricea): Use CreateReadableStream() once it is implemented.
stream[_readable] = new binding.ReadableStream({
start: startAlgorithm,
pull() {
return TransformStreamDefaultSourcePullAlgorithm(stream);
},
cancel(reason) {
TransformStreamErrorWritableAndUnblockWrite(stream, reason);
return Promise_resolve();
},
type: undefined,
}, {highWaterMark: readableHighWaterMark, size: readableSizeAlgorithm});
stream[_backpressure] = undefined;
stream[_backpressureChangePromise] = undefined;
TransformStreamSetBackpressure(stream, true);
stream[_transformStreamController] = undefined;
}
function IsTransformStream(x) { function IsTransformStream(x) {
return hasOwnPropertyNoThrow(x, _transformStreamController); return hasOwnPropertyNoThrow(x, _transformStreamController);
} }
function TransformStreamError(stream, e) { function TransformStreamError(stream, e) {
const readable = stream[_readable]; const readable = stream[_readable];
// TODO(ricea): Remove this conditional once ReadableStream is updated.
if (binding.IsReadableStreamReadable(readable)) { if (binding.IsReadableStreamReadable(readable)) {
binding.ReadableStreamDefaultControllerError( binding.ReadableStreamDefaultControllerError(
binding.getReadableStreamController(readable), e); binding.getReadableStreamController(readable), e);
...@@ -151,18 +225,10 @@ ...@@ -151,18 +225,10 @@
} }
class TransformStreamDefaultController { class TransformStreamDefaultController {
constructor(stream) { constructor() {
if (!IsTransformStream(stream)) {
throw new TypeError(streamErrors.illegalConstructor);
}
if (stream[_transformStreamController] !== undefined) {
throw new TypeError(streamErrors.illegalConstructor); throw new TypeError(streamErrors.illegalConstructor);
} }
this[_controlledTransformStream] = stream;
}
get desiredSize() { get desiredSize() {
if (!IsTransformStreamDefaultController(this)) { if (!IsTransformStreamDefaultController(this)) {
throw new TypeError(streamErrors.illegalInvocation); throw new TypeError(streamErrors.illegalInvocation);
...@@ -199,10 +265,61 @@ ...@@ -199,10 +265,61 @@
} }
} }
const TransformStreamDefaultController_prototype =
TransformStreamDefaultController.prototype;
function IsTransformStreamDefaultController(x) { function IsTransformStreamDefaultController(x) {
return hasOwnPropertyNoThrow(x, _controlledTransformStream); return hasOwnPropertyNoThrow(x, _controlledTransformStream);
} }
function SetUpTransformStreamDefaultController(
stream, controller, transformAlgorithm, flushAlgorithm) {
// assert(
// IsTransformStream(stream) === true,
// '! IsTransformStream(_stream_) is *true*');
// assert(
// stream[_transformStreamController] === undefined,
// '_stream_.[[transformStreamController]] is *undefined*');
controller[_controlledTransformStream] = stream;
stream[_transformStreamController] = controller;
controller[_transformAlgorithm] = transformAlgorithm;
controller[_flushAlgorithm] = flushAlgorithm;
}
function SetUpTransformStreamDefaultControllerFromTransformer(
stream, transformer) {
// assert(transformer !== undefined, '_transformer_ is not *undefined*');
const controller = ObjectCreate(TransformStreamDefaultController_prototype);
let transformAlgorithm;
const transformMethod = transformer.transform;
if (transformMethod !== undefined) {
if (typeof transformMethod !== 'function') {
throw new TypeError('transformer.transform is not a function');
}
transformAlgorithm = chunk => {
const transformPromise =
PromiseCall2(transformMethod, transformer, chunk, controller);
return thenPromise(transformPromise, undefined, e => {
TransformStreamError(stream, e);
throw e;
});
};
} else {
transformAlgorithm = chunk => {
try {
TransformStreamDefaultControllerEnqueue(controller, chunk);
return Promise_resolve();
} catch (resultValue) {
return Promise_reject(resultValue);
}
};
}
const flushAlgorithm = CreateAlgorithmFromUnderlyingMethodPassingController(
transformer, 'flush', 0, controller, 'transformer.flush');
SetUpTransformStreamDefaultController(
stream, controller, transformAlgorithm, flushAlgorithm);
}
function TransformStreamDefaultControllerEnqueue(controller, chunk) { function TransformStreamDefaultControllerEnqueue(controller, chunk) {
const stream = controller[_controlledTransformStream]; const stream = controller[_controlledTransformStream];
const readableController = const readableController =
...@@ -246,25 +363,13 @@ ...@@ -246,25 +363,13 @@
TransformStreamErrorWritableAndUnblockWrite(stream, error); TransformStreamErrorWritableAndUnblockWrite(stream, error);
} }
class TransformStreamDefaultSink { function TransformStreamDefaultSinkWriteAlgorithm(stream, chunk) {
constructor(stream, startPromise) {
this[_ownerTransformStream] = stream;
this[_startPromise] = startPromise;
}
start() {
const startPromise = this[_startPromise];
// Permit GC of the promise.
this[_startPromise] = undefined;
return startPromise;
}
write(chunk) {
const stream = this[_ownerTransformStream];
// assert( // assert(
// binding.isWritableStreamWritable(stream[_writable]), // binding.isWritableStreamWritable(stream[_writable]),
// `stream.[[writable]][[state]] is "writable"`); // `stream.[[writable]][[state]] is "writable"`);
const controller = stream[_transformStreamController];
if (stream[_backpressure]) { if (stream[_backpressure]) {
const backpressureChangePromise = stream[_backpressureChangePromise]; const backpressureChangePromise = stream[_backpressureChangePromise];
// assert( // assert(
...@@ -276,29 +381,25 @@ ...@@ -276,29 +381,25 @@
if (binding.isWritableStreamErroring(writable)) { if (binding.isWritableStreamErroring(writable)) {
throw binding.getWritableStreamStoredError(writable); throw binding.getWritableStreamStoredError(writable);
} }
// assert( // assert(binding.isWritableStreamWritable(writable),
// binding.isWritableStreamWritable(writable),
// `state is "writable"`); // `state is "writable"`);
return TransformStreamDefaultSinkTransform(this, chunk); return controller[_transformAlgorithm](chunk);
}); });
} }
return TransformStreamDefaultSinkTransform(this, chunk); return controller[_transformAlgorithm](chunk);
} }
abort() { function TransformStreamDefaultSinkAbortAlgorithm(stream, reason) {
const e = new TypeError(errWritableStreamAborted); TransformStreamError(stream, reason);
TransformStreamError(this[_ownerTransformStream], e); return Promise_resolve();
} }
close() { function TransformStreamDefaultSinkCloseAlgorithm(stream) {
const stream = this[_ownerTransformStream];
const readable = stream[_readable]; const readable = stream[_readable];
const flushPromise = PromiseCallOrNoop1( const flushPromise = stream[_transformStreamController][_flushAlgorithm]();
stream[_transformer], 'flush', stream[_transformStreamController],
'transformer.flush');
return thenPromise( return thenPromise(
flushPromise, flushPromise,
...@@ -319,61 +420,8 @@ ...@@ -319,61 +420,8 @@
throw binding.getReadableStreamStoredError(readable); throw binding.getReadableStreamStoredError(readable);
}); });
} }
}
function TransformStreamDefaultSinkInvokeTransform(stream, chunk) { function TransformStreamDefaultSourcePullAlgorithm(stream) {
const controller = stream[_transformStreamController];
const transformer = stream[_transformer];
const method = transformer.transform;
if (method === undefined) {
TransformStreamDefaultControllerEnqueue(controller, chunk);
return undefined;
}
if (typeof method !== 'function') {
throw new TypeError(templateErrorIsNotAFunction('transform'));
}
return Function_call(method, transformer, chunk, controller);
}
function TransformStreamDefaultSinkTransform(sink, chunk) {
const stream = sink[_ownerTransformStream];
// assert(
// !binding.IsReadableStreamErrored(stream[_readable]),
// 'stream.[[readable]].[[state]] is not "errored"');
// assert(!stream[_backpressure], 'stream.[[backpressure]] is false');
let transformPromise;
try {
transformPromise = Promise_resolve(
TransformStreamDefaultSinkInvokeTransform(stream, chunk));
} catch (e) {
transformPromise = Promise_reject(e);
}
return thenPromise(transformPromise, undefined, e => {
TransformStreamError(stream, e);
throw e;
});
}
class TransformStreamDefaultSource {
constructor(stream, startPromise) {
this[_ownerTransformStream] = stream;
this[_startPromise] = startPromise;
}
start() {
const startPromise = this[_startPromise];
// Permit GC of the promise.
this[_startPromise] = undefined;
return startPromise;
}
pull() {
const stream = this[_ownerTransformStream];
// assert(stream[_backpressure], 'stream.[[backpressure]] is true'); // assert(stream[_backpressure], 'stream.[[backpressure]] is true');
// assert( // assert(
// stream[_backpressureChangePromise] !== undefined, // stream[_backpressureChangePromise] !== undefined,
...@@ -383,12 +431,6 @@ ...@@ -383,12 +431,6 @@
return stream[_backpressureChangePromise]; return stream[_backpressureChangePromise];
} }
cancel(reason) {
TransformStreamErrorWritableAndUnblockWrite(
this[_ownerTransformStream], reason);
}
}
// //
// Additions to the global object // Additions to the global object
// //
...@@ -399,4 +441,9 @@ ...@@ -399,4 +441,9 @@
configurable: true, configurable: true,
writable: true writable: true
}); });
//
// Exports to Blink
//
binding.CreateTransformStream = CreateTransformStream;
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment