Commit ff8d67c8 authored by Stephen Belanger's avatar Stephen Belanger Committed by V8 LUCI CQ

Reland "Fix Context PromiseHook behaviour with debugger enabled"

This is a reland of commit 872b7faa

Original change's description:
> Fix Context PromiseHook behaviour with debugger enabled
>
> This is a solution for https://github.com/nodejs/node/issues/43148.
>
> Due to differences in behaviour between code with and without the debugger enabled, some promise lifecycle events were being missed and some extra ones were being added. This change resolves this and verifies the event sequence is consistent between code with and without the debugger.
>
> Change-Id: I3dabf1dceb14233226b1752083d659f1c2f97966
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3779922
> Reviewed-by: Victor Gomes <victorgomes@chromium.org>
> Commit-Queue: Camillo Bruni <cbruni@chromium.org>
> Reviewed-by: Camillo Bruni <cbruni@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#82132}

Change-Id: Ifdd407261c793887fbd012d5a04ba36b3744c349
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3805979Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarVictor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82575}
parent 5e7c8ea6
...@@ -479,30 +479,38 @@ void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext( ...@@ -479,30 +479,38 @@ void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext(
void MicrotaskQueueBuiltinsAssembler::RunAllPromiseHooks( void MicrotaskQueueBuiltinsAssembler::RunAllPromiseHooks(
PromiseHookType type, TNode<Context> context, PromiseHookType type, TNode<Context> context,
TNode<HeapObject> promise_or_capability) { TNode<HeapObject> promise_or_capability) {
Label hook(this, Label::kDeferred), done_hook(this);
TNode<Uint32T> promiseHookFlags = PromiseHookFlags(); TNode<Uint32T> promiseHookFlags = PromiseHookFlags();
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
Label hook(this, Label::kDeferred), done_hook(this);
Branch(NeedsAnyPromiseHooks(promiseHookFlags), &hook, &done_hook); Branch(NeedsAnyPromiseHooks(promiseHookFlags), &hook, &done_hook);
BIND(&hook); BIND(&hook);
{ {
#endif
switch (type) { switch (type) {
case PromiseHookType::kBefore: case PromiseHookType::kBefore:
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
RunContextPromiseHookBefore(context, promise_or_capability, RunContextPromiseHookBefore(context, promise_or_capability,
promiseHookFlags); promiseHookFlags);
#endif
RunPromiseHook(Runtime::kPromiseHookBefore, context, RunPromiseHook(Runtime::kPromiseHookBefore, context,
promise_or_capability, promiseHookFlags); promise_or_capability, promiseHookFlags);
break; break;
case PromiseHookType::kAfter: case PromiseHookType::kAfter:
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
RunContextPromiseHookAfter(context, promise_or_capability, RunContextPromiseHookAfter(context, promise_or_capability,
promiseHookFlags); promiseHookFlags);
#endif
RunPromiseHook(Runtime::kPromiseHookAfter, context, RunPromiseHook(Runtime::kPromiseHookAfter, context,
promise_or_capability, promiseHookFlags); promise_or_capability, promiseHookFlags);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
Goto(&done_hook); Goto(&done_hook);
} }
BIND(&done_hook); BIND(&done_hook);
#endif
} }
void MicrotaskQueueBuiltinsAssembler::RunPromiseHook( void MicrotaskQueueBuiltinsAssembler::RunPromiseHook(
......
...@@ -2296,6 +2296,16 @@ void Shell::AsyncHooksTriggerAsyncId( ...@@ -2296,6 +2296,16 @@ void Shell::AsyncHooksTriggerAsyncId(
PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId())); PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId()));
} }
static v8::debug::DebugDelegate dummy_delegate;
void Shell::EnableDebugger(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::debug::SetDebugDelegate(args.GetIsolate(), &dummy_delegate);
}
void Shell::DisableDebugger(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::debug::SetDebugDelegate(args.GetIsolate(), nullptr);
}
void Shell::SetPromiseHooks(const v8::FunctionCallbackInfo<v8::Value>& args) { void Shell::SetPromiseHooks(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
if (i::FLAG_correctness_fuzzer_suppressions) { if (i::FLAG_correctness_fuzzer_suppressions) {
...@@ -3378,6 +3388,18 @@ Local<ObjectTemplate> Shell::CreateD8Template(Isolate* isolate) { ...@@ -3378,6 +3388,18 @@ Local<ObjectTemplate> Shell::CreateD8Template(Isolate* isolate) {
Local<Signature>(), 4)); Local<Signature>(), 4));
d8_template->Set(isolate, "promise", promise_template); d8_template->Set(isolate, "promise", promise_template);
} }
{
Local<ObjectTemplate> debugger_template = ObjectTemplate::New(isolate);
debugger_template->Set(
isolate, "enable",
FunctionTemplate::New(isolate, EnableDebugger, Local<Value>(),
Local<Signature>(), 0));
debugger_template->Set(
isolate, "disable",
FunctionTemplate::New(isolate, DisableDebugger, Local<Value>(),
Local<Signature>(), 0));
d8_template->Set(isolate, "debugger", debugger_template);
}
{ {
Local<ObjectTemplate> serializer_template = ObjectTemplate::New(isolate); Local<ObjectTemplate> serializer_template = ObjectTemplate::New(isolate);
serializer_template->Set( serializer_template->Set(
......
...@@ -583,6 +583,9 @@ class Shell : public i::AllStatic { ...@@ -583,6 +583,9 @@ class Shell : public i::AllStatic {
static void SetPromiseHooks(const v8::FunctionCallbackInfo<v8::Value>& args); static void SetPromiseHooks(const v8::FunctionCallbackInfo<v8::Value>& args);
static void EnableDebugger(const v8::FunctionCallbackInfo<v8::Value>& args);
static void DisableDebugger(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SerializerSerialize( static void SerializerSerialize(
const v8::FunctionCallbackInfo<v8::Value>& args); const v8::FunctionCallbackInfo<v8::Value>& args);
static void SerializerDeserialize( static void SerializerDeserialize(
......
...@@ -5154,9 +5154,11 @@ void Isolate::SetPromiseHook(PromiseHook hook) { ...@@ -5154,9 +5154,11 @@ void Isolate::SetPromiseHook(PromiseHook hook) {
void Isolate::RunAllPromiseHooks(PromiseHookType type, void Isolate::RunAllPromiseHooks(PromiseHookType type,
Handle<JSPromise> promise, Handle<JSPromise> promise,
Handle<Object> parent) { Handle<Object> parent) {
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
if (HasContextPromiseHooks()) { if (HasContextPromiseHooks()) {
native_context()->RunPromiseHook(type, promise, parent); native_context()->RunPromiseHook(type, promise, parent);
} }
#endif
if (HasIsolatePromiseHooks() || HasAsyncEventDelegate()) { if (HasIsolatePromiseHooks() || HasAsyncEventDelegate()) {
RunPromiseHook(type, promise, parent); RunPromiseHook(type, promise, parent);
} }
...@@ -5173,7 +5175,7 @@ void Isolate::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise, ...@@ -5173,7 +5175,7 @@ void Isolate::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
void Isolate::OnAsyncFunctionSuspended(Handle<JSPromise> promise, void Isolate::OnAsyncFunctionSuspended(Handle<JSPromise> promise,
Handle<JSPromise> parent) { Handle<JSPromise> parent) {
DCHECK_EQ(0, promise->async_task_id()); DCHECK_EQ(0, promise->async_task_id());
RunPromiseHook(PromiseHookType::kInit, promise, parent); RunAllPromiseHooks(PromiseHookType::kInit, promise, parent);
if (HasAsyncEventDelegate()) { if (HasAsyncEventDelegate()) {
DCHECK_NE(nullptr, async_event_delegate_); DCHECK_NE(nullptr, async_event_delegate_);
promise->set_async_task_id(++async_task_count_); promise->set_async_task_id(++async_task_count_);
......
...@@ -559,6 +559,7 @@ static_assert(NativeContext::kSize == ...@@ -559,6 +559,7 @@ static_assert(NativeContext::kSize ==
(Context::SizeFor(NativeContext::NATIVE_CONTEXT_SLOTS) + (Context::SizeFor(NativeContext::NATIVE_CONTEXT_SLOTS) +
kSystemPointerSize)); kSystemPointerSize));
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
void NativeContext::RunPromiseHook(PromiseHookType type, void NativeContext::RunPromiseHook(PromiseHookType type,
Handle<JSPromise> promise, Handle<JSPromise> promise,
Handle<Object> parent) { Handle<Object> parent) {
...@@ -614,6 +615,7 @@ void NativeContext::RunPromiseHook(PromiseHookType type, ...@@ -614,6 +615,7 @@ void NativeContext::RunPromiseHook(PromiseHookType type,
isolate->clear_pending_exception(); isolate->clear_pending_exception();
} }
} }
#endif
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -784,8 +784,10 @@ class NativeContext : public Context { ...@@ -784,8 +784,10 @@ class NativeContext : public Context {
void IncrementErrorsThrown(); void IncrementErrorsThrown();
int GetErrorsThrown(); int GetErrorsThrown();
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
void RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise, void RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
Handle<Object> parent); Handle<Object> parent);
#endif
private: private:
static_assert(OffsetOfElementAt(EMBEDDER_DATA_INDEX) == static_assert(OffsetOfElementAt(EMBEDDER_DATA_INDEX) ==
......
...@@ -5483,6 +5483,14 @@ Handle<Object> JSPromise::Fulfill(Handle<JSPromise> promise, ...@@ -5483,6 +5483,14 @@ Handle<Object> JSPromise::Fulfill(Handle<JSPromise> promise,
Handle<Object> value) { Handle<Object> value) {
Isolate* const isolate = promise->GetIsolate(); Isolate* const isolate = promise->GetIsolate();
#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
if (isolate->HasContextPromiseHooks()) {
isolate->raw_native_context().RunPromiseHook(
PromiseHookType::kResolve, promise,
isolate->factory()->undefined_value());
}
#endif
// 1. Assert: The value of promise.[[PromiseState]] is "pending". // 1. Assert: The value of promise.[[PromiseState]] is "pending".
CHECK_EQ(Promise::kPending, promise->status()); CHECK_EQ(Promise::kPending, promise->status());
...@@ -5562,8 +5570,8 @@ MaybeHandle<Object> JSPromise::Resolve(Handle<JSPromise> promise, ...@@ -5562,8 +5570,8 @@ MaybeHandle<Object> JSPromise::Resolve(Handle<JSPromise> promise,
DCHECK( DCHECK(
!reinterpret_cast<v8::Isolate*>(isolate)->GetCurrentContext().IsEmpty()); !reinterpret_cast<v8::Isolate*>(isolate)->GetCurrentContext().IsEmpty());
isolate->RunAllPromiseHooks(PromiseHookType::kResolve, promise, isolate->RunPromiseHook(PromiseHookType::kResolve, promise,
isolate->factory()->undefined_value()); isolate->factory()->undefined_value());
// 7. If SameValue(resolution, promise) is true, then // 7. If SameValue(resolution, promise) is true, then
if (promise.is_identical_to(resolution)) { if (promise.is_identical_to(resolution)) {
......
...@@ -220,7 +220,7 @@ function optimizerBailout(test, verify) { ...@@ -220,7 +220,7 @@ function optimizerBailout(test, verify) {
d8.promise.setHooks(); d8.promise.setHooks();
} }
if (has_promise_hooks) { function doTest () {
optimizerBailout(async () => { optimizerBailout(async () => {
await Promise.resolve(); await Promise.resolve();
}, () => { }, () => {
...@@ -234,6 +234,19 @@ if (has_promise_hooks) { ...@@ -234,6 +234,19 @@ if (has_promise_hooks) {
assertNextEvent('after', [ 3 ]); assertNextEvent('after', [ 3 ]);
assertEmptyLog(); assertEmptyLog();
}); });
optimizerBailout(async () => {
await Promise.reject();
}, () => {
assertNextEvent('init', [ 1 ]);
assertNextEvent('init', [ 2 ]);
assertNextEvent('resolve', [ 2 ]);
assertNextEvent('init', [ 3, 2 ]);
assertNextEvent('before', [ 3 ]);
assertNextEvent('resolve', [ 1 ]);
assertNextEvent('resolve', [ 3 ]);
assertNextEvent('after', [ 3 ]);
assertEmptyLog();
});
optimizerBailout(async () => { optimizerBailout(async () => {
await { then (cb) { cb() } }; await { then (cb) { cb() } };
}, () => { }, () => {
...@@ -249,6 +262,21 @@ if (has_promise_hooks) { ...@@ -249,6 +262,21 @@ if (has_promise_hooks) {
assertNextEvent('after', [ 3 ]); assertNextEvent('after', [ 3 ]);
assertEmptyLog(); assertEmptyLog();
}); });
optimizerBailout(async () => {
await { then (_, cb) { cb() } };
}, () => {
assertNextEvent('init', [ 1 ]);
assertNextEvent('init', [ 2, 1 ]);
assertNextEvent('init', [ 3, 2 ]);
assertNextEvent('before', [ 2 ]);
assertNextEvent('resolve', [ 2 ]);
assertNextEvent('after', [ 2 ]);
assertNextEvent('before', [ 3 ]);
assertNextEvent('resolve', [ 1 ]);
assertNextEvent('resolve', [ 3 ]);
assertNextEvent('after', [ 3 ]);
assertEmptyLog();
});
basicTest(); basicTest();
exceptions(); exceptions();
...@@ -292,3 +320,9 @@ if (has_promise_hooks) { ...@@ -292,3 +320,9 @@ if (has_promise_hooks) {
}); });
} }
if (has_promise_hooks) {
doTest();
d8.debugger.enable();
doTest();
}
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