Commit 8efed0f9 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by Commit Bot

Set is_awaiting on async generators after await's Promise machinery

The is_awaiting bit on async generators distinguishes waiting on an
await. When the async generator resumes from an await, the is_awaiting
bit is cleared.

It is possible through overriding Promise#constructor that `await`
throws *after* setting is_awaiting. There is an implicit try-catch
around the body of the async generator such that, usually, caught
exceptions would clear the is_awaiting bit. However, the exception
thrown from a monkeypatched Promise#constructor can be caught by script,
and thus never clear the is_awaiting bit.

This CL sets the is_awaiting bit *after* `await` completes, with the
exception of the return resumption. It is not possible to have the
exception thrown by the await in the return resumption be caught by
script.

Bug: chromium:1171667
Change-Id: I0b615617a5c949f03350ab0f06c42920d43b5488
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2659508Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72593}
parent 3985d6a4
......@@ -242,10 +242,10 @@ void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) {
TNode<JSPromise> outer_promise = LoadObjectField<JSPromise>(
request, AsyncGeneratorRequest::kPromiseOffset);
SetGeneratorAwaiting(async_generator_object);
Await(context, async_generator_object, value, outer_promise,
AsyncGeneratorAwaitResolveSharedFunConstant(),
AsyncGeneratorAwaitRejectSharedFunConstant(), is_catchable);
SetGeneratorAwaiting(async_generator_object);
Return(UndefinedConstant());
}
......@@ -570,10 +570,10 @@ TF_BUILTIN(AsyncGeneratorYield, AsyncGeneratorBuiltinsAssembler) {
const TNode<JSPromise> outer_promise =
LoadPromiseFromAsyncGeneratorRequest(request);
SetGeneratorAwaiting(generator);
Await(context, generator, value, outer_promise,
AsyncGeneratorYieldResolveSharedFunConstant(),
AsyncGeneratorAwaitRejectSharedFunConstant(), is_caught);
SetGeneratorAwaiting(generator);
Return(UndefinedConstant());
}
......
// Copyright 2021 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.
// Async generator builtins that suspend the generator set the is_awaiting bit
// to 1 before awaiting. This is cleared when resumed. This tests that the bit
// is set after the await operation successfully completes (i.e. returns the
// Promise), since it can throw, and that thrown exception can be caught by
// script. Otherwise the is_awaiting bit won't be cleared.
// This makes `await new Promise(() => {})` throw.
Object.defineProperty(Promise.prototype, 'constructor', {
get() { throw 42; }
});
// AsyncGeneratorAwait
{
async function *f() {
try {
await new Promise(() => {});
} catch (e) {
}
}
f().next();
}
// AsyncGeneratorYield
{
async function *f() {
try {
yield new Promise(() => {});
} catch (e) {
}
}
f().next();
}
// AsyncGeneratorReturn isn't affected because it's not possible, in script, to
// catch an error thrown by a return resumption. It'll be caught by the
// synthetic try-catch around the whole body of the async generator, which will
// correctly reset the is_awaiting bit.
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