[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/+/3518909 Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/main@{#79632}
This commit is contained in:
parent
cfa4581e69
commit
89ed081c17
@ -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);
|
||||
|
||||
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) \
|
||||
|
45
test/mjsunit/async-stack-traces-promise-all-settled.js
Normal file
45
test/mjsunit/async-stack-traces-promise-all-settled.js
Normal file
@ -0,0 +1,45 @@
|
||||
// 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);
|
||||
})());
|
||||
})();
|
Loading…
Reference in New Issue
Block a user