| 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..2f0e674a316b2c561b0f5d5a6993e8ee27061f33 | 
| --- /dev/null | 
| +++ b/test/mjsunit/harmony/async-from-sync-iterator.js | 
| @@ -0,0 +1,437 @@ | 
| +// 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; | 
| + | 
| +class MyError extends Error {}; | 
| + | 
| +(async function() { | 
| +  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) { | 
| +    assertInstanceof(e, MyError); | 
| +    assertEquals("Error#1", e.message); | 
| +  } | 
| + | 
| +  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; | 
| +  } catch (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); | 
| + | 
| +  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; | 
| +  function sync(array, features, log = []) { | 
| +    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; | 
| +        i++; | 
| +        return { value, done }; | 
| +      }, | 
| +      throw(sent) { | 
| +        let done = i >= array.length; | 
| +        let value = undefined; | 
| +        log.push({ method: "throw", sent, value, done }); | 
| +        if ((features & kThrowNormal) === kThrowNormal) | 
| +            return { value: sent, done }; | 
| +        throw sent; | 
| +      }, | 
| +      return(sent) { | 
| +        let done = true; | 
| +        let value = undefined; | 
| +        log.push({ method: "return", sent, value, done }); | 
| +        if ((features & kReturnThrows) === kReturnThrows) throw sent; | 
| +        return { value, done }; | 
| +      } | 
| +    }; | 
| +    return { | 
| +      [Symbol.iterator]() { return this; }, | 
| +      next: (features & kNext) ? methods.next : undefined, | 
| +      throw: (features & kThrow) ? methods.throw : undefined, | 
| +      return: (features & kReturn) ? methods.return : undefined | 
| +    }; | 
| +  } | 
| + | 
| +  let log = []; | 
| +  iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], 0)); | 
| + | 
| +  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); | 
| +  } | 
| + | 
| +  // If [sync_iterator].return() does not exist, 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].return() does exist, return a Promise resolved with | 
| +  // the iterator result of [sync_iterator].return(). | 
| +  log = []; | 
| +  iter = %CreateAsyncFromSyncIterator(sync(['sync-return'], | 
| +                                      kNext|kReturn, log)); | 
| +  assertEquals({ | 
| +    value: undefined, | 
| +    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', | 
| +      value: undefined, | 
| +      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!!', | 
| +      value: undefined, | 
| +      done: true | 
| +    }, | 
| +    { | 
| +      method: 'next', | 
| +      sent: 'h', | 
| +      value: 'sync-value', | 
| +      done: false | 
| +    } | 
| +  ], log); | 
| + | 
| +  // If [sync_iterator].throw() does exist, 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('i')); | 
| +  assertEquals([ | 
| +    { | 
| +      method: 'next', | 
| +      sent: 'i', | 
| +      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('j')); | 
| +  assertEquals([ | 
| +    { | 
| +      method: 'throw', | 
| +      sent: 'Boo!!', | 
| +      value: undefined, | 
| +      done: false | 
| +    }, | 
| +    { | 
| +      method: 'next', | 
| +      sent: 'j', | 
| +      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('k')); | 
| +  assertEquals([ | 
| +    { | 
| +      method: 'throw', | 
| +      sent: 'Boo!!', | 
| +      value: undefined, | 
| +      done: false | 
| +    }, | 
| +    { | 
| +      method: 'next', | 
| +      sent: 'k', | 
| +      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; | 
| +} | 
|  |