Chromium Code Reviews| 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; | |
|
neis
2017/02/20 09:41:20
What's the point of catching and rethrowing here?
caitp
2017/02/20 17:09:34
it's a basic control flow test. The .throw call ge
neis
2017/02/21 12:48:08
I don't get your point, but feel free to keep it a
| |
| 21 } | |
| 22 assertUnreachable("generator is closed"); | |
|
neis
2017/02/20 09:41:20
I was at first confused by this. Could you just re
caitp
2017/02/20 17:09:35
I'm not entirely sure what you mean --- after the
neis
2017/02/21 12:48:08
What confused me is that this line is not unreacha
caitp
2017/02/21 14:36:52
It is unreachable, because the throw on line 20 re
| |
| 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); | |
|
neis
2017/02/20 09:41:20
You already tested .return above, didn't you?
caitp
2017/02/20 17:09:35
it's good to have an extra one thrown in as it hel
| |
| 75 | |
|
neis
2017/02/20 09:41:20
Can you somehow separate the tests using gen (abov
caitp
2017/02/20 17:09:35
Acknowledged.
| |
| 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 = []) { | |
|
neis
2017/02/20 09:41:20
There shouldn't be a default value for log (see co
caitp
2017/02/20 17:09:35
Removed the default value, and added a %AbortJS()
| |
| 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 }); | |
|
neis
2017/02/20 09:41:20
I think you can drop value here and in return belo
caitp
2017/02/20 17:09:35
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 }; | |
|
neis
2017/02/20 09:41:20
Not sure why you use undefined as the value in ret
caitp
2017/02/20 17:09:35
It's pretty inconsequential to the test either way
| |
| 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)); | |
|
neis
2017/02/20 09:41:20
You forgot to pass the log.
caitp
2017/02/20 17:09:35
Good catch, thx
| |
| 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'); | |
|
neis
2017/02/20 09:41:20
It's the await that throws, not the iter.next call
caitp
2017/02/20 17:09:35
throw/reject are admittedly being used inferchange
| |
| 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 } | |
|
neis
2017/02/20 09:41:20
Can you also test the case where the result of the
| |
| 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. | |
|
neis
2017/02/20 09:41:20
Do you mean "does not exist" here?
caitp
2017/02/20 17:09:35
"if [sync_iterator] does not have a .throw method"
| |
| 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 = []; | |
|
neis
2017/02/20 09:41:20
A function call cannot return a "normal" completio
| |
| 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 } | |
|
neis
2017/02/20 09:41:20
Could you please add
- a test where the sync itera
| |
| OLD | NEW |