Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1753)

Unified Diff: test/mjsunit/harmony/async-from-sync-iterator.js

Issue 2645313003: [async-iteration] implement Async-from-Sync Iterator (Closed)
Patch Set: cleanmerge Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « test/cctest/interpreter/test-bytecode-generator.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: test/mjsunit/harmony/async-from-sync-iterator.js
diff --git a/test/mjsunit/harmony/async-from-sync-iterator.js b/test/mjsunit/harmony/async-from-sync-iterator.js
new file mode 100644
index 0000000000000000000000000000000000000000..9ca33c995d8baa33b6925373443e959d27ce56f6
--- /dev/null
+++ b/test/mjsunit/harmony/async-from-sync-iterator.js
@@ -0,0 +1,670 @@
+// Copyright 2017 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-async-iteration --allow-natives-syntax
+
+let testFailed = false;
+let testFailure;
+
+function assertThrowsAsync(run, errorType, message) {
+ var actual;
+ var hadValue = false;
+ var hadError = false;
+ var promise = run();
+
+ if (typeof promise !== "object" || typeof promise.then !== "function") {
+ throw new MjsUnitAssertionError(
+ "Expected " + run.toString() +
+ " to return a Promise, but it returned " + PrettyPrint(promise));
+ }
+
+ promise.then(function(value) { hadValue = true; actual = value; },
+ function(error) { hadError = true; actual = error; });
+
+ assertFalse(hadValue || hadError);
+
+ %RunMicrotasks();
+
+ if (!hadError) {
+ throw new MjsUnitAssertionError(
+ "Expected " + run + "() to throw " + errorType.name +
+ ", but did not throw.");
+ }
+ if (!(actual instanceof errorType))
+ throw new MjsUnitAssertionError(
+ "Expected " + run + "() to throw " + errorType.name +
+ ", but threw '" + actual + "'");
+ if (message !== void 0 && actual.message !== message)
+ throw new MjsUnitAssertionError(
+ "Expected " + run + "() to throw '" + message + "', but threw '" +
+ actual.message + "'");
+};
+
+function resolveLater(value) {
+ return new Promise(function(resolve) {
+ Promise.resolve().then(function() {
+ resolve(value);
+ });
+ });
+}
+
+function rejectLater(value) {
+ return new Promise(function(resolve, reject) {
+ Promise.resolve().then(function() {
+ reject(value);
+ });
+ });
+}
+
+const kNext = 1;
+const kThrow = 2;
+const kReturn = 4;
+const kNextThrows = kNext | 8;
+const kReturnThrows = kReturn | 16;
+const kThrowNormal = kThrow | 32;
+const kNextUnchanged = kNext | 64;
+const kReturnUnchanged = kReturn | 128;
+const kThrowUnchanged = kThrow | 256;
+function sync(array, features, log) {
+ // `log` is a required parameter
+ if (log === void 0) %AbortJS("`log` is undefined");
+
+ let i = 0;
+ let methods = {
+ next(sent) {
+ let done = i >= array.length;
+ let value = array[i];
+ log.push({ method: "next", sent, value, done });
+ if ((features & kNextThrows) === kNextThrows) throw sent;
+ if ((features & kNextUnchanged) === kNextUnchanged) return sent;
+ i++;
+ return { value, done };
+ },
+ throw(sent) {
+ let done = i >= array.length;
+ log.push({ method: "throw", sent, done });
+ if ((features & kThrowNormal) === kThrowNormal)
+ return { value: sent, done };
+ if ((features & kThrowUnchanged) === kThrowUnchanged) return sent;
+ throw sent;
+ },
+ return(sent) {
+ let done = true;
+ log.push({ method: "return", sent, done });
+ if ((features & kReturnThrows) === kReturnThrows) throw sent;
+ if ((features & kReturnUnchanged) === kReturnUnchanged) return sent;
+ return { value: sent, done };
+ }
+ };
+ return {
+ [Symbol.iterator]() { return this; },
+ next: (features & kNext) ? methods.next : undefined,
+ throw: (features & kThrow) ? methods.throw : undefined,
+ return: (features & kReturn) ? methods.return : undefined
+ };
+}
+
+class MyError extends Error {};
+
+(async function AsyncFromSyncWithGenerator() {
+ function* gen() {
+ yield "sync value";
+ try {
+ yield new Promise(function(resolve) {
+ resolve("async value");
+ });
+ } catch (error) {
+ throw error;
+ }
+ assertUnreachable("generator is closed");
+ }
+ let iter = %CreateAsyncFromSyncIterator(gen());
+
+ // [Async-from-Sync Iterator] wraps sync iterator values in a Promise
+ let promise = iter.next();
+ assertInstanceof(promise, Promise);
+ let iter_result = await promise;
+ assertEquals({ value: "sync value", done: false }, iter_result);
+
+ // [Async-from-Sync Iterator] will wait for resolution of Promise values
+ promise = iter.next();
+ assertInstanceof(promise, Promise);
+ iter_result = await promise;
+ assertEquals({ value: "async value", done: false }, iter_result);
+
+ // [Async-from-Sync Iterator].throw delegates to .throw() method of sync
+ // iterator.
+ promise = iter.throw(new MyError("Error#1"));
+ assertInstanceof(promise, Promise);
+ try {
+ await promise;
+ assertUnreachable("promise should be rejected");
+ } catch (e) {
+ // If assertUnreachable failed, rethrow
+ if (e instanceof MjsUnitAssertionError) throw e;
+ assertInstanceof(e, MyError);
+ assertEquals("Error#1", e.message);
+ }
+
+ // Generator is closed, subsequent calls to .next() will not resume.
+ promise = iter.next("floof");
+ iter_result = await promise;
+ assertEquals({ value: undefined, done: true }, iter_result);
+
+ promise = iter.return("generator closed");
+ assertInstanceof(promise, Promise);
+ iter_result = await promise;
+ assertEquals({ value: "generator closed", done: true }, iter_result);
+
+ // .next(), .return() and .throw() delegate to sync iterator methods, without
+ // keeping track of the state of the generator.
+ promise = iter.next("unused");
+ assertInstanceof(promise, Promise);
+ iter_result = await promise;
+ assertEquals({ value: undefined, done: true }, iter_result);
+
+ promise = iter.throw(new MyError("Error#2"));
+ assertInstanceof(promise, Promise);
+ try {
+ await promise;
+ assertUnreachable("promise should be rejected");
+ } catch (e) {
+ // If assertUnreachable failed, rethrow
+ if (e instanceof MjsUnitAssertionError) throw e;
+ assertInstanceof(e, MyError);
+ assertEquals("Error#2", e.message);
+ }
+
+ promise = iter.return("return-after-completed");
+ assertInstanceof(promise, Promise);
+ iter_result = await promise;
+ assertEquals({ value: "return-after-completed", done: true }, iter_result);
+})().catch(function(error) {
+ testFailed = true;
+ testFailure = error;
+});
+
+%RunMicrotasks();
+if (testFailed) {
+ throw testFailure;
+}
+
+
+(async function AsyncFromSyncOrderOfOperations() {
+ let log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], 0, log));
+
+ try {
+ await iter.next();
+ assertUnreachable("Iterator.next() method is not optional");
+ } catch (e) {
+ assertInstanceof(e, TypeError);
+ assertEquals([], log);
+ }
+
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], kNext, log));
+ assertEquals({ value: "sync-value", done: false }, await iter.next("a"));
+ assertEquals([
+ {
+ method: "next",
+ sent: "a",
+ value: "sync-value",
+ done: false
+ }
+ ], log);
+
+ log = [];
+ let asyncValue = resolveLater("async-value");
+ iter = %CreateAsyncFromSyncIterator(sync([asyncValue], kNext, log));
+ assertEquals({ value: "async-value", done: false }, await iter.next("b"));
+ assertEquals([
+ {
+ method: "next",
+ sent: "b",
+ value: asyncValue,
+ done: false
+ }
+ ], log);
+
+ // If [sync_iterator].next() produces a rejected Promise or an exception is
+ // thrown, Promise is rejected with thrown/rejected value.
+ log = [];
+ asyncValue = rejectLater("Boo!");
+ iter = %CreateAsyncFromSyncIterator(sync([asyncValue], kNext, log));
+ try {
+ await iter.next('c');
+ assertUnreachable('Expected `iter.next(\'c\') to throw, but did not throw');
+ } catch (e) {
+ assertEquals("Boo!", e);
+ assertEquals([
+ {
+ method: 'next',
+ sent: 'c',
+ value: asyncValue,
+ done: false
+ }
+ ], log);
+ }
+
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNextThrows, log));
+ try {
+ await iter.next('Boo!');
+ assertUnreachable('Expected `iter.next(\'c\') to throw, but did not throw');
+ } catch (e) {
+ assertEquals("Boo!", e);
+ assertEquals([
+ {
+ method: 'next',
+ sent: 'Boo!',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+ }
+
+
+ // [Async-from-Sync Iterator].next() will be rejected with a TypeError if
+ // Type([sync_iterator].next()) is not Object.
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNextUnchanged,
+ log));
+ try {
+ await iter.next('not-a-JSReceiver');
+ assertUnreachable('Expected `iter.next(\'not-a-JSReceiver\')` to ' +
+ 'throw, but did not throw')
+ } catch (e) {
+ assertEquals(e.constructor, TypeError);
+ }
+
+ assertEquals([
+ {
+ method: 'next',
+ sent: 'not-a-JSReceiver',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+
+ // If [sync_iterator] does not have a .return() method, return a Promise
+ // resolved with the value `{ value: <<sent value>>, done: true }`.
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-return'], kNext, log));
+ assertEquals({
+ value: 'd',
+ done: true
+ }, await iter.return('d'));
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({
+ value: 'sync-return',
+ done: false
+ }, await iter.next('e'));
+
+ assertEquals([
+ {
+ method: 'next',
+ sent: 'e',
+ value: 'sync-return',
+ done: false
+ }
+ ], log);
+
+ // If [sync_iterator] does have a .return() method, return a Promise
+ // fulfilled with the iterator result of [sync_iterator].return().
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-return'],
+ kNext|kReturn, log));
+ assertEquals({
+ value: 'f',
+ done: true
+ }, await iter.return('f'));
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({
+ value: 'sync-return',
+ done: false
+ }, await iter.next('g'));
+
+ assertEquals([
+ {
+ method: 'return',
+ sent: 'f',
+ done: true
+ },
+ {
+ method: 'next',
+ sent: 'g',
+ value: 'sync-return',
+ done: false
+ }
+ ], log);
+
+ // If [sync_iterator].return() produces a rejected Promise or an exception is
+ // thrown, Promise is rejected with thrown/rejected value.
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kReturnThrows,
+ log));
+ try {
+ await iter.return('Boo!!');
+ assertUnreachable('Expected `iter.return(\'Boo!!\')` to throw, but did ' +
+ 'not throw');
+ } catch (e) {
+ assertEquals("Boo!!", e);
+ }
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({ value: 'sync-value', done: false }, await iter.next('h'));
+ assertEquals([
+ {
+ method: 'return',
+ sent: 'Boo!!',
+ done: true
+ },
+ {
+ method: 'next',
+ sent: 'h',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+
+
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kReturn, log));
+
+ let rejection = Promise.reject('Boo!!');
+ try {
+ await iter.return(rejection);
+ assertUnreachable('Expected `iter.return(Promise.reject(\'Boo!!\'))` to ' +
+ 'throw, but did not throw');
+ } catch (e) {
+ assertEquals('Boo!!', e);
+ }
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({ value: 'sync-value', done: false }, await iter.next('i'));
+ assertEquals([
+ {
+ method: 'return',
+ sent: rejection,
+ done: true
+ },
+ {
+ method: 'next',
+ sent: 'i',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+
+ // [Async-from-Sync Iterator].return() will be rejected with a TypeError if
+ // Type([sync_iterator].return()) is not Object.
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'],
+ kNext|kReturnUnchanged, log));
+ try {
+ await iter.return('not-a-JSReceiver');
+ assertUnreachable('Expected `iter.return(\'not-a-JSReceiver\')` to ' +
+ 'throw, but did not throw')
+ } catch (e) {
+ assertEquals(e.constructor, TypeError);
+ }
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({ value: 'sync-value', done: false }, await iter.next('j'));
+ assertEquals([
+ {
+ method: 'return',
+ sent: 'not-a-JSReceiver',
+ done: true
+ },
+ {
+ method: 'next',
+ sent: 'j',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+
+ // If [sync_iterator] does not have a .throw method, return a Promise rejected
+ // with the sent value.
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext, log));
+ try {
+ await iter.throw('Boo!!');
+ assertUnreachable('Expected iter.throw(\'Boo!!\') to throw, but did not ' +
+ 'throw');
+ } catch (e) {
+ assertEquals('Boo!!', e);
+ }
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({ value: 'sync-value', done: false }, await iter.next('k'));
+ assertEquals([
+ {
+ method: 'next',
+ sent: 'k',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+
+
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kThrow, log));
+ try {
+ await iter.throw('Boo!!');
+ assertUnreachable('Expected iter.throw(\'Boo!!\') to throw, but did not ' +
+ 'throw');
+ } catch (e) {
+ assertEquals('Boo!!', e);
+ }
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({ value: 'sync-value', done: false }, await iter.next('l'));
+ assertEquals([
+ {
+ method: 'throw',
+ sent: 'Boo!!',
+ done: false
+ },
+ {
+ method: 'next',
+ sent: 'l',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+
+ // If [sync_iterator].throw() returns a resolved Promise or a Completion
+ // with [[Type]] "normal" or "return", return a resolved Promise
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kThrowNormal,
+ log));
+ assertEquals({
+ value: 'Boo!!',
+ done: false
+ }, await iter.throw('Boo!!'));
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({ value: 'sync-value', done: false }, await iter.next('m'));
+ assertEquals([
+ {
+ method: 'throw',
+ sent: 'Boo!!',
+ done: false
+ },
+ {
+ method: 'next',
+ sent: 'm',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+
+ // [Async-from-Sync Iterator].throw() will be rejected with a TypeError if
+ // Type([sync_iterator].throw()) is not Object.
+ log = [];
+ iter = %CreateAsyncFromSyncIterator(sync(['sync-value'],
+ kNext|kThrowUnchanged, log));
+ try {
+ await iter.throw('not-a-JSReceiver');
+ assertUnreachable('Expected `iter.throw(\'not-a-JSReceiver\')` to ' +
+ 'throw, but did not throw')
+ } catch (e) {
+ assertEquals(e.constructor, TypeError);
+ }
+
+ // [Async-from-Sync Iterator] merely delegates, and does not keep track of
+ // whether [sync_iterator] is completed or not.
+ assertEquals({ value: 'sync-value', done: false }, await iter.next('n'));
+ assertEquals([
+ {
+ method: 'throw',
+ sent: 'not-a-JSReceiver',
+ done: false
+ },
+ {
+ method: 'next',
+ sent: 'n',
+ value: 'sync-value',
+ done: false
+ }
+ ], log);
+
+ // Let nextValue be IteratorValue(nextResult).
+ // IfAbruptRejectPromise(nextValue, promiseCapability).)
+ iter = %CreateAsyncFromSyncIterator({
+ next() { return { get value() { throw "BadValue!" }, done: false }; }
+ });
+ try {
+ await iter.next();
+ assertUnreachable('Expected `iter.next()` to throw, but did not throw');
+ } catch (e) {
+ assertEquals('BadValue!', e);
+ }
+
+ // Let nextDone be IteratorComplete(nextResult).
+ // IfAbruptRejectPromise(nextDone, promiseCapability).
+ iter = %CreateAsyncFromSyncIterator({
+ next() { return { value: undefined, get done() { throw "BadValue!" } }; }
+ });
+ try {
+ await iter.next();
+ assertUnreachable('Expected `iter.next()` to throw, but did not throw');
+ } catch (e) {
+ assertEquals('BadValue!', e);
+ }
+
+ // IfAbruptRejectPromise(returnResult, promiseCapability).
+ // Let returnValue be IteratorValue(returnResult).
+ iter = %CreateAsyncFromSyncIterator({
+ return() { return { get value() { throw "BadValue!" }, done: false }; }
+ });
+ try {
+ await iter.return();
+ assertUnreachable('Expected `iter.return()` to throw, but did not throw');
+ } catch (e) {
+ assertEquals('BadValue!', e);
+ }
+
+ // IfAbruptRejectPromise(returnValue, promiseCapability).
+ // Let returnDone be IteratorComplete(returnResult).
+ iter = %CreateAsyncFromSyncIterator({
+ return() { return { value: undefined, get done() { throw "BadValue!" } }; }
+ });
+ try {
+ await iter.return();
+ assertUnreachable('Expected `iter.return()` to throw, but did not throw');
+ } catch (e) {
+ assertEquals('BadValue!', e);
+ }
+
+ // IfAbruptRejectPromise(throwResult, promiseCapability).
+ // Let throwValue be IteratorValue(throwResult).
+ iter = %CreateAsyncFromSyncIterator({
+ throw() { return { get value() { throw "BadValue!" }, done: false }; }
+ });
+ try {
+ await iter.throw();
+ assertUnreachable('Expected `iter.throw()` to throw, but did not throw');
+ } catch (e) {
+ assertEquals('BadValue!', e);
+ }
+
+ // IfAbruptRejectPromise(throwValue, promiseCapability).
+ // Let throwDone be IteratorComplete(throwResult).
+ iter = %CreateAsyncFromSyncIterator({
+ throw() { return { value: undefined, get done() { throw "BadValue!" } }; }
+ });
+ try {
+ await iter.throw();
+ assertUnreachable('Expected `iter.throw()` to throw, but did not throw');
+ } catch (e) {
+ assertEquals('BadValue!', e);
+ }
+})().catch(function(error) {
+ testFailed = true;
+ testFailure = error;
+});
+
+%RunMicrotasks();
+if (testFailed) {
+ throw testFailure;
+}
+
+(function ExtractedAsyncFromSyncIteratorMethods() {
+ // Async-from-Sync iterator methods can be extracted via function.caller.
+ // TODO(caitp): test extracted `throw` method using yield* in async generator.
+ let extractor = [0, 1, 2, 3, 4,5,6,7,8,9];
+ let extractedNext;
+ let extractedReturn;
+
+ extractor[Symbol.iterator] = function() {
+ let it = [][Symbol.iterator].call(extractor);
+ let origNext = it.next, origThrow = it.throw, origReturn = it.return;
+ function extractNext() {
+ extractedNext = extractNext.caller;
+ return origNext;
+ }
+ function extractReturn() {
+ extractedReturn = extractReturn.caller;
+ return origReturn;
+ }
+ Object.defineProperties(it, {
+ "next": { get: extractNext, configurable: true },
+ "return": { get: extractReturn, configurable: true }
+ });
+ return it;
+ };
+
+ async function f() {
+ let i;
+ let it = extractor[Symbol.iterator]();
+ for await (let x of it) break;
+ for await (let x of it) return "x";
+ }
+
+ // Cycle through `f` to extract iterator methods
+ f().catch(function() { %AbortJS("No error should have occurred"); });
+ %RunMicrotasks();
+
+ assertEquals(typeof extractedNext, "function");
+ assertThrowsAsync(() => extractedNext.call(undefined), TypeError);
+ assertThrowsAsync(() => extractedNext.call(1), TypeError);
+
+ assertEquals(typeof extractedReturn, "function");
+ assertThrowsAsync(() => extractedReturn.call(undefined), TypeError);
+ assertThrowsAsync(() => extractedReturn.call(1), TypeError);
+})();
« no previous file with comments | « test/cctest/interpreter/test-bytecode-generator.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698