OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 // Flags: --harmony-async-iteration --allow-natives-syntax |
| 6 |
| 7 let testFailed = false; |
| 8 let testFailure; |
| 9 |
| 10 class MyError extends Error {}; |
| 11 |
| 12 (async function() { |
| 13 function* gen() { |
| 14 yield "sync value"; |
| 15 try { |
| 16 yield new Promise(function(resolve) { |
| 17 resolve("async value"); |
| 18 }); |
| 19 } catch (error) { |
| 20 throw error; |
| 21 } |
| 22 assertUnreachable("generator is closed"); |
| 23 } |
| 24 let iter = %CreateAsyncFromSyncIterator(gen()); |
| 25 |
| 26 // [Async-from-Sync Iterator] wraps sync iterator values in a Promise |
| 27 let promise = iter.next(); |
| 28 assertInstanceof(promise, Promise); |
| 29 let iter_result = await promise; |
| 30 assertEquals({ value: "sync value", done: false }, iter_result); |
| 31 |
| 32 // [Async-from-Sync Iterator] will wait for resolution of Promise values |
| 33 promise = iter.next(); |
| 34 assertInstanceof(promise, Promise); |
| 35 iter_result = await promise; |
| 36 assertEquals({ value: "async value", done: false }, iter_result); |
| 37 |
| 38 // [Async-from-Sync Iterator].throw delegates to .throw() method of sync |
| 39 // iterator. |
| 40 promise = iter.throw(new MyError("Error#1")); |
| 41 assertInstanceof(promise, Promise); |
| 42 try { |
| 43 await promise; |
| 44 assertUnreachable("promise should be rejected"); |
| 45 } catch (e) { |
| 46 assertInstanceof(e, MyError); |
| 47 assertEquals("Error#1", e.message); |
| 48 } |
| 49 |
| 50 promise = iter.return("generator closed"); |
| 51 assertInstanceof(promise, Promise); |
| 52 iter_result = await promise; |
| 53 assertEquals({ value: "generator closed", done: true }, iter_result); |
| 54 |
| 55 // .next(), .return() and .throw() delegate to sync iterator methods, without |
| 56 // keeping track of the state of the generator. |
| 57 promise = iter.next("unused"); |
| 58 assertInstanceof(promise, Promise); |
| 59 iter_result = await promise; |
| 60 assertEquals({ value: undefined, done: true }, iter_result); |
| 61 |
| 62 promise = iter.throw(new MyError("Error#2")); |
| 63 assertInstanceof(promise, Promise); |
| 64 try { |
| 65 await promise; |
| 66 } catch (e) { |
| 67 assertInstanceof(e, MyError); |
| 68 assertEquals("Error#2", e.message); |
| 69 } |
| 70 |
| 71 promise = iter.return("return-after-completed"); |
| 72 assertInstanceof(promise, Promise); |
| 73 iter_result = await promise; |
| 74 assertEquals({ value: "return-after-completed", done: true }, iter_result); |
| 75 |
| 76 function resolveLater(value) { |
| 77 return new Promise(function(resolve) { |
| 78 Promise.resolve().then(function() { |
| 79 resolve(value); |
| 80 }); |
| 81 }); |
| 82 } |
| 83 |
| 84 function rejectLater(value) { |
| 85 return new Promise(function(resolve, reject) { |
| 86 Promise.resolve().then(function() { |
| 87 reject(value); |
| 88 }); |
| 89 }); |
| 90 } |
| 91 |
| 92 const kNext = 1; |
| 93 const kThrow = 2; |
| 94 const kReturn = 4; |
| 95 const kNextThrows = kNext | 8; |
| 96 const kReturnThrows = kReturn | 16; |
| 97 const kThrowNormal = kThrow | 32; |
| 98 function sync(array, features, log = []) { |
| 99 let i = 0; |
| 100 let methods = { |
| 101 next(sent) { |
| 102 let done = i >= array.length; |
| 103 let value = array[i]; |
| 104 log.push({ method: "next", sent, value, done }); |
| 105 if ((features & kNextThrows) === kNextThrows) throw sent; |
| 106 i++; |
| 107 return { value, done }; |
| 108 }, |
| 109 throw(sent) { |
| 110 let done = i >= array.length; |
| 111 let value = undefined; |
| 112 log.push({ method: "throw", sent, value, done }); |
| 113 if ((features & kThrowNormal) === kThrowNormal) |
| 114 return { value: sent, done }; |
| 115 throw sent; |
| 116 }, |
| 117 return(sent) { |
| 118 let done = true; |
| 119 let value = undefined; |
| 120 log.push({ method: "return", sent, value, done }); |
| 121 if ((features & kReturnThrows) === kReturnThrows) throw sent; |
| 122 return { value, done }; |
| 123 } |
| 124 }; |
| 125 return { |
| 126 [Symbol.iterator]() { return this; }, |
| 127 next: (features & kNext) ? methods.next : undefined, |
| 128 throw: (features & kThrow) ? methods.throw : undefined, |
| 129 return: (features & kReturn) ? methods.return : undefined |
| 130 }; |
| 131 } |
| 132 |
| 133 let log = []; |
| 134 iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], 0)); |
| 135 |
| 136 try { |
| 137 await iter.next(); |
| 138 assertUnreachable("Iterator.next() method is not optional"); |
| 139 } catch (e) { |
| 140 assertInstanceof(e, TypeError); |
| 141 assertEquals([], log); |
| 142 } |
| 143 |
| 144 log = []; |
| 145 iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], kNext, log)); |
| 146 assertEquals({ value: "sync-value", done: false }, await iter.next("a")); |
| 147 assertEquals([ |
| 148 { |
| 149 method: "next", |
| 150 sent: "a", |
| 151 value: "sync-value", |
| 152 done: false |
| 153 } |
| 154 ], log); |
| 155 |
| 156 log = []; |
| 157 let asyncValue = resolveLater("async-value"); |
| 158 iter = %CreateAsyncFromSyncIterator(sync([asyncValue], kNext, log)); |
| 159 assertEquals({ value: "async-value", done: false }, await iter.next("b")); |
| 160 assertEquals([ |
| 161 { |
| 162 method: "next", |
| 163 sent: "b", |
| 164 value: asyncValue, |
| 165 done: false |
| 166 } |
| 167 ], log); |
| 168 |
| 169 // If [sync_iterator].next() produces a rejected Promise or an exception is |
| 170 // thrown, Promise is rejected with thrown/rejected value. |
| 171 log = []; |
| 172 asyncValue = rejectLater("Boo!"); |
| 173 iter = %CreateAsyncFromSyncIterator(sync([asyncValue], kNext, log)); |
| 174 try { |
| 175 await iter.next('c'); |
| 176 assertUnreachable('Expected `iter.next(\'c\') to throw, but did not throw'); |
| 177 } catch (e) { |
| 178 assertEquals("Boo!", e); |
| 179 assertEquals([ |
| 180 { |
| 181 method: 'next', |
| 182 sent: 'c', |
| 183 value: asyncValue, |
| 184 done: false |
| 185 } |
| 186 ], log); |
| 187 } |
| 188 |
| 189 // If [sync_iterator].return() does not exist, return a Promise resolved with |
| 190 // the value `{ value: <<sent value>>, done: true }`. |
| 191 log = []; |
| 192 iter = %CreateAsyncFromSyncIterator(sync(['sync-return'], kNext, log)); |
| 193 assertEquals({ |
| 194 value: 'd', |
| 195 done: true |
| 196 }, await iter.return('d')); |
| 197 |
| 198 // [Async-from-Sync Iterator] merely delegates, and does not keep track of |
| 199 // whether [sync_iterator] is completed or not. |
| 200 assertEquals({ |
| 201 value: 'sync-return', |
| 202 done: false |
| 203 }, await iter.next('e')); |
| 204 |
| 205 assertEquals([ |
| 206 { |
| 207 method: 'next', |
| 208 sent: 'e', |
| 209 value: 'sync-return', |
| 210 done: false |
| 211 } |
| 212 ], log); |
| 213 |
| 214 // If [sync_iterator].return() does exist, return a Promise resolved with |
| 215 // the iterator result of [sync_iterator].return(). |
| 216 log = []; |
| 217 iter = %CreateAsyncFromSyncIterator(sync(['sync-return'], |
| 218 kNext|kReturn, log)); |
| 219 assertEquals({ |
| 220 value: undefined, |
| 221 done: true |
| 222 }, await iter.return('f')); |
| 223 |
| 224 // [Async-from-Sync Iterator] merely delegates, and does not keep track of |
| 225 // whether [sync_iterator] is completed or not. |
| 226 assertEquals({ |
| 227 value: 'sync-return', |
| 228 done: false |
| 229 }, await iter.next('g')); |
| 230 |
| 231 assertEquals([ |
| 232 { |
| 233 method: 'return', |
| 234 sent: 'f', |
| 235 value: undefined, |
| 236 done: true |
| 237 }, |
| 238 { |
| 239 method: 'next', |
| 240 sent: 'g', |
| 241 value: 'sync-return', |
| 242 done: false |
| 243 } |
| 244 ], log); |
| 245 |
| 246 // If [sync_iterator].return() produces a rejected Promise or an exception is |
| 247 // thrown, Promise is rejected with thrown/rejected value. |
| 248 log = []; |
| 249 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kReturnThrows, |
| 250 log)); |
| 251 try { |
| 252 await iter.return('Boo!!'); |
| 253 assertUnreachable('Expected `iter.return(\'Boo!!\') to throw, but did ' + |
| 254 'not throw'); |
| 255 } catch (e) { |
| 256 assertEquals("Boo!!", e); |
| 257 } |
| 258 |
| 259 // [Async-from-Sync Iterator] merely delegates, and does not keep track of |
| 260 // whether [sync_iterator] is completed or not. |
| 261 assertEquals({ value: 'sync-value', done: false }, await iter.next('h')); |
| 262 assertEquals([ |
| 263 { |
| 264 method: 'return', |
| 265 sent: 'Boo!!', |
| 266 value: undefined, |
| 267 done: true |
| 268 }, |
| 269 { |
| 270 method: 'next', |
| 271 sent: 'h', |
| 272 value: 'sync-value', |
| 273 done: false |
| 274 } |
| 275 ], log); |
| 276 |
| 277 // If [sync_iterator].throw() does exist, return a Promise rejected with |
| 278 // the sent value. |
| 279 log = []; |
| 280 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext, log)); |
| 281 try { |
| 282 await iter.throw('Boo!!'); |
| 283 assertUnreachable('Expected iter.throw(\'Boo!!\') to throw, but did not ' + |
| 284 'throw'); |
| 285 } catch (e) { |
| 286 assertEquals('Boo!!', e); |
| 287 } |
| 288 |
| 289 // [Async-from-Sync Iterator] merely delegates, and does not keep track of |
| 290 // whether [sync_iterator] is completed or not. |
| 291 assertEquals({ value: 'sync-value', done: false }, await iter.next('i')); |
| 292 assertEquals([ |
| 293 { |
| 294 method: 'next', |
| 295 sent: 'i', |
| 296 value: 'sync-value', |
| 297 done: false |
| 298 } |
| 299 ], log); |
| 300 |
| 301 log = []; |
| 302 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kThrow, log)); |
| 303 try { |
| 304 await iter.throw('Boo!!'); |
| 305 assertUnreachable('Expected iter.throw(\'Boo!!\') to throw, but did not ' + |
| 306 'throw'); |
| 307 } catch (e) { |
| 308 assertEquals('Boo!!', e); |
| 309 } |
| 310 |
| 311 // [Async-from-Sync Iterator] merely delegates, and does not keep track of |
| 312 // whether [sync_iterator] is completed or not. |
| 313 assertEquals({ value: 'sync-value', done: false }, await iter.next('j')); |
| 314 assertEquals([ |
| 315 { |
| 316 method: 'throw', |
| 317 sent: 'Boo!!', |
| 318 value: undefined, |
| 319 done: false |
| 320 }, |
| 321 { |
| 322 method: 'next', |
| 323 sent: 'j', |
| 324 value: 'sync-value', |
| 325 done: false |
| 326 } |
| 327 ], log); |
| 328 |
| 329 // If [sync_iterator].throw() returns a resolved Promise or a Completion |
| 330 // with [[Type]] "normal" or "return", return a resolved Promise |
| 331 log = []; |
| 332 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kThrowNormal, |
| 333 log)); |
| 334 assertEquals({ |
| 335 value: 'Boo!!', |
| 336 done: false |
| 337 }, await iter.throw('Boo!!')); |
| 338 |
| 339 // [Async-from-Sync Iterator] merely delegates, and does not keep track of |
| 340 // whether [sync_iterator] is completed or not. |
| 341 assertEquals({ value: 'sync-value', done: false }, await iter.next('k')); |
| 342 assertEquals([ |
| 343 { |
| 344 method: 'throw', |
| 345 sent: 'Boo!!', |
| 346 value: undefined, |
| 347 done: false |
| 348 }, |
| 349 { |
| 350 method: 'next', |
| 351 sent: 'k', |
| 352 value: 'sync-value', |
| 353 done: false |
| 354 } |
| 355 ], log); |
| 356 |
| 357 // Let nextValue be IteratorValue(nextResult). |
| 358 // IfAbruptRejectPromise(nextValue, promiseCapability).) |
| 359 iter = %CreateAsyncFromSyncIterator({ |
| 360 next() { return { get value() { throw "BadValue!" }, done: false }; } |
| 361 }); |
| 362 try { |
| 363 await iter.next(); |
| 364 assertUnreachable('Expected `iter.next()` to throw, but did not throw'); |
| 365 } catch (e) { |
| 366 assertEquals('BadValue!', e); |
| 367 } |
| 368 |
| 369 // Let nextDone be IteratorComplete(nextResult). |
| 370 // IfAbruptRejectPromise(nextDone, promiseCapability). |
| 371 iter = %CreateAsyncFromSyncIterator({ |
| 372 next() { return { value: undefined, get done() { throw "BadValue!" } }; } |
| 373 }); |
| 374 try { |
| 375 await iter.next(); |
| 376 assertUnreachable('Expected `iter.next()` to throw, but did not throw'); |
| 377 } catch (e) { |
| 378 assertEquals('BadValue!', e); |
| 379 } |
| 380 |
| 381 // IfAbruptRejectPromise(returnResult, promiseCapability). |
| 382 // Let returnValue be IteratorValue(returnResult). |
| 383 iter = %CreateAsyncFromSyncIterator({ |
| 384 return() { return { get value() { throw "BadValue!" }, done: false }; } |
| 385 }); |
| 386 try { |
| 387 await iter.return(); |
| 388 assertUnreachable('Expected `iter.return()` to throw, but did not throw'); |
| 389 } catch (e) { |
| 390 assertEquals('BadValue!', e); |
| 391 } |
| 392 |
| 393 // IfAbruptRejectPromise(returnValue, promiseCapability). |
| 394 // Let returnDone be IteratorComplete(returnResult). |
| 395 iter = %CreateAsyncFromSyncIterator({ |
| 396 return() { return { value: undefined, get done() { throw "BadValue!" } }; } |
| 397 }); |
| 398 try { |
| 399 await iter.return(); |
| 400 assertUnreachable('Expected `iter.return()` to throw, but did not throw'); |
| 401 } catch (e) { |
| 402 assertEquals('BadValue!', e); |
| 403 } |
| 404 |
| 405 // IfAbruptRejectPromise(throwResult, promiseCapability). |
| 406 // Let throwValue be IteratorValue(throwResult). |
| 407 iter = %CreateAsyncFromSyncIterator({ |
| 408 throw() { return { get value() { throw "BadValue!" }, done: false }; } |
| 409 }); |
| 410 try { |
| 411 await iter.throw(); |
| 412 assertUnreachable('Expected `iter.throw()` to throw, but did not throw'); |
| 413 } catch (e) { |
| 414 assertEquals('BadValue!', e); |
| 415 } |
| 416 |
| 417 // IfAbruptRejectPromise(throwValue, promiseCapability). |
| 418 // Let throwDone be IteratorComplete(throwResult). |
| 419 iter = %CreateAsyncFromSyncIterator({ |
| 420 throw() { return { value: undefined, get done() { throw "BadValue!" } }; } |
| 421 }); |
| 422 try { |
| 423 await iter.throw(); |
| 424 assertUnreachable('Expected `iter.throw()` to throw, but did not throw'); |
| 425 } catch (e) { |
| 426 assertEquals('BadValue!', e); |
| 427 } |
| 428 })().catch(function(error) { |
| 429 testFailed = true; |
| 430 testFailure = error; |
| 431 }); |
| 432 |
| 433 %RunMicrotasks(); |
| 434 |
| 435 if (testFailed) { |
| 436 throw testFailure; |
| 437 } |
OLD | NEW |