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 |