 Chromium Code Reviews
 Chromium Code Reviews Issue 2317383002:
  Async/await Promise dependency graph  (Closed)
    
  
    Issue 2317383002:
  Async/await Promise dependency graph  (Closed) 
  | 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 |