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 GlobalSet = global.Set; | |
15 var promiseAwaitHandlerSymbol = | 16 var promiseAwaitHandlerSymbol = |
16 utils.ImportNow("promise_await_handler_symbol"); | 17 utils.ImportNow("promise_await_handler_symbol"); |
17 var promiseCombinedDeferredSymbol = | 18 var promiseCombinedDeferredSymbol = |
18 utils.ImportNow("promise_combined_deferred_symbol"); | 19 utils.ImportNow("promise_combined_deferred_symbol"); |
19 var promiseHasHandlerSymbol = | 20 var promiseHasHandlerSymbol = |
20 utils.ImportNow("promise_has_handler_symbol"); | 21 utils.ImportNow("promise_has_handler_symbol"); |
21 var promiseRejectReactionsSymbol = | 22 var promiseRejectReactionsSymbol = |
22 utils.ImportNow("promise_reject_reactions_symbol"); | 23 utils.ImportNow("promise_reject_reactions_symbol"); |
23 var promiseFulfillReactionsSymbol = | 24 var promiseFulfillReactionsSymbol = |
24 utils.ImportNow("promise_fulfill_reactions_symbol"); | 25 utils.ImportNow("promise_fulfill_reactions_symbol"); |
25 var promiseDeferredReactionsSymbol = | 26 var promiseDeferredReactionsSymbol = |
26 utils.ImportNow("promise_deferred_reactions_symbol"); | 27 utils.ImportNow("promise_deferred_reactions_symbol"); |
27 var promiseHandledHintSymbol = | 28 var promiseHandledHintSymbol = |
28 utils.ImportNow("promise_handled_hint_symbol"); | 29 utils.ImportNow("promise_handled_hint_symbol"); |
29 var promiseRawSymbol = utils.ImportNow("promise_raw_symbol"); | 30 var promiseRawSymbol = utils.ImportNow("promise_raw_symbol"); |
30 var promiseStateSymbol = utils.ImportNow("promise_state_symbol"); | 31 var promiseStateSymbol = utils.ImportNow("promise_state_symbol"); |
31 var promiseResultSymbol = utils.ImportNow("promise_result_symbol"); | 32 var promiseResultSymbol = utils.ImportNow("promise_result_symbol"); |
33 var SetAdd; | |
34 var SetHas; | |
32 var SpeciesConstructor; | 35 var SpeciesConstructor; |
33 var speciesSymbol = utils.ImportNow("species_symbol"); | 36 var speciesSymbol = utils.ImportNow("species_symbol"); |
34 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); | 37 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); |
35 | 38 |
36 utils.Import(function(from) { | 39 utils.Import(function(from) { |
37 SpeciesConstructor = from.SpeciesConstructor; | 40 SpeciesConstructor = from.SpeciesConstructor; |
41 SetAdd = from.SetAdd; | |
adamk
2016/09/15 22:39:40
Nit: sort
Dan Ehrenberg
2016/09/17 00:04:31
Done
| |
42 SetHas = from.SetHas; | |
38 }); | 43 }); |
39 | 44 |
40 // ------------------------------------------------------------------- | 45 // ------------------------------------------------------------------- |
41 | 46 |
42 // [[PromiseState]] values: | 47 // [[PromiseState]] values: |
43 const kPending = 0; | 48 const kPending = 0; |
44 const kFulfilled = +1; | 49 const kFulfilled = +1; |
45 const kRejected = -1; | 50 const kRejected = -1; |
46 | 51 |
47 var lastMicrotaskId = 0; | 52 var lastMicrotaskId = 0; |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
229 // Define exported functions. | 234 // Define exported functions. |
230 | 235 |
231 // For bootstrapper. | 236 // For bootstrapper. |
232 | 237 |
233 // ES#sec-ispromise IsPromise ( x ) | 238 // ES#sec-ispromise IsPromise ( x ) |
234 function IsPromise(x) { | 239 function IsPromise(x) { |
235 return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStateSymbol); | 240 return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStateSymbol); |
236 } | 241 } |
237 | 242 |
238 function PromiseCreate() { | 243 function PromiseCreate() { |
239 return new GlobalPromise(PromiseNopResolver) | 244 return new GlobalPromise(PromiseNopResolver); |
240 } | 245 } |
241 | 246 |
242 // ES#sec-promise-resolve-functions | 247 // ES#sec-promise-resolve-functions |
243 // Promise Resolve Functions, steps 6-13 | 248 // Promise Resolve Functions, steps 6-13 |
244 function ResolvePromise(promise, resolution) { | 249 function ResolvePromise(promise, resolution) { |
245 if (resolution === promise) { | 250 if (resolution === promise) { |
246 return RejectPromise(promise, | 251 return RejectPromise(promise, |
247 %make_type_error(kPromiseCyclic, resolution), | 252 %make_type_error(kPromiseCyclic, resolution), |
248 true); | 253 true); |
249 } | 254 } |
(...skipping 30 matching lines...) Expand all Loading... | |
280 SET_PRIVATE(resolution, promiseHasHandlerSymbol, true); | 285 SET_PRIVATE(resolution, promiseHasHandlerSymbol, true); |
281 return; | 286 return; |
282 } | 287 } |
283 } | 288 } |
284 | 289 |
285 if (IS_CALLABLE(then)) { | 290 if (IS_CALLABLE(then)) { |
286 // PromiseResolveThenableJob | 291 // PromiseResolveThenableJob |
287 var id; | 292 var id; |
288 var name = "PromiseResolveThenableJob"; | 293 var name = "PromiseResolveThenableJob"; |
289 var instrumenting = DEBUG_IS_ACTIVE; | 294 var instrumenting = DEBUG_IS_ACTIVE; |
295 if (instrumenting && !IS_UNDEFINED(resolution) && | |
adamk
2016/09/15 22:39:40
No need to check !IS_UNDEFINED if you're checking
Dan Ehrenberg
2016/09/17 00:04:31
Done
| |
296 IsPromise(resolution)) { | |
297 // Mark the dependency of the new promise on the resolution | |
298 SET_PRIVATE(resolution, promiseAwaitHandlerSymbol, promise); | |
299 } | |
290 %EnqueueMicrotask(function() { | 300 %EnqueueMicrotask(function() { |
291 if (instrumenting) { | 301 if (instrumenting) { |
292 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); | 302 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
293 } | 303 } |
294 // These resolving functions simply forward the exception, so | 304 // These resolving functions simply forward the exception, so |
295 // don't create a new debugEvent. | 305 // don't create a new debugEvent. |
296 var callbacks = CreateResolvingFunctions(promise, false); | 306 var callbacks = CreateResolvingFunctions(promise, false); |
297 try { | 307 try { |
298 %_Call(then, resolution, callbacks.resolve, callbacks.reject); | 308 %_Call(then, resolution, callbacks.resolve, callbacks.reject); |
299 } catch (e) { | 309 } catch (e) { |
300 %_Call(callbacks.reject, UNDEFINED, e); | 310 %_Call(callbacks.reject, UNDEFINED, e); |
301 } | 311 } |
302 if (instrumenting) { | 312 if (instrumenting) { |
303 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); | 313 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
304 } | 314 } |
305 }); | 315 }); |
306 if (instrumenting) { | 316 if (instrumenting) { |
307 id = ++lastMicrotaskId; | 317 id = ++lastMicrotaskId; |
308 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); | 318 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
309 } | 319 } |
310 return; | 320 return; |
311 } | 321 } |
312 } | 322 } |
313 FulfillPromise(promise, kFulfilled, resolution, promiseFulfillReactionsSymbol) ; | 323 FulfillPromise(promise, kFulfilled, resolution, |
324 promiseFulfillReactionsSymbol); | |
314 } | 325 } |
315 | 326 |
316 // ES#sec-rejectpromise | 327 // ES#sec-rejectpromise |
317 // RejectPromise ( promise, reason ) | 328 // RejectPromise ( promise, reason ) |
318 function RejectPromise(promise, reason, debugEvent) { | 329 function RejectPromise(promise, reason, debugEvent) { |
319 // Check promise status to confirm that this reject has an effect. | 330 // Check promise status to confirm that this reject has an effect. |
320 // Call runtime for callbacks to the debugger or for unhandled reject. | 331 // Call runtime for callbacks to the debugger or for unhandled reject. |
321 // The debugEvent parameter sets whether a debug ExceptionEvent should | 332 // The debugEvent parameter sets whether a debug ExceptionEvent should |
322 // be triggered. It should be set to false when forwarding a rejection | 333 // be triggered. It should be set to false when forwarding a rejection |
323 // rather than creating a new one. | 334 // rather than creating a new one. |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
529 } | 540 } |
530 } catch (e) { | 541 } catch (e) { |
531 deferred.reject(e) | 542 deferred.reject(e) |
532 } | 543 } |
533 return deferred.promise; | 544 return deferred.promise; |
534 } | 545 } |
535 | 546 |
536 | 547 |
537 // Utility for debugger | 548 // Utility for debugger |
538 | 549 |
539 function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) { | 550 function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred, visited) { |
540 // If this handler was installed by async/await, it does not indicate | 551 // If this handler was installed by a locally uncaught await, recurse |
541 // that there is a user-defined reject handler. | 552 // up to the outer Promise returned by that async function. |
542 if (GET_PRIVATE(handler, promiseAwaitHandlerSymbol)) return false; | 553 // In this case, the dependency subsumes any other things attached to the |
554 // handler, as the dependency is only present due to async/await and is not | |
555 // a real catch handler. | |
556 var outerPromise = GET_PRIVATE(handler, promiseAwaitHandlerSymbol); | |
557 if (outerPromise) { | |
558 return PromiseHasUserDefinedRejectHandlerRecursive(outerPromise, visited); | |
559 } | |
543 if (handler !== PromiseIdRejectHandler) { | 560 if (handler !== PromiseIdRejectHandler) { |
544 var combinedDeferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol); | 561 var combinedDeferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol); |
545 if (IS_UNDEFINED(combinedDeferred)) return true; | 562 if (IS_UNDEFINED(combinedDeferred)) return true; |
546 if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise)) { | 563 if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise, |
564 visited)) { | |
547 return true; | 565 return true; |
548 } | 566 } |
549 } else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) { | 567 } else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise, |
568 visited)) { | |
550 return true; | 569 return true; |
551 } | 570 } |
552 return false; | 571 return false; |
553 } | 572 } |
554 | 573 |
555 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 574 function PromiseHasUserDefinedRejectHandlerRecursive(promise, visited) { |
575 // Avoid visiting the Promise multiple times in case there is a cycle | |
576 // in the dependency graph. | |
577 if (%_Call(SetHas, visited, promise)) return false; | |
adamk
2016/09/15 22:39:40
My brain is having trouble groking all the tests;
| |
578 %_Call(SetAdd, visited, promise); | |
579 | |
556 // If this promise was marked as being handled by a catch block | 580 // 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. | 581 // in an async function, then it has a user-defined reject handler. |
558 if (GET_PRIVATE(promise, promiseHandledHintSymbol)) return true; | 582 if (GET_PRIVATE(promise, promiseHandledHintSymbol)) return true; |
559 | 583 |
584 // If this Promise is subsumed by another Promise (a Promise resolved | |
585 // with another Promise, or an intermediate, hidden, throwaway Promise | |
586 // within async/await), then recurse on the outer Promise. | |
587 // In this case, the dependency is one possible way that the Promise | |
588 // could be resolved, so it does not subsume the other following cases. | |
589 var outerPromise = GET_PRIVATE(promise, promiseAwaitHandlerSymbol); | |
590 if (outerPromise && | |
591 PromiseHasUserDefinedRejectHandlerRecursive(outerPromise, visited)) { | |
592 return true; | |
593 } | |
594 | |
560 var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol); | 595 var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol); |
561 var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); | 596 var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); |
562 | 597 |
563 if (IS_UNDEFINED(queue)) return false; | 598 if (IS_UNDEFINED(queue)) return false; |
564 | 599 |
565 if (!IS_ARRAY(queue)) { | 600 if (!IS_ARRAY(queue)) { |
566 return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds); | 601 return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds, visited); |
567 } | 602 } |
568 | 603 |
569 for (var i = 0; i < queue.length; i += 2) { | 604 for (var i = 0; i < queue.length; i += 2) { |
570 if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], queue[i + 1])) { | 605 if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], |
606 queue[i + 1], | |
607 visited)) { | |
571 return true; | 608 return true; |
572 } | 609 } |
573 } | 610 } |
574 return false; | 611 return false; |
575 } | 612 } |
576 | 613 |
577 // Return whether the promise will be handled by a user-defined reject | 614 // 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 | 615 // 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. | 616 // search for a reject handler that's not the default PromiseIdRejectHandler. |
617 // This function also traverses dependencies of one Promise on another, | |
618 // set up through async/await and Promises resolved with Promises. The graph | |
619 // may contain cycles, so a set of visited Promises is maintained. | |
580 function PromiseHasUserDefinedRejectHandler() { | 620 function PromiseHasUserDefinedRejectHandler() { |
581 return PromiseHasUserDefinedRejectHandlerRecursive(this); | 621 return PromiseHasUserDefinedRejectHandlerRecursive(this, new GlobalSet()); |
582 }; | 622 }; |
583 | 623 |
584 | 624 |
585 function PromiseSpecies() { | 625 function PromiseSpecies() { |
586 return this; | 626 return this; |
587 } | 627 } |
588 | 628 |
589 // ------------------------------------------------------------------- | 629 // ------------------------------------------------------------------- |
590 // Install exported functions. | 630 // Install exported functions. |
591 | 631 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
631 to.PromiseThen = PromiseThen; | 671 to.PromiseThen = PromiseThen; |
632 | 672 |
633 to.GlobalPromise = GlobalPromise; | 673 to.GlobalPromise = GlobalPromise; |
634 to.NewPromiseCapability = NewPromiseCapability; | 674 to.NewPromiseCapability = NewPromiseCapability; |
635 to.PerformPromiseThen = PerformPromiseThen; | 675 to.PerformPromiseThen = PerformPromiseThen; |
636 to.ResolvePromise = ResolvePromise; | 676 to.ResolvePromise = ResolvePromise; |
637 to.RejectPromise = RejectPromise; | 677 to.RejectPromise = RejectPromise; |
638 }); | 678 }); |
639 | 679 |
640 }) | 680 }) |
OLD | NEW |