Chromium Code Reviews| 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 |