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

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

Issue 2645313003: [async-iteration] implement Async-from-Sync Iterator (Closed)
Patch Set: Refactor + add more tests 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 unified diff | Download patch
OLDNEW
(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 function assertThrowsAsync(run, errorType, message) {
11 var actual;
12 var hadValue = false;
13 var hadError = false;
14 var promise = run();
15
16 if (typeof promise !== "object" || typeof promise.then !== "function") {
17 throw new MjsUnitAssertionError(
18 "Expected " + run.toString() +
19 " to return a Promise, but it returned " + PrettyPrint(promise));
20 }
21
22 promise.then(function(value) { hadValue = true; actual = value; },
23 function(error) { hadError = true; actual = error; });
24
25 assertFalse(hadValue || hadError);
26
27 %RunMicrotasks();
28
29 if (!hadError) {
30 throw new MjsUnitAssertionError(
31 "Expected " + run + "() to throw " + errorType.name +
32 ", but did not throw.");
33 }
34 if (!(actual instanceof errorType))
35 throw new MjsUnitAssertionError(
36 "Expected " + run + "() to throw " + errorType.name +
37 ", but threw '" + actual + "'");
38 if (message !== void 0 && actual.message !== message)
39 throw new MjsUnitAssertionError(
40 "Expected " + run + "() to throw '" + message + "', but threw '" +
41 actual.message + "'");
42 };
43
44 function resolveLater(value) {
45 return new Promise(function(resolve) {
46 Promise.resolve().then(function() {
47 resolve(value);
48 });
49 });
50 }
51
52 function rejectLater(value) {
53 return new Promise(function(resolve, reject) {
54 Promise.resolve().then(function() {
55 reject(value);
56 });
57 });
58 }
59
60 const kNext = 1;
61 const kThrow = 2;
62 const kReturn = 4;
63 const kNextThrows = kNext | 8;
64 const kReturnThrows = kReturn | 16;
65 const kThrowNormal = kThrow | 32;
66 const kNextUnchanged = kNext | 64;
67 const kReturnUnchanged = kReturn | 128;
68 const kThrowUnchanged = kThrow | 256;
69 function sync(array, features, log) {
70 let i = 0;
71
72 // Abort if `log` was not passed, otherwise TypeError may be eaten.
73 if (log === void 0) %AbortJS("`log` is undefined");
neis 2017/02/21 12:48:09 Nit: move this up.
caitp 2017/02/21 14:36:53 Acknowledged.
74 let methods = {
75 next(sent) {
76 let done = i >= array.length;
77 let value = array[i];
78 log.push({ method: "next", sent, value, done });
79 if ((features & kNextThrows) === kNextThrows) throw sent;
80 if ((features & kNextUnchanged) === kNextUnchanged) return sent;
81 i++;
82 return { value, done };
83 },
84 throw(sent) {
85 let done = i >= array.length;
86 log.push({ method: "throw", sent, done });
87 if ((features & kThrowNormal) === kThrowNormal)
88 return { value: sent, done };
89 if ((features & kThrowUnchanged) === kThrowUnchanged) return sent;
90 throw sent;
91 },
92 return(sent) {
93 let done = true;
94 log.push({ method: "return", sent, done });
95 if ((features & kReturnThrows) === kReturnThrows) throw sent;
96 if ((features & kReturnUnchanged) === kReturnUnchanged) return sent;
97 return { value: sent, done };
98 }
99 };
100 return {
101 [Symbol.iterator]() { return this; },
102 next: (features & kNext) ? methods.next : undefined,
103 throw: (features & kThrow) ? methods.throw : undefined,
104 return: (features & kReturn) ? methods.return : undefined
105 };
106 }
107
108 class MyError extends Error {};
109
110 (async function AsyncFromSyncWithGenerator() {
111 function* gen() {
112 yield "sync value";
113 try {
114 yield new Promise(function(resolve) {
115 resolve("async value");
116 });
117 } catch (error) {
118 throw error;
119 }
120 assertUnreachable("generator is closed");
121 }
122 let iter = %CreateAsyncFromSyncIterator(gen());
123
124 // [Async-from-Sync Iterator] wraps sync iterator values in a Promise
125 let promise = iter.next();
126 assertInstanceof(promise, Promise);
127 let iter_result = await promise;
128 assertEquals({ value: "sync value", done: false }, iter_result);
129
130 // [Async-from-Sync Iterator] will wait for resolution of Promise values
131 promise = iter.next();
132 assertInstanceof(promise, Promise);
133 iter_result = await promise;
134 assertEquals({ value: "async value", done: false }, iter_result);
135
136 // [Async-from-Sync Iterator].throw delegates to .throw() method of sync
137 // iterator.
138 promise = iter.throw(new MyError("Error#1"));
139 assertInstanceof(promise, Promise);
140 try {
141 await promise;
142 assertUnreachable("promise should be rejected");
143 } catch (e) {
144 assertInstanceof(e, MyError);
145 assertEquals("Error#1", e.message);
146 }
147
148 promise = iter.return("generator closed");
149 assertInstanceof(promise, Promise);
150 iter_result = await promise;
151 assertEquals({ value: "generator closed", done: true }, iter_result);
152
153 // .next(), .return() and .throw() delegate to sync iterator methods, without
154 // keeping track of the state of the generator.
155 promise = iter.next("unused");
156 assertInstanceof(promise, Promise);
157 iter_result = await promise;
158 assertEquals({ value: undefined, done: true }, iter_result);
159
160 promise = iter.throw(new MyError("Error#2"));
161 assertInstanceof(promise, Promise);
162 try {
163 await promise;
164 } catch (e) {
165 assertInstanceof(e, MyError);
166 assertEquals("Error#2", e.message);
167 }
168
169 promise = iter.return("return-after-completed");
170 assertInstanceof(promise, Promise);
171 iter_result = await promise;
172 assertEquals({ value: "return-after-completed", done: true }, iter_result);
173 })().catch(function(error) {
174 testFailed = true;
175 testFailure = error;
176 });
177
178 %RunMicrotasks();
179 if (testFailed) {
180 throw testFailure;
181 }
182
183
184 (async function AsyncFromSyncOrderOfOperations() {
185 let log = [];
186 iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], 0, log));
187
188 try {
189 await iter.next();
190 assertUnreachable("Iterator.next() method is not optional");
191 } catch (e) {
192 assertInstanceof(e, TypeError);
193 assertEquals([], log);
194 }
195
196 log = [];
197 iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], kNext, log));
198 assertEquals({ value: "sync-value", done: false }, await iter.next("a"));
199 assertEquals([
200 {
201 method: "next",
202 sent: "a",
203 value: "sync-value",
204 done: false
205 }
206 ], log);
207
208 log = [];
209 let asyncValue = resolveLater("async-value");
210 iter = %CreateAsyncFromSyncIterator(sync([asyncValue], kNext, log));
211 assertEquals({ value: "async-value", done: false }, await iter.next("b"));
212 assertEquals([
213 {
214 method: "next",
215 sent: "b",
216 value: asyncValue,
217 done: false
218 }
219 ], log);
220
221 // If [sync_iterator].next() produces a rejected Promise or an exception is
222 // thrown, Promise is rejected with thrown/rejected value.
223 log = [];
224 asyncValue = rejectLater("Boo!");
225 iter = %CreateAsyncFromSyncIterator(sync([asyncValue], kNext, log));
226 try {
227 await iter.next('c');
228 assertUnreachable('Expected `iter.next(\'c\') to throw, but did not throw');
229 } catch (e) {
230 assertEquals("Boo!", e);
231 assertEquals([
232 {
233 method: 'next',
234 sent: 'c',
235 value: asyncValue,
236 done: false
237 }
238 ], log);
239 }
240
241 log = [];
242 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNextThrows, log));
243 try {
244 await iter.next('Boo!');
245 assertUnreachable('Expected `iter.next(\'c\') to throw, but did not throw');
246 } catch (e) {
247 assertEquals("Boo!", e);
248 assertEquals([
249 {
250 method: 'next',
251 sent: 'Boo!',
252 value: 'sync-value',
253 done: false
254 }
255 ], log);
256 }
257
258
259 // [Async-from-Sync Iterator].next() will be rejected with a TypeError if
260 // Type([sync_iterator].next()) is not Object.
261 log = [];
262 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'],
263 kNext|kNextUnchanged, log));
neis 2017/02/21 12:48:09 Nit: kNextUnchanged includes kNext.
caitp 2017/02/21 14:36:52 Acknowledged.
264 try {
265 await iter.next('not-a-JSReceiver');
266 assertUnreachable('Expected `iter.next(\'not-a-JSReceiver\')` to ' +
267 'throw, but did not throw')
268 } catch (e) {
269 assertEquals(e.constructor, TypeError);
270 }
271
272 assertEquals([
273 {
274 method: 'next',
275 sent: 'not-a-JSReceiver',
276 value: 'sync-value',
277 done: false
278 }
279 ], log);
280
281 // If [sync_iterator] does not have a .return() method, return a Promise
282 // resolved with the value `{ value: <<sent value>>, done: true }`.
283 log = [];
284 iter = %CreateAsyncFromSyncIterator(sync(['sync-return'], kNext, log));
285 assertEquals({
286 value: 'd',
287 done: true
288 }, await iter.return('d'));
289
290 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
291 // whether [sync_iterator] is completed or not.
292 assertEquals({
293 value: 'sync-return',
294 done: false
295 }, await iter.next('e'));
296
297 assertEquals([
298 {
299 method: 'next',
300 sent: 'e',
301 value: 'sync-return',
302 done: false
303 }
304 ], log);
305
306 // If [sync_iterator] does have a .return() method, return a Promise
307 // fulfilled with the iterator result of [sync_iterator].return().
308 log = [];
309 iter = %CreateAsyncFromSyncIterator(sync(['sync-return'],
310 kNext|kReturn, log));
311 assertEquals({
312 value: 'f',
313 done: true
314 }, await iter.return('f'));
315
316 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
317 // whether [sync_iterator] is completed or not.
318 assertEquals({
319 value: 'sync-return',
320 done: false
321 }, await iter.next('g'));
322
323 assertEquals([
324 {
325 method: 'return',
326 sent: 'f',
327 done: true
328 },
329 {
330 method: 'next',
331 sent: 'g',
332 value: 'sync-return',
333 done: false
334 }
335 ], log);
336
337 // If [sync_iterator].return() produces a rejected Promise or an exception is
338 // thrown, Promise is rejected with thrown/rejected value.
339 log = [];
340 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kReturnThrows,
341 log));
342 try {
343 await iter.return('Boo!!');
344 assertUnreachable('Expected `iter.return(\'Boo!!\')` to throw, but did ' +
345 'not throw');
346 } catch (e) {
347 assertEquals("Boo!!", e);
348 }
349
350 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
351 // whether [sync_iterator] is completed or not.
352 assertEquals({ value: 'sync-value', done: false }, await iter.next('h'));
353 assertEquals([
354 {
355 method: 'return',
356 sent: 'Boo!!',
357 done: true
358 },
359 {
360 method: 'next',
361 sent: 'h',
362 value: 'sync-value',
363 done: false
364 }
365 ], log);
366
367
368 log = [];
369 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kReturn, log));
370
371 let rejection = Promise.reject('Boo!!');
372 try {
373 await iter.return(rejection);
374 assertUnreachable('Expected `iter.return(Promise.reject(\'Boo!!\'))` to ' +
375 'throw, but did not throw');
376 } catch (e) {
377 assertEquals('Boo!!', e);
378 }
379
380 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
381 // whether [sync_iterator] is completed or not.
382 assertEquals({ value: 'sync-value', done: false }, await iter.next('i'));
383 assertEquals([
384 {
385 method: 'return',
386 sent: rejection,
387 done: true
388 },
389 {
390 method: 'next',
391 sent: 'i',
392 value: 'sync-value',
393 done: false
394 }
395 ], log);
396
397 // [Async-from-Sync Iterator].return() will be rejected with a TypeError if
398 // Type([sync_iterator].return()) is not Object.
399 log = [];
400 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'],
401 kNext|kReturnUnchanged, log));
402 try {
403 await iter.return('not-a-JSReceiver');
404 assertUnreachable('Expected `iter.return(\'not-a-JSReceiver\')` to ' +
405 'throw, but did not throw')
406 } catch (e) {
407 assertEquals(e.constructor, TypeError);
408 }
409
410 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
411 // whether [sync_iterator] is completed or not.
412 assertEquals({ value: 'sync-value', done: false }, await iter.next('j'));
413 assertEquals([
414 {
415 method: 'return',
416 sent: 'not-a-JSReceiver',
417 done: true
418 },
419 {
420 method: 'next',
421 sent: 'j',
422 value: 'sync-value',
423 done: false
424 }
425 ], log);
426
427 // If [sync_iterator] does not have a .throw method, return a Promise rejected
428 // with the sent value.
429 log = [];
430 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext, log));
431 try {
432 await iter.throw('Boo!!');
433 assertUnreachable('Expected iter.throw(\'Boo!!\') to throw, but did not ' +
434 'throw');
435 } catch (e) {
436 assertEquals('Boo!!', e);
437 }
438
439 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
440 // whether [sync_iterator] is completed or not.
441 assertEquals({ value: 'sync-value', done: false }, await iter.next('k'));
442 assertEquals([
443 {
444 method: 'next',
445 sent: 'k',
446 value: 'sync-value',
447 done: false
448 }
449 ], log);
450
451
452 log = [];
453 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kThrow, log));
454 try {
455 await iter.throw('Boo!!');
456 assertUnreachable('Expected iter.throw(\'Boo!!\') to throw, but did not ' +
457 'throw');
458 } catch (e) {
459 assertEquals('Boo!!', e);
460 }
461
462 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
463 // whether [sync_iterator] is completed or not.
464 assertEquals({ value: 'sync-value', done: false }, await iter.next('l'));
465 assertEquals([
466 {
467 method: 'throw',
468 sent: 'Boo!!',
469 done: false
470 },
471 {
472 method: 'next',
473 sent: 'l',
474 value: 'sync-value',
475 done: false
476 }
477 ], log);
478
479 // If [sync_iterator].throw() returns a resolved Promise or a Completion
480 // with [[Type]] "normal" or "return", return a resolved Promise
481 log = [];
482 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kThrowNormal,
483 log));
484 assertEquals({
485 value: 'Boo!!',
486 done: false
487 }, await iter.throw('Boo!!'));
488
489 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
490 // whether [sync_iterator] is completed or not.
491 assertEquals({ value: 'sync-value', done: false }, await iter.next('m'));
492 assertEquals([
493 {
494 method: 'throw',
495 sent: 'Boo!!',
496 done: false
497 },
498 {
499 method: 'next',
500 sent: 'm',
501 value: 'sync-value',
502 done: false
503 }
504 ], log);
505
506 // [Async-from-Sync Iterator].throw() will be rejected with a TypeError if
507 // Type([sync_iterator].throw()) is not Object.
508 log = [];
509 iter = %CreateAsyncFromSyncIterator(sync(['sync-value'],
510 kNext|kThrowUnchanged, log));
511 try {
512 await iter.throw('not-a-JSReceiver');
513 assertUnreachable('Expected `iter.throw(\'not-a-JSReceiver\')` to ' +
514 'throw, but did not throw')
515 } catch (e) {
516 assertEquals(e.constructor, TypeError);
517 }
518
519 // [Async-from-Sync Iterator] merely delegates, and does not keep track of
520 // whether [sync_iterator] is completed or not.
521 assertEquals({ value: 'sync-value', done: false }, await iter.next('n'));
522 assertEquals([
523 {
524 method: 'throw',
525 sent: 'not-a-JSReceiver',
526 done: false
527 },
528 {
529 method: 'next',
530 sent: 'n',
531 value: 'sync-value',
532 done: false
533 }
534 ], log);
535
536 // Let nextValue be IteratorValue(nextResult).
537 // IfAbruptRejectPromise(nextValue, promiseCapability).)
538 iter = %CreateAsyncFromSyncIterator({
539 next() { return { get value() { throw "BadValue!" }, done: false }; }
540 });
541 try {
542 await iter.next();
543 assertUnreachable('Expected `iter.next()` to throw, but did not throw');
544 } catch (e) {
545 assertEquals('BadValue!', e);
546 }
547
548 // Let nextDone be IteratorComplete(nextResult).
549 // IfAbruptRejectPromise(nextDone, promiseCapability).
550 iter = %CreateAsyncFromSyncIterator({
551 next() { return { value: undefined, get done() { throw "BadValue!" } }; }
552 });
553 try {
554 await iter.next();
555 assertUnreachable('Expected `iter.next()` to throw, but did not throw');
556 } catch (e) {
557 assertEquals('BadValue!', e);
558 }
559
560 // IfAbruptRejectPromise(returnResult, promiseCapability).
561 // Let returnValue be IteratorValue(returnResult).
562 iter = %CreateAsyncFromSyncIterator({
563 return() { return { get value() { throw "BadValue!" }, done: false }; }
564 });
565 try {
566 await iter.return();
567 assertUnreachable('Expected `iter.return()` to throw, but did not throw');
568 } catch (e) {
569 assertEquals('BadValue!', e);
570 }
571
572 // IfAbruptRejectPromise(returnValue, promiseCapability).
573 // Let returnDone be IteratorComplete(returnResult).
574 iter = %CreateAsyncFromSyncIterator({
575 return() { return { value: undefined, get done() { throw "BadValue!" } }; }
576 });
577 try {
578 await iter.return();
579 assertUnreachable('Expected `iter.return()` to throw, but did not throw');
580 } catch (e) {
581 assertEquals('BadValue!', e);
582 }
583
584 // IfAbruptRejectPromise(throwResult, promiseCapability).
585 // Let throwValue be IteratorValue(throwResult).
586 iter = %CreateAsyncFromSyncIterator({
587 throw() { return { get value() { throw "BadValue!" }, done: false }; }
588 });
589 try {
590 await iter.throw();
591 assertUnreachable('Expected `iter.throw()` to throw, but did not throw');
592 } catch (e) {
593 assertEquals('BadValue!', e);
594 }
595
596 // IfAbruptRejectPromise(throwValue, promiseCapability).
597 // Let throwDone be IteratorComplete(throwResult).
598 iter = %CreateAsyncFromSyncIterator({
599 throw() { return { value: undefined, get done() { throw "BadValue!" } }; }
600 });
601 try {
602 await iter.throw();
603 assertUnreachable('Expected `iter.throw()` to throw, but did not throw');
604 } catch (e) {
605 assertEquals('BadValue!', e);
606 }
607 })().catch(function(error) {
608 testFailed = true;
609 testFailure = error;
610 });
611
612 %RunMicrotasks();
613 if (testFailed) {
614 throw testFailure;
615 }
616
617 (function ExtractedAsyncFromSyncIteratorMethods() {
618 // Async-from-Sync iterator methods can be extracted via function.caller.
619 // TODO(caitp): test extracted `throw` method using yield* in async generator.
620 let extractor = [0, 1, 2, 3, 4,5,6,7,8,9];
621 let extractedNext;
622 let extractedReturn;
623
624 extractor[Symbol.iterator] = function() {
625 let it = [][Symbol.iterator].call(extractor);
626 let origNext = it.next, origThrow = it.throw, origReturn = it.return;
627 function extractNext() {
628 extractedNext = extractNext.caller;
629 return origNext;
630 }
631 function extractReturn() {
632 extractedReturn = extractReturn.caller;
633 return origReturn;
634 }
635 Object.defineProperties(it, {
636 "next": { get: extractNext, configurable: true },
637 "return": { get: extractReturn, configurable: true }
638 });
639 return it;
640 };
641
642 async function f() {
643 let i;
644 let it = extractor[Symbol.iterator]();
645 for await (let x of it) break;
646 for await (let x of it) return "x";
647 }
648
649 // Cycle through `f` to extract iterator methods
650 f().catch(function() { assertUnreachable("No error should have occurred"); });
neis 2017/02/21 12:48:09 This assertUnreachable would go unnoticed.
caitp 2017/02/21 14:36:52 Good point, using %AbortJS instead.
651 %RunMicrotasks();
652
653 assertEquals(typeof extractedNext, "function");
654 assertThrowsAsync(() => extractedNext.call(undefined), TypeError);
655 assertThrowsAsync(() => extractedNext.call(1), TypeError);
656
657 assertEquals(typeof extractedReturn, "function");
658 assertThrowsAsync(() => extractedReturn.call(undefined), TypeError);
659 assertThrowsAsync(() => extractedReturn.call(1), TypeError);
660 })();
OLDNEW
« src/builtins/builtins-async-iterator.cc ('K') | « 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