OLD | NEW |
---|---|
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 (function(global, utils, extrasUtils) { | 5 (function(global, utils, extrasUtils) { |
6 | 6 |
7 "use strict"; | 7 "use strict"; |
8 | 8 |
9 %CheckIsBootstrapping(); | 9 %CheckIsBootstrapping(); |
10 | 10 |
11 // ------------------------------------------------------------------- | 11 // ------------------------------------------------------------------- |
12 // Imports | 12 // Imports |
13 | 13 |
14 var InternalArray = utils.InternalArray; | 14 var InternalArray = utils.InternalArray; |
15 var promiseAwaitHandlerSymbol = | 15 var promiseHandledBySymbol = |
16 utils.ImportNow("promise_await_handler_symbol"); | 16 utils.ImportNow("promise_handled_by_symbol"); |
17 var promiseCombinedDeferredSymbol = | 17 var promiseForwardingHandlerSymbol = |
18 utils.ImportNow("promise_combined_deferred_symbol"); | 18 utils.ImportNow("promise_forwarding_handler_symbol"); |
19 var promiseHasHandlerSymbol = | 19 var promiseHasHandlerSymbol = |
20 utils.ImportNow("promise_has_handler_symbol"); | 20 utils.ImportNow("promise_has_handler_symbol"); |
21 var promiseRejectReactionsSymbol = | 21 var promiseRejectReactionsSymbol = |
22 utils.ImportNow("promise_reject_reactions_symbol"); | 22 utils.ImportNow("promise_reject_reactions_symbol"); |
23 var promiseFulfillReactionsSymbol = | 23 var promiseFulfillReactionsSymbol = |
24 utils.ImportNow("promise_fulfill_reactions_symbol"); | 24 utils.ImportNow("promise_fulfill_reactions_symbol"); |
25 var promiseDeferredReactionsSymbol = | 25 var promiseDeferredReactionsSymbol = |
26 utils.ImportNow("promise_deferred_reactions_symbol"); | 26 utils.ImportNow("promise_deferred_reactions_symbol"); |
27 var promiseHandledHintSymbol = | 27 var promiseHandledHintSymbol = |
28 utils.ImportNow("promise_handled_hint_symbol"); | 28 utils.ImportNow("promise_handled_hint_symbol"); |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
215 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, resolveCallbacks); | 215 SET_PRIVATE(promise, promiseFulfillReactionsSymbol, resolveCallbacks); |
216 SET_PRIVATE(promise, promiseRejectReactionsSymbol, rejectCallbacks); | 216 SET_PRIVATE(promise, promiseRejectReactionsSymbol, rejectCallbacks); |
217 } else { | 217 } else { |
218 maybeResolveCallbacks.push(onResolve, deferred); | 218 maybeResolveCallbacks.push(onResolve, deferred); |
219 GET_PRIVATE(promise, promiseRejectReactionsSymbol).push(onReject, deferred); | 219 GET_PRIVATE(promise, promiseRejectReactionsSymbol).push(onReject, deferred); |
220 } | 220 } |
221 } | 221 } |
222 | 222 |
223 function PromiseIdResolveHandler(x) { return x; } | 223 function PromiseIdResolveHandler(x) { return x; } |
224 function PromiseIdRejectHandler(r) { %_ReThrow(r); } | 224 function PromiseIdRejectHandler(r) { %_ReThrow(r); } |
225 SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true); | |
225 | 226 |
226 function PromiseNopResolver() {} | 227 function PromiseNopResolver() {} |
227 | 228 |
228 // ------------------------------------------------------------------- | 229 // ------------------------------------------------------------------- |
229 // Define exported functions. | 230 // Define exported functions. |
230 | 231 |
231 // For bootstrapper. | 232 // For bootstrapper. |
232 | 233 |
233 // ES#sec-ispromise IsPromise ( x ) | 234 // ES#sec-ispromise IsPromise ( x ) |
234 function IsPromise(x) { | 235 function IsPromise(x) { |
235 return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStateSymbol); | 236 return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStateSymbol); |
236 } | 237 } |
237 | 238 |
238 function PromiseCreate() { | 239 function PromiseCreate() { |
239 return new GlobalPromise(PromiseNopResolver) | 240 return new GlobalPromise(PromiseNopResolver); |
240 } | 241 } |
241 | 242 |
242 // ES#sec-promise-resolve-functions | 243 // ES#sec-promise-resolve-functions |
243 // Promise Resolve Functions, steps 6-13 | 244 // Promise Resolve Functions, steps 6-13 |
244 function ResolvePromise(promise, resolution) { | 245 function ResolvePromise(promise, resolution) { |
245 if (resolution === promise) { | 246 if (resolution === promise) { |
246 return RejectPromise(promise, | 247 return RejectPromise(promise, |
247 %make_type_error(kPromiseCyclic, resolution), | 248 %make_type_error(kPromiseCyclic, resolution), |
248 true); | 249 true); |
249 } | 250 } |
(...skipping 30 matching lines...) Expand all Loading... | |
280 SET_PRIVATE(resolution, promiseHasHandlerSymbol, true); | 281 SET_PRIVATE(resolution, promiseHasHandlerSymbol, true); |
281 return; | 282 return; |
282 } | 283 } |
283 } | 284 } |
284 | 285 |
285 if (IS_CALLABLE(then)) { | 286 if (IS_CALLABLE(then)) { |
286 // PromiseResolveThenableJob | 287 // PromiseResolveThenableJob |
287 var id; | 288 var id; |
288 var name = "PromiseResolveThenableJob"; | 289 var name = "PromiseResolveThenableJob"; |
289 var instrumenting = DEBUG_IS_ACTIVE; | 290 var instrumenting = DEBUG_IS_ACTIVE; |
291 if (instrumenting && IsPromise(resolution)) { | |
292 // Mark the dependency of the new promise on the resolution | |
293 SET_PRIVATE(resolution, promiseHandledBySymbol, promise); | |
294 } | |
290 %EnqueueMicrotask(function() { | 295 %EnqueueMicrotask(function() { |
291 if (instrumenting) { | 296 if (instrumenting) { |
292 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); | 297 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
293 } | 298 } |
294 // These resolving functions simply forward the exception, so | 299 // These resolving functions simply forward the exception, so |
295 // don't create a new debugEvent. | 300 // don't create a new debugEvent. |
296 var callbacks = CreateResolvingFunctions(promise, false); | 301 var callbacks = CreateResolvingFunctions(promise, false); |
297 try { | 302 try { |
298 %_Call(then, resolution, callbacks.resolve, callbacks.reject); | 303 %_Call(then, resolution, callbacks.resolve, callbacks.reject); |
299 } catch (e) { | 304 } catch (e) { |
300 %_Call(callbacks.reject, UNDEFINED, e); | 305 %_Call(callbacks.reject, UNDEFINED, e); |
301 } | 306 } |
302 if (instrumenting) { | 307 if (instrumenting) { |
303 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); | 308 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
304 } | 309 } |
305 }); | 310 }); |
306 if (instrumenting) { | 311 if (instrumenting) { |
307 id = ++lastMicrotaskId; | 312 id = ++lastMicrotaskId; |
308 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); | 313 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
309 } | 314 } |
310 return; | 315 return; |
311 } | 316 } |
312 } | 317 } |
313 FulfillPromise(promise, kFulfilled, resolution, promiseFulfillReactionsSymbol) ; | 318 FulfillPromise(promise, kFulfilled, resolution, |
319 promiseFulfillReactionsSymbol); | |
314 } | 320 } |
315 | 321 |
316 // ES#sec-rejectpromise | 322 // ES#sec-rejectpromise |
317 // RejectPromise ( promise, reason ) | 323 // RejectPromise ( promise, reason ) |
318 function RejectPromise(promise, reason, debugEvent) { | 324 function RejectPromise(promise, reason, debugEvent) { |
319 // Check promise status to confirm that this reject has an effect. | 325 // Check promise status to confirm that this reject has an effect. |
320 // Call runtime for callbacks to the debugger or for unhandled reject. | 326 // Call runtime for callbacks to the debugger or for unhandled reject. |
321 // The debugEvent parameter sets whether a debug ExceptionEvent should | 327 // The debugEvent parameter sets whether a debug ExceptionEvent should |
322 // be triggered. It should be set to false when forwarding a rejection | 328 // be triggered. It should be set to false when forwarding a rejection |
323 // rather than creating a new one. | 329 // rather than creating a new one. |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
465 if (!IS_RECEIVER(this)) { | 471 if (!IS_RECEIVER(this)) { |
466 throw %make_type_error(kCalledOnNonObject, "Promise.all"); | 472 throw %make_type_error(kCalledOnNonObject, "Promise.all"); |
467 } | 473 } |
468 | 474 |
469 // false debugEvent so that forwarding the rejection through all does not | 475 // false debugEvent so that forwarding the rejection through all does not |
470 // trigger redundant ExceptionEvents | 476 // trigger redundant ExceptionEvents |
471 var deferred = NewPromiseCapability(this, false); | 477 var deferred = NewPromiseCapability(this, false); |
472 var resolutions = new InternalArray(); | 478 var resolutions = new InternalArray(); |
473 var count; | 479 var count; |
474 | 480 |
481 // For catch prediction, don't treat the .then calls as handling it; | |
482 // instead, recurse outwards. | |
483 SET_PRIVATE(deferred.reject, promiseForwardingHandlerSymbol, true); | |
484 | |
475 function CreateResolveElementFunction(index, values, promiseCapability) { | 485 function CreateResolveElementFunction(index, values, promiseCapability) { |
476 var alreadyCalled = false; | 486 var alreadyCalled = false; |
477 return (x) => { | 487 return (x) => { |
478 if (alreadyCalled === true) return; | 488 if (alreadyCalled === true) return; |
479 alreadyCalled = true; | 489 alreadyCalled = true; |
480 values[index] = x; | 490 values[index] = x; |
481 if (--count === 0) { | 491 if (--count === 0) { |
482 var valuesArray = []; | 492 var valuesArray = []; |
483 %MoveArrayContents(values, valuesArray); | 493 %MoveArrayContents(values, valuesArray); |
484 %_Call(promiseCapability.resolve, UNDEFINED, valuesArray); | 494 %_Call(promiseCapability.resolve, UNDEFINED, valuesArray); |
485 } | 495 } |
486 }; | 496 }; |
487 } | 497 } |
488 | 498 |
489 try { | 499 try { |
490 var i = 0; | 500 var i = 0; |
491 count = 1; | 501 count = 1; |
492 for (var value of iterable) { | 502 for (var value of iterable) { |
493 var nextPromise = this.resolve(value); | 503 var nextPromise = this.resolve(value); |
494 ++count; | 504 ++count; |
495 nextPromise.then( | 505 let throwawayPromise = nextPromise.then( |
adamk
2016/09/17 00:32:40
This is the first "let" in this file (or anywhere
Dan Ehrenberg
2016/09/17 00:39:31
Fixed
| |
496 CreateResolveElementFunction(i, resolutions, deferred), | 506 CreateResolveElementFunction(i, resolutions, deferred), |
497 deferred.reject); | 507 deferred.reject); |
498 SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred); | 508 // For catch prediction, mark that rejections here are semantically |
509 // handled by the combined Promise. | |
510 if (IsPromise(throwawayPromise)) { | |
511 SET_PRIVATE(throwawayPromise, promiseHandledBySymbol, deferred.promise); | |
512 } | |
499 ++i; | 513 ++i; |
500 } | 514 } |
501 | 515 |
502 // 6.d | 516 // 6.d |
503 if (--count === 0) { | 517 if (--count === 0) { |
504 var valuesArray = []; | 518 var valuesArray = []; |
505 %MoveArrayContents(resolutions, valuesArray); | 519 %MoveArrayContents(resolutions, valuesArray); |
506 %_Call(deferred.resolve, UNDEFINED, valuesArray); | 520 %_Call(deferred.resolve, UNDEFINED, valuesArray); |
507 } | 521 } |
508 | 522 |
509 } catch (e) { | 523 } catch (e) { |
510 %_Call(deferred.reject, UNDEFINED, e); | 524 %_Call(deferred.reject, UNDEFINED, e); |
511 } | 525 } |
512 return deferred.promise; | 526 return deferred.promise; |
513 } | 527 } |
514 | 528 |
515 // ES#sec-promise.race | 529 // ES#sec-promise.race |
516 // Promise.race ( iterable ) | 530 // Promise.race ( iterable ) |
517 function PromiseRace(iterable) { | 531 function PromiseRace(iterable) { |
518 if (!IS_RECEIVER(this)) { | 532 if (!IS_RECEIVER(this)) { |
519 throw %make_type_error(kCalledOnNonObject, PromiseRace); | 533 throw %make_type_error(kCalledOnNonObject, PromiseRace); |
520 } | 534 } |
521 | 535 |
522 // false debugEvent so that forwarding the rejection through race does not | 536 // false debugEvent so that forwarding the rejection through race does not |
523 // trigger redundant ExceptionEvents | 537 // trigger redundant ExceptionEvents |
524 var deferred = NewPromiseCapability(this, false); | 538 var deferred = NewPromiseCapability(this, false); |
539 | |
540 // For catch prediction, don't treat the .then calls as handling it; | |
541 // instead, recurse outwards. | |
542 SET_PRIVATE(deferred.reject, promiseForwardingHandlerSymbol, true); | |
543 | |
525 try { | 544 try { |
526 for (var value of iterable) { | 545 for (var value of iterable) { |
527 this.resolve(value).then(deferred.resolve, deferred.reject); | 546 var throwawayPromise = this.resolve(value).then(deferred.resolve, |
528 SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred); | 547 deferred.reject); |
548 // For catch prediction, mark that rejections here are semantically | |
549 // handled by the combined Promise. | |
550 if (IsPromise(throwawayPromise)) { | |
551 SET_PRIVATE(throwawayPromise, promiseHandledBySymbol, deferred.promise); | |
552 } | |
529 } | 553 } |
530 } catch (e) { | 554 } catch (e) { |
531 deferred.reject(e) | 555 deferred.reject(e) |
532 } | 556 } |
533 return deferred.promise; | 557 return deferred.promise; |
534 } | 558 } |
535 | 559 |
536 | 560 |
537 // Utility for debugger | 561 // Utility for debugger |
538 | 562 |
539 function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) { | 563 function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) { |
540 // If this handler was installed by async/await, it does not indicate | 564 // Recurse to the forwarding Promise, if any. This may be due to |
541 // that there is a user-defined reject handler. | 565 // - await reaction forwarding to the throwaway Promise, which has |
542 if (GET_PRIVATE(handler, promiseAwaitHandlerSymbol)) return false; | 566 // a dependency edge to the outer Promise. |
543 if (handler !== PromiseIdRejectHandler) { | 567 // - PromiseIdResolveHandler forwarding to the output of .then |
544 var combinedDeferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol); | 568 // - Promise.all/Promise.race forwarding to a throwaway Promise, which |
545 if (IS_UNDEFINED(combinedDeferred)) return true; | 569 // has a dependency edge to the generated outer Promise. |
546 if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise)) { | 570 if (GET_PRIVATE(handler, promiseForwardingHandlerSymbol)) { |
547 return true; | 571 return PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise); |
548 } | |
549 } else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) { | |
550 return true; | |
551 } | 572 } |
552 return false; | 573 |
574 // Otherwise, this is a real reject handler for the Promise | |
575 return true; | |
553 } | 576 } |
554 | 577 |
555 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 578 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
556 // If this promise was marked as being handled by a catch block | 579 // If this promise was marked as being handled by a catch block |
557 // in an async function, then it has a user-defined reject handler. | 580 // in an async function, then it has a user-defined reject handler. |
558 if (GET_PRIVATE(promise, promiseHandledHintSymbol)) return true; | 581 if (GET_PRIVATE(promise, promiseHandledHintSymbol)) return true; |
559 | 582 |
583 // If this Promise is subsumed by another Promise (a Promise resolved | |
584 // with another Promise, or an intermediate, hidden, throwaway Promise | |
585 // within async/await), then recurse on the outer Promise. | |
586 // In this case, the dependency is one possible way that the Promise | |
587 // could be resolved, so it does not subsume the other following cases. | |
588 var outerPromise = GET_PRIVATE(promise, promiseHandledBySymbol); | |
589 if (outerPromise && | |
590 PromiseHasUserDefinedRejectHandlerRecursive(outerPromise)) { | |
591 return true; | |
592 } | |
593 | |
560 var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol); | 594 var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol); |
561 var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); | 595 var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); |
562 | 596 |
563 if (IS_UNDEFINED(queue)) return false; | 597 if (IS_UNDEFINED(queue)) return false; |
564 | 598 |
565 if (!IS_ARRAY(queue)) { | 599 if (!IS_ARRAY(queue)) { |
566 return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds); | 600 return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds); |
567 } | 601 } |
568 | 602 |
569 for (var i = 0; i < queue.length; i += 2) { | 603 for (var i = 0; i < queue.length; i += 2) { |
570 if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], queue[i + 1])) { | 604 if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], queue[i + 1])) { |
571 return true; | 605 return true; |
572 } | 606 } |
573 } | 607 } |
574 return false; | 608 return false; |
575 } | 609 } |
576 | 610 |
577 // Return whether the promise will be handled by a user-defined reject | 611 // Return whether the promise will be handled by a user-defined reject |
578 // handler somewhere down the promise chain. For this, we do a depth-first | 612 // handler somewhere down the promise chain. For this, we do a depth-first |
579 // search for a reject handler that's not the default PromiseIdRejectHandler. | 613 // search for a reject handler that's not the default PromiseIdRejectHandler. |
614 // This function also traverses dependencies of one Promise on another, | |
615 // set up through async/await and Promises resolved with Promises. | |
580 function PromiseHasUserDefinedRejectHandler() { | 616 function PromiseHasUserDefinedRejectHandler() { |
581 return PromiseHasUserDefinedRejectHandlerRecursive(this); | 617 return PromiseHasUserDefinedRejectHandlerRecursive(this); |
582 }; | 618 }; |
583 | 619 |
584 | 620 |
585 function PromiseSpecies() { | 621 function PromiseSpecies() { |
586 return this; | 622 return this; |
587 } | 623 } |
588 | 624 |
589 // ------------------------------------------------------------------- | 625 // ------------------------------------------------------------------- |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
631 to.PromiseThen = PromiseThen; | 667 to.PromiseThen = PromiseThen; |
632 | 668 |
633 to.GlobalPromise = GlobalPromise; | 669 to.GlobalPromise = GlobalPromise; |
634 to.NewPromiseCapability = NewPromiseCapability; | 670 to.NewPromiseCapability = NewPromiseCapability; |
635 to.PerformPromiseThen = PerformPromiseThen; | 671 to.PerformPromiseThen = PerformPromiseThen; |
636 to.ResolvePromise = ResolvePromise; | 672 to.ResolvePromise = ResolvePromise; |
637 to.RejectPromise = RejectPromise; | 673 to.RejectPromise = RejectPromise; |
638 }); | 674 }); |
639 | 675 |
640 }) | 676 }) |
OLD | NEW |