Avoid leaking Promises when detaching debugger

When the debugger is active and a Promise begins executing,
Isolate::PushPromise adds a global handle for that Promise. If the
debugger is no longer attached when the Promise finishes executing, then
there is no corresponding call to PopPromise which would clean up the
global handle. To avoid leaking memory in that case, we should clean up
the Promise stack when detaching the debugger.

Bug: v8:12613
Change-Id: I47a2c37713b43b482e23e2457e96fba5f52623f4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3448949
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#79017}
This commit is contained in:
Seth Brenith 2022-02-08 10:58:41 -08:00 committed by V8 LUCI CQ
parent 0d05f1807d
commit aae45ca822
4 changed files with 12 additions and 3 deletions

View File

@ -440,6 +440,7 @@ void Debug::Unload() {
ClearStepping();
RemoveAllCoverageInfos();
ClearAllDebuggerHints();
ClearGlobalPromiseStack();
debug_delegate_ = nullptr;
}
@ -1990,6 +1991,11 @@ void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev,
delete node;
}
void Debug::ClearGlobalPromiseStack() {
while (isolate_->PopPromise()) {
}
}
bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
HandleScope scope(isolate_);

View File

@ -475,6 +475,8 @@ class V8_EXPORT_PRIVATE Debug {
DebugInfoListNode** curr);
void FreeDebugInfoListNode(DebugInfoListNode* prev, DebugInfoListNode* node);
void ClearGlobalPromiseStack();
void SetTemporaryObjectTrackingDisabled(bool disabled);
bool GetTemporaryObjectTrackingDisabled() const;

View File

@ -2599,14 +2599,15 @@ void Isolate::PushPromise(Handle<JSObject> promise) {
tltop->promise_on_stack_ = new PromiseOnStack(global_promise, prev);
}
void Isolate::PopPromise() {
bool Isolate::PopPromise() {
ThreadLocalTop* tltop = thread_local_top();
if (tltop->promise_on_stack_ == nullptr) return;
if (tltop->promise_on_stack_ == nullptr) return false;
PromiseOnStack* prev = tltop->promise_on_stack_->prev();
Handle<Object> global_promise = tltop->promise_on_stack_->promise();
delete tltop->promise_on_stack_;
tltop->promise_on_stack_ = prev;
global_handles()->Destroy(global_promise.location());
return true;
}
namespace {

View File

@ -855,7 +855,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
// Push and pop a promise and the current try-catch handler.
void PushPromise(Handle<JSObject> promise);
void PopPromise();
bool PopPromise();
// Return the relevant Promise that a throw/rejection pertains to, based
// on the contents of the Promise stack