| Index: src/js/promise.js
|
| diff --git a/src/js/promise.js b/src/js/promise.js
|
| index e020028c244f8f0d45bf429316ae916757012de5..1a626c2c55ff9317b6ff2b25b58b565ffe347888 100644
|
| --- a/src/js/promise.js
|
| +++ b/src/js/promise.js
|
| @@ -12,6 +12,7 @@
|
| // Imports
|
|
|
| var InternalArray = utils.InternalArray;
|
| +var GlobalSet = global.Set;
|
| var promiseAwaitHandlerSymbol =
|
| utils.ImportNow("promise_await_handler_symbol");
|
| var promiseCombinedDeferredSymbol =
|
| @@ -29,12 +30,16 @@ var promiseHandledHintSymbol =
|
| var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
|
| var promiseStateSymbol = utils.ImportNow("promise_state_symbol");
|
| var promiseResultSymbol = utils.ImportNow("promise_result_symbol");
|
| +var SetAdd;
|
| +var SetHas;
|
| var SpeciesConstructor;
|
| var speciesSymbol = utils.ImportNow("species_symbol");
|
| var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
|
|
|
| utils.Import(function(from) {
|
| SpeciesConstructor = from.SpeciesConstructor;
|
| + SetAdd = from.SetAdd;
|
| + SetHas = from.SetHas;
|
| });
|
|
|
| // -------------------------------------------------------------------
|
| @@ -236,7 +241,7 @@ function IsPromise(x) {
|
| }
|
|
|
| function PromiseCreate() {
|
| - return new GlobalPromise(PromiseNopResolver)
|
| + return new GlobalPromise(PromiseNopResolver);
|
| }
|
|
|
| // ES#sec-promise-resolve-functions
|
| @@ -287,6 +292,11 @@ function ResolvePromise(promise, resolution) {
|
| var id;
|
| var name = "PromiseResolveThenableJob";
|
| var instrumenting = DEBUG_IS_ACTIVE;
|
| + if (instrumenting && !IS_UNDEFINED(resolution) &&
|
| + IsPromise(resolution)) {
|
| + // Mark the dependency of the new promise on the resolution
|
| + SET_PRIVATE(resolution, promiseAwaitHandlerSymbol, promise);
|
| + }
|
| %EnqueueMicrotask(function() {
|
| if (instrumenting) {
|
| %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
|
| @@ -310,7 +320,8 @@ function ResolvePromise(promise, resolution) {
|
| return;
|
| }
|
| }
|
| - FulfillPromise(promise, kFulfilled, resolution, promiseFulfillReactionsSymbol);
|
| + FulfillPromise(promise, kFulfilled, resolution,
|
| + promiseFulfillReactionsSymbol);
|
| }
|
|
|
| // ES#sec-rejectpromise
|
| @@ -536,38 +547,64 @@ function PromiseRace(iterable) {
|
|
|
| // Utility for debugger
|
|
|
| -function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) {
|
| - // If this handler was installed by async/await, it does not indicate
|
| - // that there is a user-defined reject handler.
|
| - if (GET_PRIVATE(handler, promiseAwaitHandlerSymbol)) return false;
|
| +function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred, visited) {
|
| + // If this handler was installed by a locally uncaught await, recurse
|
| + // up to the outer Promise returned by that async function.
|
| + // In this case, the dependency subsumes any other things attached to the
|
| + // handler, as the dependency is only present due to async/await and is not
|
| + // a real catch handler.
|
| + var outerPromise = GET_PRIVATE(handler, promiseAwaitHandlerSymbol);
|
| + if (outerPromise) {
|
| + return PromiseHasUserDefinedRejectHandlerRecursive(outerPromise, visited);
|
| + }
|
| if (handler !== PromiseIdRejectHandler) {
|
| var combinedDeferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol);
|
| if (IS_UNDEFINED(combinedDeferred)) return true;
|
| - if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise)) {
|
| + if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise,
|
| + visited)) {
|
| return true;
|
| }
|
| - } else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) {
|
| + } else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise,
|
| + visited)) {
|
| return true;
|
| }
|
| return false;
|
| }
|
|
|
| -function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
|
| +function PromiseHasUserDefinedRejectHandlerRecursive(promise, visited) {
|
| + // Avoid visiting the Promise multiple times in case there is a cycle
|
| + // in the dependency graph.
|
| + if (%_Call(SetHas, visited, promise)) return false;
|
| + %_Call(SetAdd, visited, promise);
|
| +
|
| // If this promise was marked as being handled by a catch block
|
| // in an async function, then it has a user-defined reject handler.
|
| if (GET_PRIVATE(promise, promiseHandledHintSymbol)) return true;
|
|
|
| + // If this Promise is subsumed by another Promise (a Promise resolved
|
| + // with another Promise, or an intermediate, hidden, throwaway Promise
|
| + // within async/await), then recurse on the outer Promise.
|
| + // In this case, the dependency is one possible way that the Promise
|
| + // could be resolved, so it does not subsume the other following cases.
|
| + var outerPromise = GET_PRIVATE(promise, promiseAwaitHandlerSymbol);
|
| + if (outerPromise &&
|
| + PromiseHasUserDefinedRejectHandlerRecursive(outerPromise, visited)) {
|
| + return true;
|
| + }
|
| +
|
| var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol);
|
| var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol);
|
|
|
| if (IS_UNDEFINED(queue)) return false;
|
|
|
| if (!IS_ARRAY(queue)) {
|
| - return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds);
|
| + return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds, visited);
|
| }
|
|
|
| for (var i = 0; i < queue.length; i += 2) {
|
| - if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], queue[i + 1])) {
|
| + if (PromiseHasUserDefinedRejectHandlerCheck(queue[i],
|
| + queue[i + 1],
|
| + visited)) {
|
| return true;
|
| }
|
| }
|
| @@ -577,8 +614,11 @@ function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
|
| // Return whether the promise will be handled by a user-defined reject
|
| // handler somewhere down the promise chain. For this, we do a depth-first
|
| // search for a reject handler that's not the default PromiseIdRejectHandler.
|
| +// This function also traverses dependencies of one Promise on another,
|
| +// set up through async/await and Promises resolved with Promises. The graph
|
| +// may contain cycles, so a set of visited Promises is maintained.
|
| function PromiseHasUserDefinedRejectHandler() {
|
| - return PromiseHasUserDefinedRejectHandlerRecursive(this);
|
| + return PromiseHasUserDefinedRejectHandlerRecursive(this, new GlobalSet());
|
| };
|
|
|
|
|
|
|