Skip to content

Commit

Permalink
Merge pull request #33 from MattiasBuelens/update-20190823
Browse files Browse the repository at this point in the history
Update to spec version of 23 August 2019
  • Loading branch information
MattiasBuelens authored Aug 24, 2019
2 parents e9e7d97 + 47f8aaa commit 98aafe5
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 146 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
## Unreleased

* 👓 Align with [spec version `ae5e0cb`](https://github.com/whatwg/streams/tree/ae5e0cb41e9f72cdd97f3a6d47bc674c1f4049d1/) ([#33](https://github.com/MattiasBuelens/web-streams-polyfill/pull/33))

## v2.0.4 (2019-08-01)

* 🐛 Fix pipe not aborting when both `preventAbort` and `preventCancel` are set ([#31](https://github.com/MattiasBuelens/web-streams-polyfill/pull/31))
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ The `polyfill/es2018` and `ponyfill/es2018` variants work in any ES2018-compatib

### Compliance

The polyfill implements [version `e4d3b1a` (29 Jul 2019)][spec-snapshot] of the streams specification.
The polyfill implements [version `ae5e0cb` (23 Aug 2019)][spec-snapshot] of the streams specification.

The polyfill is tested against the same [web platform tests][wpt] that are used by browsers to test their native implementations.
The polyfill aims to pass all tests, although it allows some exceptions for practical reasons:
Expand Down Expand Up @@ -99,12 +99,12 @@ Thanks to these people for their work on [the original polyfill][creatorrr-polyf
[promise-support]: https://kangax.github.io/compat-table/es6/#test-Promise
[promise-polyfill]: https://www.npmjs.com/package/promise-polyfill
[rs-asynciterator]: https://streams.spec.whatwg.org/#rs-asynciterator
[spec-snapshot]: https://streams.spec.whatwg.org/commit-snapshots/e4d3b1a826e34d27a7cb5485a1cc4b078608c9ec/
[wpt]: https://github.com/web-platform-tests/wpt/tree/0da3476dcd5fd3148041d090d2330cf8d412d7f9/streams
[wpt-detached-buffer]: https://github.com/web-platform-tests/wpt/blob/0da3476dcd5fd3148041d090d2330cf8d412d7f9/streams/readable-byte-streams/detached-buffers.any.js
[spec-snapshot]: https://streams.spec.whatwg.org/commit-snapshots/ae5e0cb41e9f72cdd97f3a6d47bc674c1f4049d1/
[wpt]: https://github.com/web-platform-tests/wpt/tree/7046bf42a72ef21e5e39267d89dc3e30be6d72e3/streams
[wpt-detached-buffer]: https://github.com/web-platform-tests/wpt/blob/7046bf42a72ef21e5e39267d89dc3e30be6d72e3/streams/readable-byte-streams/detached-buffers.any.js
[proposal-arraybuffer-transfer]: https://github.com/domenic/proposal-arraybuffer-transfer
[ref-impl-transferarraybuffer]: https://github.com/whatwg/streams/blob/e4d3b1a826e34d27a7cb5485a1cc4b078608c9ec/reference-implementation/lib/helpers.js#L119
[ref-impl-transferarraybuffer]: https://github.com/whatwg/streams/blob/ae5e0cb41e9f72cdd97f3a6d47bc674c1f4049d1/reference-implementation/lib/helpers.js#L120
[issue-3]: https://github.com/MattiasBuelens/web-streams-polyfill/issues/3
[wpt-async-iterator-prototype]: https://github.com/web-platform-tests/wpt/blob/0da3476dcd5fd3148041d090d2330cf8d412d7f9/streams/readable-streams/async-iterator.any.js#L17
[wpt-async-iterator-prototype]: https://github.com/web-platform-tests/wpt/blob/7046bf42a72ef21e5e39267d89dc3e30be6d72e3/streams/readable-streams/async-iterator.any.js#L17
[stub-async-iterator-prototype]: https://github.com/MattiasBuelens/web-streams-polyfill/blob/v2.0.0/src/target/es5/stub/async-iterator-prototype.ts
[creatorrr-polyfill]: https://github.com/creatorrr/web-streams-polyfill
66 changes: 63 additions & 3 deletions src/lib/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import assert from '../stub/assert';
import NumberIsNaN from '../stub/number-isnan';
import { rethrowAssertionErrorRejection } from './utils';
import { FunctionPropertyNames, InferFirst, InferFunction, InferRest, Promisify } from '../util/type-utils';

function IsPropertyKey(argument: any): argument is string | symbol {
Expand Down Expand Up @@ -101,7 +102,7 @@ export function CreateAlgorithmFromUnderlyingMethod(underlyingObject: any,
}
}
}
return () => Promise.resolve();
return () => promiseResolvedWith(undefined);
}

export function InvokeOrNoop<T, Key extends FunctionPropertyNames<Required<T>> = FunctionPropertyNames<Required<T>>>(
Expand All @@ -127,9 +128,9 @@ export function PromiseCall<T, A extends any[], R>(F: (this: T, ...args: A) => R
assert(V !== undefined);
assert(Array.isArray(args));
try {
return Promise.resolve(Call(F, V, args));
return promiseResolvedWith(Call(F, V, args));
} catch (value) {
return Promise.reject(value);
return promiseRejectedWith(value);
}
}

Expand Down Expand Up @@ -161,3 +162,62 @@ export function MakeSizeAlgorithmFromSizeFunction<T>(size?: (chunk: T) => number
}
return chunk => size(chunk);
}

const originalPromise = Promise;
const originalPromiseThen = Promise.prototype.then;
const originalPromiseResolve = Promise.resolve.bind(originalPromise);
const originalPromiseReject = Promise.reject.bind(originalPromise);

export function newPromise<T>(executor: (
resolve: (value?: T | PromiseLike<T>) => void,
reject: (reason?: any) => void
) => void): Promise<T> {
return new originalPromise(executor);
}

export function promiseResolvedWith<T>(value: T | PromiseLike<T>): Promise<T> {
return originalPromiseResolve(value);
}

export function promiseRejectedWith<T = never>(reason: any): Promise<T> {
return originalPromiseReject(reason);
}

export function PerformPromiseThen<T, TResult1 = T, TResult2 = never>(
promise: Promise<T>,
onFulfilled?: (value: T) => TResult1 | PromiseLike<TResult1>,
onRejected?: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2> {
// There doesn't appear to be any way to correctly emulate the behaviour from JavaScript, so this is just an
// approximation.
return originalPromiseThen.call(promise, onFulfilled, onRejected) as Promise<TResult1 | TResult2>;
}

export function uponPromise<T>(
promise: Promise<T>,
onFulfilled?: (value: T) => void | PromiseLike<void>,
onRejected?: (reason: any) => void | PromiseLike<void>): void {
PerformPromiseThen(
PerformPromiseThen(promise, onFulfilled, onRejected),
undefined,
rethrowAssertionErrorRejection
);
}

export function uponFulfillment<T>(promise: Promise<T>, onFulfilled: (value: T) => void | PromiseLike<void>): void {
uponPromise(promise, onFulfilled);
}

export function uponRejection(promise: Promise<unknown>, onRejected: (reason: any) => void | PromiseLike<void>): void {
uponPromise(promise, undefined, onRejected);
}

export function transformPromiseWith<T, TResult1 = T, TResult2 = never>(
promise: Promise<T>,
fulfillmentHandler?: (value: T) => TResult1 | PromiseLike<TResult1>,
rejectionHandler?: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2> {
return PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler);
}

export function setPromiseIsHandledToTrue(promise: Promise<unknown>): void {
PerformPromiseThen(promise, undefined, rethrowAssertionErrorRejection);
}
31 changes: 19 additions & 12 deletions src/lib/readable-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
createArrayFromList,
IsNonNegativeNumber,
MakeSizeAlgorithmFromSizeFunction,
promiseRejectedWith,
promiseResolvedWith,
setPromiseIsHandledToTrue,
transformPromiseWith,
typeIsObject,
ValidateAndNormalizeHighWaterMark
} from './helpers';
Expand All @@ -27,7 +31,6 @@ import { ReadableStreamTee } from './readable-stream/tee';
import { IsWritableStream, IsWritableStreamLocked, WritableStream } from './writable-stream';
import NumberIsInteger from '../stub/number-isinteger';
import { SimpleQueue } from './simple-queue';
import { noop } from '../utils';
import {
AcquireReadableStreamBYOBReader,
IsReadableStreamBYOBReader,
Expand Down Expand Up @@ -55,6 +58,7 @@ import {
UnderlyingByteSource,
UnderlyingSource
} from './readable-stream/underlying-source';
import { noop } from '../utils';

export type ReadableByteStream = ReadableStream<Uint8Array>;

Expand Down Expand Up @@ -129,11 +133,11 @@ export class ReadableStream<R = any> {

cancel(reason: any): Promise<void> {
if (IsReadableStream(this) === false) {
return Promise.reject(streamBrandCheckException('cancel'));
return promiseRejectedWith(streamBrandCheckException('cancel'));
}

if (IsReadableStreamLocked(this) === true) {
return Promise.reject(new TypeError('Cannot cancel a stream that already has a reader'));
return promiseRejectedWith(new TypeError('Cannot cancel a stream that already has a reader'));
}

return ReadableStreamCancel(this, reason);
Expand Down Expand Up @@ -190,18 +194,18 @@ export class ReadableStream<R = any> {

const promise = ReadableStreamPipeTo(this, writable, preventClose, preventAbort, preventCancel, signal);

promise.catch(noop);
setPromiseIsHandledToTrue(promise);

return readable;
}

pipeTo(dest: WritableStream<R>,
{ preventClose, preventAbort, preventCancel, signal }: PipeOptions = {}): Promise<void> {
if (IsReadableStream(this) === false) {
return Promise.reject(streamBrandCheckException('pipeTo'));
return promiseRejectedWith(streamBrandCheckException('pipeTo'));
}
if (IsWritableStream(dest) === false) {
return Promise.reject(
return promiseRejectedWith(
new TypeError('ReadableStream.prototype.pipeTo\'s first argument must be a WritableStream'));
}

Expand All @@ -210,14 +214,17 @@ export class ReadableStream<R = any> {
preventCancel = Boolean(preventCancel);

if (signal !== undefined && !isAbortSignal(signal)) {
return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo\'s signal option must be an AbortSignal'));
return promiseRejectedWith(
new TypeError('ReadableStream.prototype.pipeTo\'s signal option must be an AbortSignal'));
}

if (IsReadableStreamLocked(this) === true) {
return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked ReadableStream'));
return promiseRejectedWith(
new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked ReadableStream'));
}
if (IsWritableStreamLocked(dest) === true) {
return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked WritableStream'));
return promiseRejectedWith(
new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked WritableStream'));
}

return ReadableStreamPipeTo(this, dest, preventClose, preventAbort, preventCancel, signal);
Expand Down Expand Up @@ -348,16 +355,16 @@ export function ReadableStreamCancel<R>(stream: ReadableStream<R>, reason: any):
stream._disturbed = true;

if (stream._state === 'closed') {
return Promise.resolve(undefined);
return promiseResolvedWith(undefined);
}
if (stream._state === 'errored') {
return Promise.reject(stream._storedError);
return promiseRejectedWith(stream._storedError);
}

ReadableStreamClose(stream);

const sourceCancelPromise = stream._readableStreamController[CancelSteps](reason);
return sourceCancelPromise.then(() => undefined);
return transformPromiseWith(sourceCancelPromise, noop);
}

export function ReadableStreamClose<R>(stream: ReadableStream<R>): void {
Expand Down
18 changes: 9 additions & 9 deletions src/lib/readable-stream/async-iterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
readerLockException
} from './generic-reader';
import assert from '../../stub/assert';
import { typeIsObject } from '../helpers';
import { promiseRejectedWith, promiseResolvedWith, transformPromiseWith, typeIsObject } from '../helpers';
import { AsyncIteratorPrototype } from '@@target/stub/async-iterator-prototype';

export interface ReadableStreamAsyncIterator<R> extends AsyncIterator<R> {
Expand All @@ -36,13 +36,13 @@ declare class ReadableStreamAsyncIteratorImpl<R> implements ReadableStreamAsyncI
const ReadableStreamAsyncIteratorPrototype: ReadableStreamAsyncIteratorImpl<any> = {
next(): Promise<IteratorResult<any>> {
if (IsReadableStreamAsyncIterator(this) === false) {
return Promise.reject(streamAsyncIteratorBrandCheckException('next'));
return promiseRejectedWith(streamAsyncIteratorBrandCheckException('next'));
}
const reader = this._asyncIteratorReader;
if (reader._ownerReadableStream === undefined) {
return Promise.reject(readerLockException('iterate'));
return promiseRejectedWith(readerLockException('iterate'));
}
return ReadableStreamDefaultReaderRead(reader).then(result => {
return transformPromiseWith(ReadableStreamDefaultReaderRead(reader), result => {
assert(typeIsObject(result));
const done = result.done;
assert(typeof done === 'boolean');
Expand All @@ -56,23 +56,23 @@ const ReadableStreamAsyncIteratorPrototype: ReadableStreamAsyncIteratorImpl<any>

return(value: any): Promise<IteratorResult<any>> {
if (IsReadableStreamAsyncIterator(this) === false) {
return Promise.reject(streamAsyncIteratorBrandCheckException('next'));
return promiseRejectedWith(streamAsyncIteratorBrandCheckException('next'));
}
const reader = this._asyncIteratorReader;
if (reader._ownerReadableStream === undefined) {
return Promise.reject(readerLockException('finish iterating'));
return promiseRejectedWith(readerLockException('finish iterating'));
}
if (reader._readRequests.length > 0) {
return Promise.reject(new TypeError(
return promiseRejectedWith(new TypeError(
'Tried to release a reader lock when that reader has pending read() calls un-settled'));
}
if (this._preventCancel === false) {
const result = ReadableStreamReaderGenericCancel(reader, value);
ReadableStreamReaderGenericRelease(reader);
return result.then(() => ReadableStreamCreateReadResult(value, true, true));
return transformPromiseWith(result, () => ReadableStreamCreateReadResult(value, true, true));
}
ReadableStreamReaderGenericRelease(reader);
return Promise.resolve(ReadableStreamCreateReadResult(value, true, true));
return promiseResolvedWith(ReadableStreamCreateReadResult(value, true, true));
}
} as any;
if (AsyncIteratorPrototype !== undefined) {
Expand Down
22 changes: 11 additions & 11 deletions src/lib/readable-stream/byob-reader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IsDetachedBuffer, typeIsObject } from '../helpers';
import { IsDetachedBuffer, newPromise, promiseRejectedWith, typeIsObject } from '../helpers';
import assert from '../../stub/assert';
import { SimpleQueue } from '../simple-queue';
import {
Expand Down Expand Up @@ -31,7 +31,7 @@ export function ReadableStreamAddReadIntoRequest<T extends ArrayBufferView>(stre
assert(IsReadableStreamBYOBReader(stream._reader) === true);
assert(stream._state === 'readable' || stream._state === 'closed');

const promise = new Promise<ReadResult<T>>((resolve, reject) => {
const promise = newPromise<ReadResult<T>>((resolve, reject) => {
const readIntoRequest: ReadIntoRequest<T> = {
_resolve: resolve,
_reject: reject
Expand Down Expand Up @@ -115,43 +115,43 @@ export class ReadableStreamBYOBReader {

get closed(): Promise<void> {
if (!IsReadableStreamBYOBReader(this)) {
return Promise.reject(byobReaderBrandCheckException('closed'));
return promiseRejectedWith(byobReaderBrandCheckException('closed'));
}

return this._closedPromise;
}

cancel(reason: any): Promise<void> {
if (!IsReadableStreamBYOBReader(this)) {
return Promise.reject(byobReaderBrandCheckException('cancel'));
return promiseRejectedWith(byobReaderBrandCheckException('cancel'));
}

if (this._ownerReadableStream === undefined) {
return Promise.reject(readerLockException('cancel'));
return promiseRejectedWith(readerLockException('cancel'));
}

return ReadableStreamReaderGenericCancel(this, reason);
}

read<T extends ArrayBufferView>(view: T): Promise<ReadResult<T>> {
if (!IsReadableStreamBYOBReader(this)) {
return Promise.reject(byobReaderBrandCheckException('read'));
return promiseRejectedWith(byobReaderBrandCheckException('read'));
}

if (this._ownerReadableStream === undefined) {
return Promise.reject(readerLockException('read from'));
return promiseRejectedWith(readerLockException('read from'));
}

if (!ArrayBuffer.isView(view)) {
return Promise.reject(new TypeError('view must be an array buffer view'));
return promiseRejectedWith(new TypeError('view must be an array buffer view'));
}

if (IsDetachedBuffer(view.buffer) === true) {
return Promise.reject(new TypeError('Cannot read into a view onto a detached ArrayBuffer'));
return promiseRejectedWith(new TypeError('Cannot read into a view onto a detached ArrayBuffer'));
}

if (view.byteLength === 0) {
return Promise.reject(new TypeError('view must have non-zero byteLength'));
return promiseRejectedWith(new TypeError('view must have non-zero byteLength'));
}

return ReadableStreamBYOBReaderRead(this, view);
Expand Down Expand Up @@ -197,7 +197,7 @@ function ReadableStreamBYOBReaderRead<T extends ArrayBufferView>(reader: Readabl
stream._disturbed = true;

if (stream._state === 'errored') {
return Promise.reject(stream._storedError);
return promiseRejectedWith(stream._storedError);
}

// Controllers must implement this.
Expand Down
Loading

0 comments on commit 98aafe5

Please sign in to comment.