Commit 100336f5 authored by littledan's avatar littledan Committed by Commit bot

Revert of Async/await Promise dependency graph (patchset #30 id:550001 of...

Revert of Async/await Promise dependency graph (patchset #30 id:550001 of https://codereview.chromium.org/2317383002/ )

Reason for revert:
Need to break up test into smaller tests to avoid timeouts

Original issue's description:
> Async/await Promise dependency graph
>
> This patch knits together Promises returned by async/await such that when
> one async function awaits the result of another one, catch prediction works
> across the boundaries, whether the exception comes synchronously or
> asynchronously. Edges are added in three places:
> - When a locally uncaught await happens, if the value passed into await
>   is a Promise, from the awaited value to the Promise under construction
>   in the broader async function
> - From a "throwaway" Promise, which may be found on the Promise debug
>   stack, to the Promise under construction in the async function that
>   surrounds it
> - When a Promise is resolved with another Promise (e.g., when returning a
>   Promise from an async function)
>
> BUG=v8:5167
>
> Committed: https://crrev.com/7265fdde7c76b9f875b40b0b139515936d491d64
> Cr-Commit-Position: refs/heads/master@{#39522}

TBR=adamk@chromium.org,jgruber@chromium.org,kozyatinskiy@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:5167

Review-Url: https://codereview.chromium.org/2351953002
Cr-Commit-Position: refs/heads/master@{#39523}
parent 7265fdde
...@@ -199,11 +199,11 @@ ...@@ -199,11 +199,11 @@
V(normal_ic_symbol) \ V(normal_ic_symbol) \
V(not_mapped_symbol) \ V(not_mapped_symbol) \
V(premonomorphic_symbol) \ V(premonomorphic_symbol) \
V(promise_await_handler_symbol) \
V(promise_combined_deferred_symbol) \
V(promise_debug_marker_symbol) \ V(promise_debug_marker_symbol) \
V(promise_deferred_reactions_symbol) \ V(promise_deferred_reactions_symbol) \
V(promise_forwarding_handler_symbol) \
V(promise_fulfill_reactions_symbol) \ V(promise_fulfill_reactions_symbol) \
V(promise_handled_by_symbol) \
V(promise_handled_hint_symbol) \ V(promise_handled_hint_symbol) \
V(promise_has_handler_symbol) \ V(promise_has_handler_symbol) \
V(promise_raw_symbol) \ V(promise_raw_symbol) \
......
...@@ -33,10 +33,7 @@ utils.Import(function(from) { ...@@ -33,10 +33,7 @@ utils.Import(function(from) {
ResolvePromise = from.ResolvePromise; ResolvePromise = from.ResolvePromise;
}); });
var promiseHandledBySymbol = var promiseAwaitHandlerSymbol = utils.ImportNow("promise_await_handler_symbol");
utils.ImportNow("promise_handled_by_symbol");
var promiseForwardingHandlerSymbol =
utils.ImportNow("promise_forwarding_handler_symbol");
var promiseHandledHintSymbol = var promiseHandledHintSymbol =
utils.ImportNow("promise_handled_hint_symbol"); utils.ImportNow("promise_handled_hint_symbol");
var promiseHasHandlerSymbol = var promiseHasHandlerSymbol =
...@@ -59,11 +56,10 @@ function PromiseCastResolved(value) { ...@@ -59,11 +56,10 @@ function PromiseCastResolved(value) {
// Shared logic for the core of await. The parser desugars // Shared logic for the core of await. The parser desugars
// await awaited // await awaited
// into // into
// yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise) // yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited)
// The 'awaited' parameter is the value; the generator stands in // The 'awaited' parameter is the value; the generator stands in
// for the asyncContext, and .promise is the larger promise under // for the asyncContext, and mark is metadata for debugging
// construction by the enclosing async function. function AsyncFunctionAwait(generator, awaited, mark) {
function AsyncFunctionAwait(generator, awaited, outerPromise) {
// Promise.resolve(awaited).then( // Promise.resolve(awaited).then(
// value => AsyncFunctionNext(value), // value => AsyncFunctionNext(value),
// error => AsyncFunctionThrow(error) // error => AsyncFunctionThrow(error)
...@@ -87,6 +83,12 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) { ...@@ -87,6 +83,12 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) {
return; return;
} }
if (mark && DEBUG_IS_ACTIVE && IsPromise(awaited)) {
// Mark the reject handler callback such that it does not influence
// catch prediction.
SET_PRIVATE(onRejected, promiseAwaitHandlerSymbol, true);
}
// Just forwarding the exception, so no debugEvent for throwawayCapability // Just forwarding the exception, so no debugEvent for throwawayCapability
var throwawayCapability = NewPromiseCapability(GlobalPromise, false); var throwawayCapability = NewPromiseCapability(GlobalPromise, false);
...@@ -94,38 +96,24 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) { ...@@ -94,38 +96,24 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) {
// unhandled reject events as its work is done // unhandled reject events as its work is done
SET_PRIVATE(throwawayCapability.promise, promiseHasHandlerSymbol, true); SET_PRIVATE(throwawayCapability.promise, promiseHasHandlerSymbol, true);
PerformPromiseThen(promise, onFulfilled, onRejected, throwawayCapability); return PerformPromiseThen(promise, onFulfilled, onRejected,
throwawayCapability);
if (DEBUG_IS_ACTIVE && !IS_UNDEFINED(outerPromise)) {
if (IsPromise(awaited)) {
// Mark the reject handler callback to be a forwarding edge, rather
// than a meaningful catch handler
SET_PRIVATE(onRejected, promiseForwardingHandlerSymbol, true);
}
// Mark the dependency to outerPromise in case the throwaway Promise is
// found on the Promise stack
SET_PRIVATE(throwawayCapability.promise, promiseHandledBySymbol,
outerPromise);
}
} }
// Called by the parser from the desugaring of 'await' when catch // Called by the parser from the desugaring of 'await' when catch
// prediction indicates no locally surrounding catch block // prediction indicates no locally surrounding catch block
function AsyncFunctionAwaitUncaught(generator, awaited, outerPromise) { function AsyncFunctionAwaitUncaught(generator, awaited) {
AsyncFunctionAwait(generator, awaited, outerPromise); // TODO(littledan): Install a dependency edge from awaited to outerPromise
return AsyncFunctionAwait(generator, awaited, true);
} }
// Called by the parser from the desugaring of 'await' when catch // Called by the parser from the desugaring of 'await' when catch
// prediction indicates that there is a locally surrounding catch block // prediction indicates that there is a locally surrounding catch block
function AsyncFunctionAwaitCaught(generator, awaited, outerPromise) { function AsyncFunctionAwaitCaught(generator, awaited) {
if (DEBUG_IS_ACTIVE && IsPromise(awaited)) { if (DEBUG_IS_ACTIVE && IsPromise(awaited)) {
SET_PRIVATE(awaited, promiseHandledHintSymbol, true); SET_PRIVATE(awaited, promiseHandledHintSymbol, true);
} }
// Pass undefined for the outer Promise to not waste time setting up return AsyncFunctionAwait(generator, awaited, false);
// or following the dependency chain when this Promise is already marked
// as handled
AsyncFunctionAwait(generator, awaited, UNDEFINED);
} }
// How the parser rejects promises from async/await desugaring // How the parser rejects promises from async/await desugaring
......
...@@ -219,8 +219,7 @@ function PostNatives(utils) { ...@@ -219,8 +219,7 @@ function PostNatives(utils) {
"iterator_symbol", "iterator_symbol",
"promise_result_symbol", "promise_result_symbol",
"promise_state_symbol", "promise_state_symbol",
"promise_forwarding_handler_symbol", "promise_await_handler_symbol",
"promise_handled_by_symbol",
"promise_handled_hint_symbol", "promise_handled_hint_symbol",
"promise_has_handler_symbol", "promise_has_handler_symbol",
"object_freeze", "object_freeze",
......
...@@ -12,10 +12,10 @@ ...@@ -12,10 +12,10 @@
// Imports // Imports
var InternalArray = utils.InternalArray; var InternalArray = utils.InternalArray;
var promiseHandledBySymbol = var promiseAwaitHandlerSymbol =
utils.ImportNow("promise_handled_by_symbol"); utils.ImportNow("promise_await_handler_symbol");
var promiseForwardingHandlerSymbol = var promiseCombinedDeferredSymbol =
utils.ImportNow("promise_forwarding_handler_symbol"); utils.ImportNow("promise_combined_deferred_symbol");
var promiseHasHandlerSymbol = var promiseHasHandlerSymbol =
utils.ImportNow("promise_has_handler_symbol"); utils.ImportNow("promise_has_handler_symbol");
var promiseRejectReactionsSymbol = var promiseRejectReactionsSymbol =
...@@ -222,7 +222,6 @@ function PromiseAttachCallbacks(promise, deferred, onResolve, onReject) { ...@@ -222,7 +222,6 @@ function PromiseAttachCallbacks(promise, deferred, onResolve, onReject) {
function PromiseIdResolveHandler(x) { return x; } function PromiseIdResolveHandler(x) { return x; }
function PromiseIdRejectHandler(r) { %_ReThrow(r); } function PromiseIdRejectHandler(r) { %_ReThrow(r); }
SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true);
function PromiseNopResolver() {} function PromiseNopResolver() {}
...@@ -237,7 +236,7 @@ function IsPromise(x) { ...@@ -237,7 +236,7 @@ function IsPromise(x) {
} }
function PromiseCreate() { function PromiseCreate() {
return new GlobalPromise(PromiseNopResolver); return new GlobalPromise(PromiseNopResolver)
} }
// ES#sec-promise-resolve-functions // ES#sec-promise-resolve-functions
...@@ -288,10 +287,6 @@ function ResolvePromise(promise, resolution) { ...@@ -288,10 +287,6 @@ function ResolvePromise(promise, resolution) {
var id; var id;
var name = "PromiseResolveThenableJob"; var name = "PromiseResolveThenableJob";
var instrumenting = DEBUG_IS_ACTIVE; var instrumenting = DEBUG_IS_ACTIVE;
if (instrumenting && IsPromise(resolution)) {
// Mark the dependency of the new promise on the resolution
SET_PRIVATE(resolution, promiseHandledBySymbol, promise);
}
%EnqueueMicrotask(function() { %EnqueueMicrotask(function() {
if (instrumenting) { if (instrumenting) {
%DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
...@@ -315,8 +310,7 @@ function ResolvePromise(promise, resolution) { ...@@ -315,8 +310,7 @@ function ResolvePromise(promise, resolution) {
return; return;
} }
} }
FulfillPromise(promise, kFulfilled, resolution, FulfillPromise(promise, kFulfilled, resolution, promiseFulfillReactionsSymbol);
promiseFulfillReactionsSymbol);
} }
// ES#sec-rejectpromise // ES#sec-rejectpromise
...@@ -478,10 +472,6 @@ function PromiseAll(iterable) { ...@@ -478,10 +472,6 @@ function PromiseAll(iterable) {
var resolutions = new InternalArray(); var resolutions = new InternalArray();
var count; var count;
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
SET_PRIVATE(deferred.reject, promiseForwardingHandlerSymbol, true);
function CreateResolveElementFunction(index, values, promiseCapability) { function CreateResolveElementFunction(index, values, promiseCapability) {
var alreadyCalled = false; var alreadyCalled = false;
return (x) => { return (x) => {
...@@ -502,14 +492,10 @@ function PromiseAll(iterable) { ...@@ -502,14 +492,10 @@ function PromiseAll(iterable) {
for (var value of iterable) { for (var value of iterable) {
var nextPromise = this.resolve(value); var nextPromise = this.resolve(value);
++count; ++count;
var throwawayPromise = nextPromise.then( nextPromise.then(
CreateResolveElementFunction(i, resolutions, deferred), CreateResolveElementFunction(i, resolutions, deferred),
deferred.reject); deferred.reject);
// For catch prediction, mark that rejections here are semantically SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred);
// handled by the combined Promise.
if (IsPromise(throwawayPromise)) {
SET_PRIVATE(throwawayPromise, promiseHandledBySymbol, deferred.promise);
}
++i; ++i;
} }
...@@ -536,20 +522,10 @@ function PromiseRace(iterable) { ...@@ -536,20 +522,10 @@ function PromiseRace(iterable) {
// false debugEvent so that forwarding the rejection through race does not // false debugEvent so that forwarding the rejection through race does not
// trigger redundant ExceptionEvents // trigger redundant ExceptionEvents
var deferred = NewPromiseCapability(this, false); var deferred = NewPromiseCapability(this, false);
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
SET_PRIVATE(deferred.reject, promiseForwardingHandlerSymbol, true);
try { try {
for (var value of iterable) { for (var value of iterable) {
var throwawayPromise = this.resolve(value).then(deferred.resolve, this.resolve(value).then(deferred.resolve, deferred.reject);
deferred.reject); SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred);
// For catch prediction, mark that rejections here are semantically
// handled by the combined Promise.
if (IsPromise(throwawayPromise)) {
SET_PRIVATE(throwawayPromise, promiseHandledBySymbol, deferred.promise);
}
} }
} catch (e) { } catch (e) {
deferred.reject(e) deferred.reject(e)
...@@ -561,18 +537,19 @@ function PromiseRace(iterable) { ...@@ -561,18 +537,19 @@ function PromiseRace(iterable) {
// Utility for debugger // Utility for debugger
function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) { function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) {
// Recurse to the forwarding Promise, if any. This may be due to // If this handler was installed by async/await, it does not indicate
// - await reaction forwarding to the throwaway Promise, which has // that there is a user-defined reject handler.
// a dependency edge to the outer Promise. if (GET_PRIVATE(handler, promiseAwaitHandlerSymbol)) return false;
// - PromiseIdResolveHandler forwarding to the output of .then if (handler !== PromiseIdRejectHandler) {
// - Promise.all/Promise.race forwarding to a throwaway Promise, which var combinedDeferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol);
// has a dependency edge to the generated outer Promise. if (IS_UNDEFINED(combinedDeferred)) return true;
if (GET_PRIVATE(handler, promiseForwardingHandlerSymbol)) { if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise)) {
return PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise); return true;
}
} else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) {
return true;
} }
return false;
// Otherwise, this is a real reject handler for the Promise
return true;
} }
function PromiseHasUserDefinedRejectHandlerRecursive(promise) { function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
...@@ -580,17 +557,6 @@ function PromiseHasUserDefinedRejectHandlerRecursive(promise) { ...@@ -580,17 +557,6 @@ function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
// in an async function, then it has a user-defined reject handler. // in an async function, then it has a user-defined reject handler.
if (GET_PRIVATE(promise, promiseHandledHintSymbol)) return true; 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, promiseHandledBySymbol);
if (outerPromise &&
PromiseHasUserDefinedRejectHandlerRecursive(outerPromise)) {
return true;
}
var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol); var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol);
var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol);
...@@ -611,8 +577,6 @@ function PromiseHasUserDefinedRejectHandlerRecursive(promise) { ...@@ -611,8 +577,6 @@ function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
// Return whether the promise will be handled by a user-defined reject // 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 // handler somewhere down the promise chain. For this, we do a depth-first
// search for a reject handler that's not the default PromiseIdRejectHandler. // 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.
function PromiseHasUserDefinedRejectHandler() { function PromiseHasUserDefinedRejectHandler() {
return PromiseHasUserDefinedRejectHandlerRecursive(this); return PromiseHasUserDefinedRejectHandlerRecursive(this);
}; };
......
...@@ -4563,7 +4563,7 @@ Expression* Parser::RewriteAwaitExpression(Expression* value, int await_pos) { ...@@ -4563,7 +4563,7 @@ Expression* Parser::RewriteAwaitExpression(Expression* value, int await_pos) {
// yield do { // yield do {
// promise_tmp = .promise; // promise_tmp = .promise;
// tmp = <operand>; // tmp = <operand>;
// %AsyncFunctionAwait(.generator_object, tmp, promise_tmp); // %AsyncFunctionAwait(.generator_object, tmp);
// promise_tmp // promise_tmp
// } // }
// The value of the expression is returned to the caller of the async // The value of the expression is returned to the caller of the async
...@@ -4608,13 +4608,11 @@ Expression* Parser::RewriteAwaitExpression(Expression* value, int await_pos) { ...@@ -4608,13 +4608,11 @@ Expression* Parser::RewriteAwaitExpression(Expression* value, int await_pos) {
zone()); zone());
ZoneList<Expression*>* async_function_await_args = ZoneList<Expression*>* async_function_await_args =
new (zone()) ZoneList<Expression*>(3, zone()); new (zone()) ZoneList<Expression*>(2, zone());
Expression* generator_object = Expression* generator_object =
factory()->NewVariableProxy(generator_object_variable); factory()->NewVariableProxy(generator_object_variable);
async_function_await_args->Add(generator_object, zone()); async_function_await_args->Add(generator_object, zone());
async_function_await_args->Add(factory()->NewVariableProxy(temp_var), zone()); async_function_await_args->Add(factory()->NewVariableProxy(temp_var), zone());
async_function_await_args->Add(factory()->NewVariableProxy(promise_temp_var),
zone());
// The parser emits calls to AsyncFunctionAwaitCaught, but the // The parser emits calls to AsyncFunctionAwaitCaught, but the
// AstNumberingVisitor will rewrite this to AsyncFunctionAwaitUncaught // AstNumberingVisitor will rewrite this to AsyncFunctionAwaitUncaught
......
...@@ -31,19 +31,6 @@ function rejectConstructor() { ...@@ -31,19 +31,6 @@ function rejectConstructor() {
async function argThrower(x = (() => { throw "d"; })()) { } // Exception d async function argThrower(x = (() => { throw "d"; })()) { } // Exception d
async function awaitThrow() {
await undefined;
throw "e"; // Exception e
}
function constructorThrow() {
return new Promise((resolve, reject) =>
Promise.resolve().then(() =>
reject("f") // Exception f
)
);
}
function suppressThrow() { function suppressThrow() {
return thrower(); return thrower();
} }
...@@ -93,43 +80,7 @@ async function indirectAwaitCatch(producer) { ...@@ -93,43 +80,7 @@ async function indirectAwaitCatch(producer) {
} }
} }
function switchCatch(producer) { let catches = [caught, indirectCaught, indirectAwaitCatch];
let resolve;
let promise = new Promise(r => resolve = r);
async function localCaught() {
try {
await promise; // force switching to localUncaught and back
await producer();
} catch (e) { }
}
async function localUncaught() {
await undefined;
resolve();
}
localCaught();
localUncaught();
}
function switchDotCatch(producer) {
let resolve;
let promise = new Promise(r => resolve = r);
async function localCaught() {
await promise; // force switching to localUncaught and back
await producer();
}
async function localUncaught() {
await undefined;
resolve();
}
localCaught().catch(() => {});
localUncaught();
}
let catches = [caught,
indirectCaught,
indirectAwaitCatch,
switchCatch,
switchDotCatch];
let noncatches = [uncaught, indirectUncaught]; let noncatches = [uncaught, indirectUncaught];
let lateCatches = [dotCatch, let lateCatches = [dotCatch,
indirectReturnDotCatch, indirectReturnDotCatch,
...@@ -138,19 +89,18 @@ let lateCatches = [dotCatch, ...@@ -138,19 +89,18 @@ let lateCatches = [dotCatch,
let throws = [thrower, reject, argThrower, suppressThrow]; let throws = [thrower, reject, argThrower, suppressThrow];
let nonthrows = [awaitReturn, scalar, nothing]; let nonthrows = [awaitReturn, scalar, nothing];
let lateThrows = [awaitThrow, constructorThrow];
let uncatchable = [rejectConstructor]; let uncatchable = [rejectConstructor];
let cases = []; let cases = [];
for (let producer of throws.concat(lateThrows)) { for (let producer of throws) {
for (let consumer of catches) { for (let consumer of catches) {
cases.push({ producer, consumer, expectedEvents: 1, caught: true }); cases.push({ producer, consumer, expectedEvents: 1, caught: true });
cases.push({ producer, consumer, expectedEvents: 0, caught: false }); cases.push({ producer, consumer, expectedEvents: 0, caught: false });
} }
} }
for (let producer of throws.concat(lateThrows)) { for (let producer of throws) {
for (let consumer of noncatches) { for (let consumer of noncatches) {
cases.push({ producer, consumer, expectedEvents: 1, caught: true }); cases.push({ producer, consumer, expectedEvents: 1, caught: true });
cases.push({ producer, consumer, expectedEvents: 1, caught: false }); cases.push({ producer, consumer, expectedEvents: 1, caught: false });
...@@ -171,13 +121,6 @@ for (let producer of uncatchable) { ...@@ -171,13 +121,6 @@ for (let producer of uncatchable) {
} }
} }
for (let producer of lateThrows) {
for (let consumer of lateCatches) {
cases.push({ producer, consumer, expectedEvents: 1, caught: true });
cases.push({ producer, consumer, expectedEvents: 0, caught: false });
}
}
for (let producer of throws) { for (let producer of throws) {
for (let consumer of lateCatches) { for (let consumer of lateCatches) {
cases.push({ producer, consumer, expectedEvents: 1, caught: true }); cases.push({ producer, consumer, expectedEvents: 1, caught: true });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment