Commit 89ed081c authored by jameslahm's avatar jameslahm Committed by V8 LUCI CQ

[runtime] Add async-stack-trace support for Promise.allSettled

... with zero cost.

Bug: v8:9357
Change-Id: I66985c3fd3e7b4efa354eb564c641562cf55ab49
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3518909Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79632}
parent cfa4581e
......@@ -104,7 +104,8 @@ BUILTIN(CallSitePrototypeGetPosition) {
BUILTIN(CallSitePrototypeGetPromiseIndex) {
HandleScope scope(isolate);
CHECK_CALLSITE(frame, "getPromiseIndex");
if (!frame->IsPromiseAll() && !frame->IsPromiseAny()) {
if (!frame->IsPromiseAll() && !frame->IsPromiseAny() &&
!frame->IsPromiseAllSettled()) {
return ReadOnlyRoots(isolate).null_value();
}
return Smi::FromInt(CallSiteInfo::GetSourcePosition(frame));
......
......@@ -989,6 +989,25 @@ void CaptureAsyncStackTrace(Isolate* isolate, Handle<JSPromise> promise,
PromiseCapability::cast(context->get(index)), isolate);
if (!capability->promise().IsJSPromise()) return;
promise = handle(JSPromise::cast(capability->promise()), isolate);
} else if (IsBuiltinFunction(
isolate, reaction->fulfill_handler(),
Builtin::kPromiseAllSettledResolveElementClosure)) {
Handle<JSFunction> function(JSFunction::cast(reaction->fulfill_handler()),
isolate);
Handle<Context> context(function->context(), isolate);
Handle<JSFunction> combinator(
context->native_context().promise_all_settled(), isolate);
builder->AppendPromiseCombinatorFrame(function, combinator);
// Now peak into the Promise.allSettled() resolve element context to
// find the promise capability that's being resolved when all
// the concurrent promises resolve.
int const index =
PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot;
Handle<PromiseCapability> capability(
PromiseCapability::cast(context->get(index)), isolate);
if (!capability->promise().IsJSPromise()) return;
promise = handle(JSPromise::cast(capability->promise()), isolate);
} else if (IsBuiltinFunction(isolate, reaction->reject_handler(),
Builtin::kPromiseAnyRejectElementClosure)) {
Handle<JSFunction> function(JSFunction::cast(reaction->reject_handler()),
......
......@@ -2387,8 +2387,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
isolate_, promise_fun, "all", Builtin::kPromiseAll, 1, true);
native_context()->set_promise_all(*promise_all);
InstallFunctionWithBuiltinId(isolate_, promise_fun, "allSettled",
Builtin::kPromiseAllSettled, 1, true);
Handle<JSFunction> promise_all_settled =
InstallFunctionWithBuiltinId(isolate_, promise_fun, "allSettled",
Builtin::kPromiseAllSettled, 1, true);
native_context()->set_promise_all_settled(*promise_all_settled);
Handle<JSFunction> promise_any = InstallFunctionWithBuiltinId(
isolate_, promise_fun, "any", Builtin::kPromiseAny, 1, true);
......
......@@ -22,6 +22,12 @@ bool CallSiteInfo::IsPromiseAll() const {
return fun == fun.native_context().promise_all();
}
bool CallSiteInfo::IsPromiseAllSettled() const {
if (!IsAsync()) return false;
JSFunction fun = JSFunction::cast(function());
return fun == fun.native_context().promise_all_settled();
}
bool CallSiteInfo::IsPromiseAny() const {
if (!IsAsync()) return false;
JSFunction fun = JSFunction::cast(function());
......@@ -507,6 +513,7 @@ int CallSiteInfo::GetSourcePosition(Handle<CallSiteInfo> info) {
return info->code_offset_or_source_position();
}
DCHECK(!info->IsPromiseAll());
DCHECK(!info->IsPromiseAllSettled());
DCHECK(!info->IsPromiseAny());
int source_position =
ComputeSourcePosition(info, info->code_offset_or_source_position());
......@@ -711,7 +718,8 @@ void SerializeJSStackFrame(Isolate* isolate, Handle<CallSiteInfo> frame,
Handle<Object> function_name = CallSiteInfo::GetFunctionName(frame);
if (frame->IsAsync()) {
builder->AppendCStringLiteral("async ");
if (frame->IsPromiseAll() || frame->IsPromiseAny()) {
if (frame->IsPromiseAll() || frame->IsPromiseAny() ||
frame->IsPromiseAllSettled()) {
builder->AppendCStringLiteral("Promise.");
builder->AppendString(Handle<String>::cast(function_name));
builder->AppendCStringLiteral(" (index ");
......
......@@ -40,6 +40,7 @@ class CallSiteInfo : public TorqueGeneratedCallSiteInfo<CallSiteInfo, Struct> {
bool IsMethodCall() const;
bool IsToplevel() const;
bool IsPromiseAll() const;
bool IsPromiseAllSettled() const;
bool IsPromiseAny() const;
bool IsNative() const;
......
......@@ -345,6 +345,7 @@ enum ContextLookupFlags {
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
V(OBJECT_VALUE_OF_FUNCTION_INDEX, JSFunction, object_value_of_function) \
V(PROMISE_ALL_INDEX, JSFunction, promise_all) \
V(PROMISE_ALL_SETTLED_INDEX, JSFunction, promise_all_settled) \
V(PROMISE_ANY_INDEX, JSFunction, promise_any) \
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --async-stack-traces
// Basic test with Promise.allSettled().
(function() {
async function fine() { }
async function thrower() {
await fine();
throw new Error();
}
async function driver() {
return await Promise.allSettled([fine(), fine(), thrower(), thrower()]);
}
async function test(f) {
const results = await f();
results.forEach((result, i) => {
if (result.status === 'rejected') {
const error = result.reason;
assertInstanceof(error, Error);
const stackRegexp = new RegExp("Error.+at thrower.+at " +
`async Promise.allSettled \\(index ${ i }\\)` +
".+ at async driver.+at async test",
"ms")
assertMatches(stackRegexp, error.stack);
}
});
}
assertPromiseResult((async () => {
%PrepareFunctionForOptimization(thrower);
%PrepareFunctionForOptimization(driver);
await test(driver);
await test(driver);
%OptimizeFunctionOnNextCall(thrower);
await test(driver);
%OptimizeFunctionOnNextCall(driver);
await test(driver);
})());
})();
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