Commit a3b145c7 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by V8 LUCI CQ

[debug] Fix catch prediction inside async generators.

Properly push back the current request promise for async generators when
resuming after an `await` to ensure that the catch prediction works as
expected for async generators.

Fixed: chromium:1220203
Change-Id: I8c3592ceb567aadcba8f460794cd5d60a965a360
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3442680
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarPhilip Pfaffe <pfaffe@chromium.org>
Commit-Queue: Philip Pfaffe <pfaffe@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78984}
parent 8cdd0bfb
...@@ -211,21 +211,40 @@ AsyncGeneratorBuiltinsAssembler::AllocateAsyncGeneratorRequest( ...@@ -211,21 +211,40 @@ AsyncGeneratorBuiltinsAssembler::AllocateAsyncGeneratorRequest(
void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure( void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure(
TNode<Context> context, TNode<Object> value, TNode<Context> context, TNode<Object> value,
JSAsyncGeneratorObject::ResumeMode resume_mode) { JSAsyncGeneratorObject::ResumeMode resume_mode) {
const TNode<JSAsyncGeneratorObject> generator = const TNode<JSAsyncGeneratorObject> async_generator_object =
CAST(LoadContextElement(context, Context::EXTENSION_INDEX)); CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
SetGeneratorNotAwaiting(generator); SetGeneratorNotAwaiting(async_generator_object);
CSA_SLOW_DCHECK(this, IsGeneratorSuspended(generator)); CSA_SLOW_DCHECK(this, IsGeneratorSuspended(async_generator_object));
// Remember the {resume_mode} for the {generator}. // Remember the {resume_mode} for the {async_generator_object}.
StoreObjectFieldNoWriteBarrier(generator, StoreObjectFieldNoWriteBarrier(async_generator_object,
JSGeneratorObject::kResumeModeOffset, JSGeneratorObject::kResumeModeOffset,
SmiConstant(resume_mode)); SmiConstant(resume_mode));
CallStub(CodeFactory::ResumeGenerator(isolate()), context, value, generator); // Push the promise for the {async_generator_object} back onto the catch
// prediction stack to handle exceptions thrown after resuming from the
// await properly.
Label if_instrumentation(this, Label::kDeferred),
if_instrumentation_done(this);
Branch(IsDebugActive(), &if_instrumentation, &if_instrumentation_done);
BIND(&if_instrumentation);
{
TNode<AsyncGeneratorRequest> request =
CAST(LoadFirstAsyncGeneratorRequestFromQueue(async_generator_object));
TNode<JSPromise> promise = LoadObjectField<JSPromise>(
request, AsyncGeneratorRequest::kPromiseOffset);
CallRuntime(Runtime::kDebugPushPromise, context, promise);
Goto(&if_instrumentation_done);
}
BIND(&if_instrumentation_done);
CallStub(CodeFactory::ResumeGenerator(isolate()), context, value,
async_generator_object);
TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator); TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context,
async_generator_object);
} }
template <typename Descriptor> template <typename Descriptor>
......
Regression test for crbug.com/1220203.
Running test: testBreakOnUncaughtException
Uncaught exception at
function throwError() {
#throw new Error();
}
// 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.
let {session, contextGroup, Protocol} = InspectorTest.start('Regression test for crbug.com/1220203.');
contextGroup.addScript(`
async function *generatorFunction() {
await 1;
throwError();
}
function throwError() {
throw new Error();
}
async function main() {
for await (const value of generatorFunction()) {}
}`);
session.setupScriptMap();
InspectorTest.runAsyncTestSuite([
async function testBreakOnUncaughtException() {
await Promise.all([
Protocol.Runtime.enable(),
Protocol.Debugger.enable(),
Protocol.Debugger.setPauseOnExceptions({state: 'uncaught'}),
]);
const pausedPromise = Protocol.Debugger.oncePaused();
const evalPromise = Protocol.Runtime.evaluate({expression: 'main()', awaitPromise: true});
const {params: {callFrames, data}} = await pausedPromise;
InspectorTest.log(`${data.uncaught ? 'Uncaught' : 'Caught'} exception at`);
await session.logSourceLocation(callFrames[0].location);
await Promise.all([
Protocol.Debugger.resume(),
evalPromise,
Protocol.Runtime.disable(),
Protocol.Debugger.disable(),
]);
},
]);
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