[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) {
|
BUILTIN(CallSitePrototypeGetPromiseIndex) {
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
CHECK_CALLSITE(frame, "getPromiseIndex");
|
CHECK_CALLSITE(frame, "getPromiseIndex");
|
||||||
if (!frame->IsPromiseAll() && !frame->IsPromiseAny()) {
|
if (!frame->IsPromiseAll() && !frame->IsPromiseAny() &&
|
||||||
|
!frame->IsPromiseAllSettled()) {
|
||||||
return ReadOnlyRoots(isolate).null_value();
|
return ReadOnlyRoots(isolate).null_value();
|
||||||
}
|
}
|
||||||
return Smi::FromInt(CallSiteInfo::GetSourcePosition(frame));
|
return Smi::FromInt(CallSiteInfo::GetSourcePosition(frame));
|
||||||
|
@ -989,6 +989,25 @@ void CaptureAsyncStackTrace(Isolate* isolate, Handle<JSPromise> promise,
|
|||||||
PromiseCapability::cast(context->get(index)), isolate);
|
PromiseCapability::cast(context->get(index)), isolate);
|
||||||
if (!capability->promise().IsJSPromise()) return;
|
if (!capability->promise().IsJSPromise()) return;
|
||||||
promise = handle(JSPromise::cast(capability->promise()), isolate);
|
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(),
|
} else if (IsBuiltinFunction(isolate, reaction->reject_handler(),
|
||||||
Builtin::kPromiseAnyRejectElementClosure)) {
|
Builtin::kPromiseAnyRejectElementClosure)) {
|
||||||
Handle<JSFunction> function(JSFunction::cast(reaction->reject_handler()),
|
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);
|
isolate_, promise_fun, "all", Builtin::kPromiseAll, 1, true);
|
||||||
native_context()->set_promise_all(*promise_all);
|
native_context()->set_promise_all(*promise_all);
|
||||||
|
|
||||||
InstallFunctionWithBuiltinId(isolate_, promise_fun, "allSettled",
|
Handle<JSFunction> promise_all_settled =
|
||||||
Builtin::kPromiseAllSettled, 1, true);
|
InstallFunctionWithBuiltinId(isolate_, promise_fun, "allSettled",
|
||||||
|
Builtin::kPromiseAllSettled, 1, true);
|
||||||
|
native_context()->set_promise_all_settled(*promise_all_settled);
|
||||||
|
|
||||||
Handle<JSFunction> promise_any = InstallFunctionWithBuiltinId(
|
Handle<JSFunction> promise_any = InstallFunctionWithBuiltinId(
|
||||||
isolate_, promise_fun, "any", Builtin::kPromiseAny, 1, true);
|
isolate_, promise_fun, "any", Builtin::kPromiseAny, 1, true);
|
||||||
|
@ -22,6 +22,12 @@ bool CallSiteInfo::IsPromiseAll() const {
|
|||||||
return fun == fun.native_context().promise_all();
|
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 {
|
bool CallSiteInfo::IsPromiseAny() const {
|
||||||
if (!IsAsync()) return false;
|
if (!IsAsync()) return false;
|
||||||
JSFunction fun = JSFunction::cast(function());
|
JSFunction fun = JSFunction::cast(function());
|
||||||
@ -507,6 +513,7 @@ int CallSiteInfo::GetSourcePosition(Handle<CallSiteInfo> info) {
|
|||||||
return info->code_offset_or_source_position();
|
return info->code_offset_or_source_position();
|
||||||
}
|
}
|
||||||
DCHECK(!info->IsPromiseAll());
|
DCHECK(!info->IsPromiseAll());
|
||||||
|
DCHECK(!info->IsPromiseAllSettled());
|
||||||
DCHECK(!info->IsPromiseAny());
|
DCHECK(!info->IsPromiseAny());
|
||||||
int source_position =
|
int source_position =
|
||||||
ComputeSourcePosition(info, info->code_offset_or_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);
|
Handle<Object> function_name = CallSiteInfo::GetFunctionName(frame);
|
||||||
if (frame->IsAsync()) {
|
if (frame->IsAsync()) {
|
||||||
builder->AppendCStringLiteral("async ");
|
builder->AppendCStringLiteral("async ");
|
||||||
if (frame->IsPromiseAll() || frame->IsPromiseAny()) {
|
if (frame->IsPromiseAll() || frame->IsPromiseAny() ||
|
||||||
|
frame->IsPromiseAllSettled()) {
|
||||||
builder->AppendCStringLiteral("Promise.");
|
builder->AppendCStringLiteral("Promise.");
|
||||||
builder->AppendString(Handle<String>::cast(function_name));
|
builder->AppendString(Handle<String>::cast(function_name));
|
||||||
builder->AppendCStringLiteral(" (index ");
|
builder->AppendCStringLiteral(" (index ");
|
||||||
|
@ -40,6 +40,7 @@ class CallSiteInfo : public TorqueGeneratedCallSiteInfo<CallSiteInfo, Struct> {
|
|||||||
bool IsMethodCall() const;
|
bool IsMethodCall() const;
|
||||||
bool IsToplevel() const;
|
bool IsToplevel() const;
|
||||||
bool IsPromiseAll() const;
|
bool IsPromiseAll() const;
|
||||||
|
bool IsPromiseAllSettled() const;
|
||||||
bool IsPromiseAny() const;
|
bool IsPromiseAny() const;
|
||||||
bool IsNative() const;
|
bool IsNative() const;
|
||||||
|
|
||||||
|
@ -345,6 +345,7 @@ enum ContextLookupFlags {
|
|||||||
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
|
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
|
||||||
V(OBJECT_VALUE_OF_FUNCTION_INDEX, JSFunction, object_value_of_function) \
|
V(OBJECT_VALUE_OF_FUNCTION_INDEX, JSFunction, object_value_of_function) \
|
||||||
V(PROMISE_ALL_INDEX, JSFunction, promise_all) \
|
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_ANY_INDEX, JSFunction, promise_any) \
|
||||||
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
|
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
|
||||||
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_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