summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/AsyncIteration.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/builtin/AsyncIteration.js528
1 files changed, 528 insertions, 0 deletions
diff --git a/js/src/builtin/AsyncIteration.js b/js/src/builtin/AsyncIteration.js
new file mode 100644
index 0000000000..041a0ba867
--- /dev/null
+++ b/js/src/builtin/AsyncIteration.js
@@ -0,0 +1,528 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function AsyncIteratorIdentity() {
+ return this;
+}
+
+function AsyncGeneratorNext(val) {
+ assert(IsAsyncGeneratorObject(this),
+ "ThisArgument must be a generator object for async generators");
+ return resumeGenerator(this, val, "next");
+}
+
+function AsyncGeneratorThrow(val) {
+ assert(IsAsyncGeneratorObject(this),
+ "ThisArgument must be a generator object for async generators");
+ return resumeGenerator(this, val, "throw");
+}
+
+function AsyncGeneratorReturn(val) {
+ assert(IsAsyncGeneratorObject(this),
+ "ThisArgument must be a generator object for async generators");
+ return resumeGenerator(this, val, "return");
+}
+
+/* ECMA262 7.4.7 AsyncIteratorClose */
+async function AsyncIteratorClose(iteratorRecord, value) {
+ // Step 3.
+ const iterator = iteratorRecord.iterator;
+ // Step 4.
+ const returnMethod = iterator.return;
+ // Step 5.
+ if (returnMethod !== undefined && returnMethod !== null) {
+ const result = await callContentFunction(returnMethod, iterator);
+ // Step 8.
+ if (!IsObject(result)) {
+ ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, result));
+ }
+ }
+ // Step 5b & 9.
+ return value;
+}
+
+/* Iterator Helpers proposal 1.1.1 */
+function GetAsyncIteratorDirectWrapper(obj) {
+ // Step 1.
+ if (!IsObject(obj)) {
+ ThrowTypeError(JSMSG_OBJECT_REQUIRED, obj);
+ }
+
+ // Step 2.
+ const nextMethod = obj.next;
+ // Step 3.
+ if (!IsCallable(nextMethod)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, nextMethod);
+ }
+
+ // Steps 4-5.
+ return {
+ // Use a named function expression instead of a method definition, so
+ // we don't create an inferred name for this function at runtime.
+ [std_asyncIterator]: function AsyncIteratorMethod() {
+ return this;
+ },
+ next(value) {
+ return callContentFunction(nextMethod, obj, value);
+ },
+ async return(value) {
+ const returnMethod = obj.return;
+ if (returnMethod !== undefined && returnMethod !== null) {
+ return callContentFunction(returnMethod, obj, value);
+ }
+ return {done: true, value};
+ },
+ };
+}
+
+/* AsyncIteratorHelper object prototype methods. */
+function AsyncIteratorHelperNext(value) {
+ let O;
+ if (!IsObject(this) || (O = GuardToAsyncIteratorHelper(this)) === null) {
+ return callFunction(CallAsyncIteratorHelperMethodIfWrapped, this,
+ value, "AsyncIteratorHelperNext");
+ }
+ const generator = UnsafeGetReservedSlot(O, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT);
+ return callFunction(IntrinsicAsyncGeneratorNext, generator, value);
+}
+
+function AsyncIteratorHelperReturn(value) {
+ let O;
+ if (!IsObject(this) || (O = GuardToAsyncIteratorHelper(this)) === null) {
+ return callFunction(CallAsyncIteratorHelperMethodIfWrapped, this,
+ value, "AsyncIteratorHelperReturn");
+ }
+ const generator = UnsafeGetReservedSlot(O, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT);
+ return callFunction(IntrinsicAsyncGeneratorReturn, generator, value);
+}
+
+function AsyncIteratorHelperThrow(value) {
+ let O;
+ if (!IsObject(this) || (O = GuardToAsyncIteratorHelper(this)) === null) {
+ return callFunction(CallAsyncIteratorHelperMethodIfWrapped, this,
+ value, "AsyncIteratorHelperThrow");
+ }
+ const generator = UnsafeGetReservedSlot(O, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT);
+ return callFunction(IntrinsicAsyncGeneratorThrow, generator, value);
+}
+
+// AsyncIterator lazy Iterator Helper methods
+// Iterator Helpers proposal 2.1.6.2-2.1.6.7
+//
+// The AsyncIterator lazy methods are structured closely to how the Iterator
+// lazy methods are. See builtin/Iterator.js for the reasoning.
+
+/* Iterator Helpers proposal 2.1.6.2 Prelude */
+function AsyncIteratorMap(mapper) {
+ // Step 1.
+ const iterated = GetIteratorDirect(this);
+
+ // Step 2.
+ if (!IsCallable(mapper)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapper));
+ }
+
+ const iteratorHelper = NewAsyncIteratorHelper();
+ const generator = AsyncIteratorMapGenerator(iterated, mapper);
+ callFunction(IntrinsicAsyncGeneratorNext, generator);
+ UnsafeSetReservedSlot(iteratorHelper, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT, generator);
+ return iteratorHelper;
+}
+
+/* Iterator Helpers proposal 2.1.6.2 Body */
+async function* AsyncIteratorMapGenerator(iterated, mapper) {
+ // Step 1.
+ let lastValue;
+ // Step 2.
+ let needClose = true;
+ try {
+ yield;
+ needClose = false;
+
+ for (let next = await IteratorNext(iterated, lastValue);
+ !next.done;
+ next = await IteratorNext(iterated, lastValue)) {
+ // Step c.
+ const value = next.value;
+
+ // Steps d-i.
+ needClose = true;
+ lastValue = yield callContentFunction(mapper, undefined, value);
+ needClose = false;
+ }
+ } finally {
+ if (needClose) {
+ AsyncIteratorClose(iterated);
+ }
+ }
+}
+
+/* Iterator Helpers proposal 2.1.6.3 Prelude */
+function AsyncIteratorFilter(filterer) {
+ // Step 1.
+ const iterated = GetIteratorDirect(this);
+
+ // Step 2.
+ if (!IsCallable(filterer)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, filterer));
+ }
+
+ const iteratorHelper = NewAsyncIteratorHelper();
+ const generator = AsyncIteratorFilterGenerator(iterated, filterer);
+ callFunction(IntrinsicAsyncGeneratorNext, generator);
+ UnsafeSetReservedSlot(iteratorHelper, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT, generator);
+ return iteratorHelper;
+}
+
+/* Iterator Helpers proposal 2.1.6.3 Body */
+async function* AsyncIteratorFilterGenerator(iterated, filterer) {
+ // Step 1.
+ let lastValue;
+ // Step 2.
+ let needClose = true;
+ try {
+ yield;
+ needClose = false;
+
+ for (let next = await IteratorNext(iterated, lastValue);
+ !next.done;
+ next = await IteratorNext(iterated, lastValue)) {
+ // Step c.
+ const value = next.value;
+
+ // Steps d-h.
+ needClose = true;
+ if (await callContentFunction(filterer, undefined, value)) {
+ lastValue = yield value;
+ }
+ needClose = false;
+ }
+ } finally {
+ if (needClose) {
+ AsyncIteratorClose(iterated);
+ }
+ }
+}
+
+/* Iterator Helpers proposal 2.1.6.4 Prelude */
+function AsyncIteratorTake(limit) {
+ // Step 1.
+ const iterated = GetIteratorDirect(this);
+
+ // Step 2.
+ const remaining = ToInteger(limit);
+ // Step 3.
+ if (remaining < 0) {
+ ThrowRangeError(JSMSG_NEGATIVE_LIMIT);
+ }
+
+ const iteratorHelper = NewAsyncIteratorHelper();
+ const generator = AsyncIteratorTakeGenerator(iterated, remaining);
+ callFunction(IntrinsicAsyncGeneratorNext, generator);
+ UnsafeSetReservedSlot(iteratorHelper, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT, generator);
+ return iteratorHelper;
+}
+
+/* Iterator Helpers proposal 2.1.6.4 Body */
+async function* AsyncIteratorTakeGenerator(iterated, remaining) {
+ // Step 1.
+ let lastValue;
+ // Step 2.
+ let needClose = true;
+ try {
+ yield;
+ needClose = false;
+
+ for (; remaining > 0; remaining--) {
+ const next = await IteratorNext(iterated, lastValue);
+ if (next.done) {
+ return undefined;
+ }
+
+ const value = next.value;
+
+ needClose = true;
+ lastValue = yield value;
+ needClose = false;
+ }
+ } finally {
+ if (needClose) {
+ AsyncIteratorClose(iterated, undefined);
+ }
+ }
+
+ return AsyncIteratorClose(iterated, undefined);
+}
+
+/* Iterator Helpers proposal 2.1.6.5 Prelude */
+function AsyncIteratorDrop(limit) {
+ // Step 1.
+ const iterated = GetIteratorDirect(this);
+
+ // Step 2.
+ const remaining = ToInteger(limit);
+ // Step 3.
+ if (remaining < 0) {
+ ThrowRangeError(JSMSG_NEGATIVE_LIMIT);
+ }
+
+ const iteratorHelper = NewAsyncIteratorHelper();
+ const generator = AsyncIteratorDropGenerator(iterated, remaining);
+ callFunction(IntrinsicAsyncGeneratorNext, generator);
+ UnsafeSetReservedSlot(iteratorHelper, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT, generator);
+ return iteratorHelper;
+}
+
+/* Iterator Helpers proposal 2.1.6.5 Body */
+async function* AsyncIteratorDropGenerator(iterated, remaining) {
+ let needClose = true;
+ try {
+ yield;
+ needClose = false;
+
+ // Step 1.
+ for (; remaining > 0; remaining--) {
+ const next = await IteratorNext(iterated);
+ if (next.done) {
+ return;
+ }
+ }
+
+ // Step 2.
+ let lastValue;
+ // Step 3.
+ for (let next = await IteratorNext(iterated, lastValue);
+ !next.done;
+ next = await IteratorNext(iterated, lastValue)) {
+ // Steps c-d.
+ const value = next.value;
+
+ needClose = true;
+ lastValue = yield value;
+ needClose = false;
+ }
+ } finally {
+ if (needClose) {
+ AsyncIteratorClose(iterated);
+ }
+ }
+}
+
+/* Iterator Helpers proposal 2.1.6.6 Prelude */
+function AsyncIteratorAsIndexedPairs() {
+ // Step 1.
+ const iterated = GetIteratorDirect(this);
+
+ const iteratorHelper = NewAsyncIteratorHelper();
+ const generator = AsyncIteratorAsIndexedPairsGenerator(iterated);
+ callFunction(IntrinsicAsyncGeneratorNext, generator);
+ UnsafeSetReservedSlot(iteratorHelper, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT, generator);
+ return iteratorHelper;
+}
+
+/* Iterator Helpers proposal 2.1.6.6 Body */
+async function* AsyncIteratorAsIndexedPairsGenerator(iterated) {
+ let needClose = true;
+ try {
+ yield;
+ needClose = false;
+
+ // Step 2.
+ let lastValue;
+ // Step 3.
+ for (let next = await IteratorNext(iterated, lastValue), index = 0;
+ !next.done;
+ next = await IteratorNext(iterated, lastValue), index++) {
+ // Steps c-g.
+ const value = next.value;
+
+ needClose = true;
+ lastValue = yield [index, value];
+ needClose = false;
+ }
+ } finally {
+ if (needClose) {
+ AsyncIteratorClose(iterated);
+ }
+ }
+}
+
+/* Iterator Helpers proposal 2.1.6.7 Prelude */
+function AsyncIteratorFlatMap(mapper) {
+ // Step 1.
+ const iterated = GetIteratorDirect(this);
+
+ // Step 2.
+ if (!IsCallable(mapper)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapper));
+ }
+
+ const iteratorHelper = NewAsyncIteratorHelper();
+ const generator = AsyncIteratorFlatMapGenerator(iterated, mapper);
+ callFunction(IntrinsicAsyncGeneratorNext, generator);
+ UnsafeSetReservedSlot(iteratorHelper, ASYNC_ITERATOR_HELPER_GENERATOR_SLOT, generator);
+ return iteratorHelper;
+}
+
+/* Iterator Helpers proposal 2.1.6.7 Body */
+async function* AsyncIteratorFlatMapGenerator(iterated, mapper) {
+ let needClose = true;
+ try {
+ yield;
+ needClose = false;
+
+ // Step 1.
+ for (let next = await IteratorNext(iterated);
+ !next.done;
+ next = await IteratorNext(iterated)) {
+ // Step c.
+ const value = next.value;
+
+ needClose = true;
+ // Step d.
+ const mapped = await callContentFunction(mapper, undefined, value);
+ // Steps f-k.
+ for await (const innerValue of allowContentIter(mapped)) {
+ yield innerValue;
+ }
+ needClose = false;
+ }
+ } finally {
+ if (needClose) {
+ AsyncIteratorClose(iterated);
+ }
+ }
+}
+
+/* Iterator Helpers proposal 2.1.6.8 */
+async function AsyncIteratorReduce(reducer/*, initialValue*/) {
+ // Step 1.
+ const iterated = GetAsyncIteratorDirectWrapper(this);
+
+ // Step 2.
+ if (!IsCallable(reducer)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, reducer));
+ }
+
+ // Step 3.
+ let accumulator;
+ if (arguments.length === 1) {
+ // Step a.
+ const next = await callContentFunction(iterated.next, iterated);
+ if (!IsObject(next)) {
+ ThrowTypeError(JSMSG_OBJECT_REQUIRED, DecompileArg(0, next));
+ }
+ // Step b.
+ if (next.done) {
+ ThrowTypeError(JSMSG_EMPTY_ITERATOR_REDUCE);
+ }
+ // Step c.
+ accumulator = next.value;
+ } else {
+ // Step 4.
+ accumulator = arguments[1];
+ }
+
+ // Step 5.
+ for await (const value of allowContentIter(iterated)) {
+ // Steps d-h.
+ accumulator = await callContentFunction(reducer, undefined, accumulator, value);
+ }
+ // Step 5b.
+ return accumulator;
+}
+
+/* Iterator Helpers proposal 2.1.6.9 */
+async function AsyncIteratorToArray() {
+ // Step 1.
+ const iterated = {[std_asyncIterator]: () => this};
+ // Step 2.
+ const items = [];
+ let index = 0;
+ // Step 3.
+ for await (const value of allowContentIter(iterated)) {
+ // Step d.
+ _DefineDataProperty(items, index++, value);
+ }
+ // Step 3b.
+ return items;
+}
+
+/* Iterator Helpers proposal 2.1.6.10 */
+async function AsyncIteratorForEach(fn) {
+ // Step 1.
+ const iterated = GetAsyncIteratorDirectWrapper(this);
+
+ // Step 2.
+ if (!IsCallable(fn)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, fn));
+ }
+
+ // Step 3.
+ for await (const value of allowContentIter(iterated)) {
+ // Steps d-g.
+ await callContentFunction(fn, undefined, value);
+ }
+}
+
+/* Iterator Helpers proposal 2.1.6.11 */
+async function AsyncIteratorSome(fn) {
+ // Step 1.
+ const iterated = GetAsyncIteratorDirectWrapper(this);
+
+ // Step 2.
+ if (!IsCallable(fn)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, fn));
+ }
+
+ // Step 3.
+ for await (const value of allowContentIter(iterated)) {
+ // Steps d-h.
+ if (await callContentFunction(fn, undefined, value)) {
+ return true;
+ }
+ }
+ // Step 3b.
+ return false;
+}
+
+/* Iterator Helpers proposal 2.1.6.12 */
+async function AsyncIteratorEvery(fn) {
+ // Step 1.
+ const iterated = GetAsyncIteratorDirectWrapper(this);
+
+ // Step 2.
+ if (!IsCallable(fn)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, fn));
+ }
+
+ // Step 3.
+ for await (const value of allowContentIter(iterated)) {
+ // Steps d-h.
+ if (!await callContentFunction(fn, undefined, value)) {
+ return false;
+ }
+ }
+ // Step 3b.
+ return true;
+}
+
+/* Iterator Helpers proposal 2.1.6.13 */
+async function AsyncIteratorFind(fn) {
+ // Step 1.
+ const iterated = GetAsyncIteratorDirectWrapper(this);
+
+ // Step 2.
+ if (!IsCallable(fn)) {
+ ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, fn));
+ }
+
+ // Step 3.
+ for await (const value of allowContentIter(iterated)) {
+ // Steps d-h.
+ if (await callContentFunction(fn, undefined, value)) {
+ return value;
+ }
+ }
+}