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

[debug] Hold on to promises weakly from the debugger's promise stack.

The debugger maintains a stack of promises used for catch prediction
with promise builtins and async functions. Previously this stack would
hold on to the individual promises strongly, and subtle bugs that lead
to not properly cleaning up the stack in some corner cases would often
lead to significant memory issues (e.g. leaking whole iframes).

This refactors the PromiseOnStack to be

  (a) on the V8 heap, rather than allocating C++ structs with global
      handles pointing to the promises, and
  (b) hold on to the promises only weakly.

While this will not guarantee proper promise stack management, it will
at least ensure that edge cases don't lead to catastrophic (debugger
only) leaks.

Bug: chromium:1292063
Change-Id: I9c293ca2032de3a59e1e9624f132d37187805567
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3545176
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79594}
parent a18b1606
...@@ -390,6 +390,7 @@ void Debug::ThreadInit() { ...@@ -390,6 +390,7 @@ void Debug::ThreadInit() {
static_cast<base::AtomicWord>(0)); static_cast<base::AtomicWord>(0));
thread_local_.break_on_next_function_call_ = false; thread_local_.break_on_next_function_call_ = false;
UpdateHookOnFunctionCall(); UpdateHookOnFunctionCall();
thread_local_.promise_stack_ = Smi::zero();
} }
char* Debug::ArchiveDebug(char* storage) { char* Debug::ArchiveDebug(char* storage) {
...@@ -448,6 +449,8 @@ void Debug::Iterate(RootVisitor* v, ThreadLocal* thread_local_data) { ...@@ -448,6 +449,8 @@ void Debug::Iterate(RootVisitor* v, ThreadLocal* thread_local_data) {
v->VisitRootPointer( v->VisitRootPointer(
Root::kDebug, nullptr, Root::kDebug, nullptr,
FullObjectSlot(&thread_local_data->ignore_step_into_function_)); FullObjectSlot(&thread_local_data->ignore_step_into_function_));
v->VisitRootPointer(Root::kDebug, nullptr,
FullObjectSlot(&thread_local_data->promise_stack_));
} }
DebugInfoListNode::DebugInfoListNode(Isolate* isolate, DebugInfo debug_info) DebugInfoListNode::DebugInfoListNode(Isolate* isolate, DebugInfo debug_info)
...@@ -469,7 +472,6 @@ void Debug::Unload() { ...@@ -469,7 +472,6 @@ void Debug::Unload() {
ClearStepping(); ClearStepping();
RemoveAllCoverageInfos(); RemoveAllCoverageInfos();
ClearAllDebuggerHints(); ClearAllDebuggerHints();
ClearGlobalPromiseStack();
debug_delegate_ = nullptr; debug_delegate_ = nullptr;
} }
...@@ -2067,11 +2069,6 @@ void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev, ...@@ -2067,11 +2069,6 @@ void Debug::FreeDebugInfoListNode(DebugInfoListNode* prev,
delete node; delete node;
} }
void Debug::ClearGlobalPromiseStack() {
while (isolate_->PopPromise()) {
}
}
bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger); RCS_SCOPE(isolate_, RuntimeCallCounterId::kDebugger);
HandleScope scope(isolate_); HandleScope scope(isolate_);
......
...@@ -490,8 +490,6 @@ class V8_EXPORT_PRIVATE Debug { ...@@ -490,8 +490,6 @@ class V8_EXPORT_PRIVATE Debug {
DebugInfoListNode** curr); DebugInfoListNode** curr);
void FreeDebugInfoListNode(DebugInfoListNode* prev, DebugInfoListNode* node); void FreeDebugInfoListNode(DebugInfoListNode* prev, DebugInfoListNode* node);
void ClearGlobalPromiseStack();
void SetTemporaryObjectTrackingDisabled(bool disabled); void SetTemporaryObjectTrackingDisabled(bool disabled);
bool GetTemporaryObjectTrackingDisabled() const; bool GetTemporaryObjectTrackingDisabled() const;
...@@ -569,6 +567,10 @@ class V8_EXPORT_PRIVATE Debug { ...@@ -569,6 +567,10 @@ class V8_EXPORT_PRIVATE Debug {
// This flag is true when SetBreakOnNextFunctionCall is called and it forces // This flag is true when SetBreakOnNextFunctionCall is called and it forces
// debugger to break on next function call. // debugger to break on next function call.
bool break_on_next_function_call_; bool break_on_next_function_call_;
// Throwing an exception may cause a Promise rejection. For this purpose
// we keep track of a stack of nested promises.
Object promise_stack_;
}; };
static void Iterate(RootVisitor* v, ThreadLocal* thread_local_data); static void Iterate(RootVisitor* v, ThreadLocal* thread_local_data);
......
...@@ -2600,21 +2600,22 @@ bool Isolate::OptionalRescheduleException(bool clear_exception) { ...@@ -2600,21 +2600,22 @@ bool Isolate::OptionalRescheduleException(bool clear_exception) {
} }
void Isolate::PushPromise(Handle<JSObject> promise) { void Isolate::PushPromise(Handle<JSObject> promise) {
ThreadLocalTop* tltop = thread_local_top(); Handle<Object> promise_on_stack(debug()->thread_local_.promise_stack_, this);
PromiseOnStack* prev = tltop->promise_on_stack_; promise_on_stack = factory()->NewPromiseOnStack(promise_on_stack, promise);
Handle<JSObject> global_promise = global_handles()->Create(*promise); debug()->thread_local_.promise_stack_ = *promise_on_stack;
tltop->promise_on_stack_ = new PromiseOnStack(global_promise, prev); }
}
void Isolate::PopPromise() {
bool Isolate::PopPromise() { if (!IsPromiseStackEmpty()) {
ThreadLocalTop* tltop = thread_local_top(); debug()->thread_local_.promise_stack_ =
if (tltop->promise_on_stack_ == nullptr) return false; PromiseOnStack::cast(debug()->thread_local_.promise_stack_).prev();
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; bool Isolate::IsPromiseStackEmpty() const {
global_handles()->Destroy(global_promise.location()); DCHECK_IMPLIES(!debug()->thread_local_.promise_stack_.IsSmi(),
return true; debug()->thread_local_.promise_stack_.IsPromiseOnStack());
return debug()->thread_local_.promise_stack_.IsSmi();
} }
namespace { namespace {
...@@ -2688,15 +2689,14 @@ bool Isolate::PromiseHasUserDefinedRejectHandler(Handle<JSPromise> promise) { ...@@ -2688,15 +2689,14 @@ bool Isolate::PromiseHasUserDefinedRejectHandler(Handle<JSPromise> promise) {
Handle<Object> Isolate::GetPromiseOnStackOnThrow() { Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
Handle<Object> undefined = factory()->undefined_value(); Handle<Object> undefined = factory()->undefined_value();
ThreadLocalTop* tltop = thread_local_top(); if (IsPromiseStackEmpty()) return undefined;
if (tltop->promise_on_stack_ == nullptr) return undefined;
// Find the top-most try-catch or try-finally handler. // Find the top-most try-catch or try-finally handler.
CatchType prediction = PredictExceptionCatcher(); CatchType prediction = PredictExceptionCatcher();
if (prediction == NOT_CAUGHT || prediction == CAUGHT_BY_EXTERNAL) { if (prediction == NOT_CAUGHT || prediction == CAUGHT_BY_EXTERNAL) {
return undefined; return undefined;
} }
Handle<Object> retval = undefined; Handle<Object> retval = undefined;
PromiseOnStack* promise_on_stack = tltop->promise_on_stack_; Handle<Object> promise_stack(debug()->thread_local_.promise_stack_, this);
for (StackFrameIterator it(this); !it.done(); it.Advance()) { for (StackFrameIterator it(this); !it.done(); it.Advance()) {
StackFrame* frame = it.frame(); StackFrame* frame = it.frame();
HandlerTable::CatchPrediction catch_prediction; HandlerTable::CatchPrediction catch_prediction;
...@@ -2728,10 +2728,16 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() { ...@@ -2728,10 +2728,16 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
Handle<JSPromise>::cast(retval)->set_handled_hint(true); Handle<JSPromise>::cast(retval)->set_handled_hint(true);
} }
return retval; return retval;
case HandlerTable::PROMISE: case HandlerTable::PROMISE: {
return promise_on_stack Handle<JSObject> promise;
? Handle<Object>::cast(promise_on_stack->promise()) if (promise_stack->IsPromiseOnStack() &&
: undefined; PromiseOnStack::GetPromise(
Handle<PromiseOnStack>::cast(promise_stack))
.ToHandle(&promise)) {
return promise;
}
return undefined;
}
case HandlerTable::UNCAUGHT_ASYNC_AWAIT: case HandlerTable::UNCAUGHT_ASYNC_AWAIT:
case HandlerTable::ASYNC_AWAIT: { case HandlerTable::ASYNC_AWAIT: {
// If in the initial portion of async/await, continue the loop to pop up // If in the initial portion of async/await, continue the loop to pop up
...@@ -2739,15 +2745,21 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() { ...@@ -2739,15 +2745,21 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
// dependents is found, or a non-async stack frame is encountered, in // dependents is found, or a non-async stack frame is encountered, in
// order to handle the synchronous async/await catch prediction case: // order to handle the synchronous async/await catch prediction case:
// assume that async function calls are awaited. // assume that async function calls are awaited.
if (!promise_on_stack) return retval; if (!promise_stack->IsPromiseOnStack()) {
retval = promise_on_stack->promise(); return retval;
}
Handle<PromiseOnStack> promise_on_stack =
Handle<PromiseOnStack>::cast(promise_stack);
if (!PromiseOnStack::GetPromise(promise_on_stack).ToHandle(&retval)) {
return retval;
}
if (retval->IsJSPromise()) { if (retval->IsJSPromise()) {
if (PromiseHasUserDefinedRejectHandler( if (PromiseHasUserDefinedRejectHandler(
Handle<JSPromise>::cast(retval))) { Handle<JSPromise>::cast(retval))) {
return retval; return retval;
} }
} }
promise_on_stack = promise_on_stack->prev(); promise_stack = handle(promise_on_stack->prev(), this);
continue; continue;
} }
} }
...@@ -5039,8 +5051,7 @@ void Isolate::OnTerminationDuringRunMicrotasks() { ...@@ -5039,8 +5051,7 @@ void Isolate::OnTerminationDuringRunMicrotasks() {
heap()->set_current_microtask(ReadOnlyRoots(this).undefined_value()); heap()->set_current_microtask(ReadOnlyRoots(this).undefined_value());
// Empty the promise stack. // Empty the promise stack.
while (PopPromise()) { debug()->thread_local_.promise_stack_ = Smi::zero();
}
if (current_microtask->IsPromiseReactionJobTask()) { if (current_microtask->IsPromiseReactionJobTask()) {
Handle<PromiseReactionJobTask> promise_reaction_job_task = Handle<PromiseReactionJobTask> promise_reaction_job_task =
......
...@@ -859,7 +859,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -859,7 +859,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
// Push and pop a promise and the current try-catch handler. // Push and pop a promise and the current try-catch handler.
void PushPromise(Handle<JSObject> promise); void PushPromise(Handle<JSObject> promise);
bool PopPromise(); void PopPromise();
bool IsPromiseStackEmpty() const;
// Return the relevant Promise that a throw/rejection pertains to, based // Return the relevant Promise that a throw/rejection pertains to, based
// on the contents of the Promise stack // on the contents of the Promise stack
...@@ -2410,18 +2411,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { ...@@ -2410,18 +2411,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
#undef FIELD_ACCESSOR #undef FIELD_ACCESSOR
#undef THREAD_LOCAL_TOP_ACCESSOR #undef THREAD_LOCAL_TOP_ACCESSOR
class PromiseOnStack {
public:
PromiseOnStack(Handle<JSObject> promise, PromiseOnStack* prev)
: promise_(promise), prev_(prev) {}
Handle<JSObject> promise() { return promise_; }
PromiseOnStack* prev() { return prev_; }
private:
Handle<JSObject> promise_;
PromiseOnStack* prev_;
};
// SaveContext scopes save the current context on the Isolate on creation, and // SaveContext scopes save the current context on the Isolate on creation, and
// restore it on destruction. // restore it on destruction.
class V8_EXPORT_PRIVATE SaveContext { class V8_EXPORT_PRIVATE SaveContext {
......
...@@ -27,7 +27,6 @@ void ThreadLocalTop::Clear() { ...@@ -27,7 +27,6 @@ void ThreadLocalTop::Clear() {
c_entry_fp_ = kNullAddress; c_entry_fp_ = kNullAddress;
handler_ = kNullAddress; handler_ = kNullAddress;
c_function_ = kNullAddress; c_function_ = kNullAddress;
promise_on_stack_ = nullptr;
simulator_ = nullptr; simulator_ = nullptr;
js_entry_sp_ = kNullAddress; js_entry_sp_ = kNullAddress;
external_callback_scope_ = nullptr; external_callback_scope_ = nullptr;
...@@ -57,10 +56,7 @@ void ThreadLocalTop::Initialize(Isolate* isolate) { ...@@ -57,10 +56,7 @@ void ThreadLocalTop::Initialize(Isolate* isolate) {
#endif #endif
} }
void ThreadLocalTop::Free() { void ThreadLocalTop::Free() {}
// Match unmatched PopPromise calls.
while (promise_on_stack_) isolate_->PopPromise();
}
#if defined(USE_SIMULATOR) #if defined(USE_SIMULATOR)
void ThreadLocalTop::StoreCurrentStackPosition() { void ThreadLocalTop::StoreCurrentStackPosition() {
......
...@@ -26,7 +26,6 @@ namespace internal { ...@@ -26,7 +26,6 @@ namespace internal {
class EmbedderState; class EmbedderState;
class ExternalCallbackScope; class ExternalCallbackScope;
class Isolate; class Isolate;
class PromiseOnStack;
class Simulator; class Simulator;
class ThreadLocalTop { class ThreadLocalTop {
...@@ -35,9 +34,9 @@ class ThreadLocalTop { ...@@ -35,9 +34,9 @@ class ThreadLocalTop {
// refactor this to really consist of just Addresses and 32-bit // refactor this to really consist of just Addresses and 32-bit
// integer fields. // integer fields.
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING #ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
static constexpr uint32_t kSizeInBytes = 27 * kSystemPointerSize;
#else
static constexpr uint32_t kSizeInBytes = 26 * kSystemPointerSize; static constexpr uint32_t kSizeInBytes = 26 * kSystemPointerSize;
#else
static constexpr uint32_t kSizeInBytes = 25 * kSystemPointerSize;
#endif #endif
// Does early low-level initialization that does not depend on the // Does early low-level initialization that does not depend on the
...@@ -140,11 +139,6 @@ class ThreadLocalTop { ...@@ -140,11 +139,6 @@ class ThreadLocalTop {
// C function that was called at c entry. // C function that was called at c entry.
Address c_function_; Address c_function_;
// Throwing an exception may cause a Promise rejection. For this purpose
// we keep track of a stack of nested promises and the corresponding
// try-catch handlers.
PromiseOnStack* promise_on_stack_;
// Simulator field is always present to get predictable layout. // Simulator field is always present to get predictable layout.
Simulator* simulator_; Simulator* simulator_;
......
...@@ -3460,6 +3460,16 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo( ...@@ -3460,6 +3460,16 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
return handle(info, isolate()); return handle(info, isolate());
} }
Handle<PromiseOnStack> Factory::NewPromiseOnStack(Handle<Object> prev,
Handle<JSObject> promise) {
PromiseOnStack promise_on_stack = NewStructInternal<PromiseOnStack>(
PROMISE_ON_STACK_TYPE, AllocationType::kYoung);
DisallowGarbageCollection no_gc;
promise_on_stack.set_prev(*prev, SKIP_WRITE_BARRIER);
promise_on_stack.set_promise(*MaybeObjectHandle::Weak(promise));
return handle(promise_on_stack, isolate());
}
Handle<JSObject> Factory::NewArgumentsObject(Handle<JSFunction> callee, Handle<JSObject> Factory::NewArgumentsObject(Handle<JSFunction> callee,
int length) { int length) {
bool strict_mode_callee = is_strict(callee->shared().language_mode()) || bool strict_mode_callee = is_strict(callee->shared().language_mode()) ||
......
...@@ -407,6 +407,9 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> { ...@@ -407,6 +407,9 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
int bytecode_offset_or_source_position, Handle<String> function_name, int bytecode_offset_or_source_position, Handle<String> function_name,
bool is_constructor); bool is_constructor);
Handle<PromiseOnStack> NewPromiseOnStack(Handle<Object> prev,
Handle<JSObject> promise);
// Allocate various microtasks. // Allocate various microtasks.
Handle<CallableTask> NewCallableTask(Handle<JSReceiver> callable, Handle<CallableTask> NewCallableTask(Handle<JSReceiver> callable,
Handle<Context> context); Handle<Context> context);
......
...@@ -41,6 +41,7 @@ namespace internal { ...@@ -41,6 +41,7 @@ namespace internal {
V(Map) \ V(Map) \
V(NativeContext) \ V(NativeContext) \
V(PreparseData) \ V(PreparseData) \
V(PromiseOnStack) \
V(PropertyArray) \ V(PropertyArray) \
V(PropertyCell) \ V(PropertyCell) \
V(PrototypeInfo) \ V(PrototypeInfo) \
......
...@@ -85,6 +85,9 @@ ACCESSORS_RELAXED_CHECKED(ErrorStackData, call_site_infos, FixedArray, ...@@ -85,6 +85,9 @@ ACCESSORS_RELAXED_CHECKED(ErrorStackData, call_site_infos, FixedArray,
kCallSiteInfosOrFormattedStackOffset, kCallSiteInfosOrFormattedStackOffset,
!HasFormattedStack()) !HasFormattedStack())
NEVER_READ_ONLY_SPACE_IMPL(PromiseOnStack)
TQ_OBJECT_CONSTRUCTORS_IMPL(PromiseOnStack)
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -457,5 +457,16 @@ void ErrorStackData::EnsureStackFrameInfos(Isolate* isolate, ...@@ -457,5 +457,16 @@ void ErrorStackData::EnsureStackFrameInfos(Isolate* isolate,
error_stack->set_limit_or_stack_frame_infos(*stack_frame_infos); error_stack->set_limit_or_stack_frame_infos(*stack_frame_infos);
} }
// static
MaybeHandle<JSObject> PromiseOnStack::GetPromise(
Handle<PromiseOnStack> promise_on_stack) {
HeapObject promise;
Isolate* isolate = promise_on_stack->GetIsolate();
if (promise_on_stack->promise()->GetHeapObjectIfWeak(isolate, &promise)) {
return handle(JSObject::cast(promise), isolate);
}
return {};
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -251,6 +251,19 @@ class ErrorStackData ...@@ -251,6 +251,19 @@ class ErrorStackData
TQ_OBJECT_CONSTRUCTORS(ErrorStackData) TQ_OBJECT_CONSTRUCTORS(ErrorStackData)
}; };
class PromiseOnStack
: public TorqueGeneratedPromiseOnStack<PromiseOnStack, Struct> {
public:
NEVER_READ_ONLY_SPACE
static MaybeHandle<JSObject> GetPromise(
Handle<PromiseOnStack> promise_on_stack);
class BodyDescriptor;
TQ_OBJECT_CONSTRUCTORS(PromiseOnStack)
};
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -127,3 +127,8 @@ extern class ErrorStackData extends Struct { ...@@ -127,3 +127,8 @@ extern class ErrorStackData extends Struct {
// stack traces. // stack traces.
limit_or_stack_frame_infos: Smi|FixedArray; limit_or_stack_frame_infos: Smi|FixedArray;
} }
extern class PromiseOnStack extends Struct {
prev: PromiseOnStack|Zero;
promise: Weak<JSObject>;
}
...@@ -348,6 +348,9 @@ VisitorId Map::GetVisitorId(Map map) { ...@@ -348,6 +348,9 @@ VisitorId Map::GetVisitorId(Map map) {
#define MAKE_STRUCT_CASE(TYPE, Name, name) case TYPE: #define MAKE_STRUCT_CASE(TYPE, Name, name) case TYPE:
STRUCT_LIST(MAKE_STRUCT_CASE) STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE #undef MAKE_STRUCT_CASE
if (instance_type == PROMISE_ON_STACK_TYPE) {
return kVisitPromiseOnStack;
}
if (instance_type == PROTOTYPE_INFO_TYPE) { if (instance_type == PROTOTYPE_INFO_TYPE) {
return kVisitPrototypeInfo; return kVisitPrototypeInfo;
} }
......
...@@ -57,6 +57,7 @@ enum InstanceType : uint16_t; ...@@ -57,6 +57,7 @@ enum InstanceType : uint16_t;
V(Map) \ V(Map) \
V(NativeContext) \ V(NativeContext) \
V(PreparseData) \ V(PreparseData) \
V(PromiseOnStack) \
V(PropertyArray) \ V(PropertyArray) \
V(PropertyCell) \ V(PropertyCell) \
V(PrototypeInfo) \ V(PrototypeInfo) \
......
...@@ -606,6 +606,25 @@ class PreparseData::BodyDescriptor final : public BodyDescriptorBase { ...@@ -606,6 +606,25 @@ class PreparseData::BodyDescriptor final : public BodyDescriptorBase {
} }
}; };
class PromiseOnStack::BodyDescriptor final : public BodyDescriptorBase {
public:
static bool IsValidSlot(Map map, HeapObject obj, int offset) {
return offset >= HeapObject::kHeaderSize;
}
template <typename ObjectVisitor>
static inline void IterateBody(Map map, HeapObject obj, int object_size,
ObjectVisitor* v) {
IteratePointers(obj, Struct::kHeaderSize, kPromiseOffset, v);
IterateMaybeWeakPointer(obj, kPromiseOffset, v);
STATIC_ASSERT(kPromiseOffset + kTaggedSize == kHeaderSize);
}
static inline int SizeOf(Map map, HeapObject obj) {
return obj.SizeFromMap(map);
}
};
class PrototypeInfo::BodyDescriptor final : public BodyDescriptorBase { class PrototypeInfo::BodyDescriptor final : public BodyDescriptorBase {
public: public:
static bool IsValidSlot(Map map, HeapObject obj, int offset) { static bool IsValidSlot(Map map, HeapObject obj, int offset) {
......
...@@ -151,6 +151,7 @@ namespace internal { ...@@ -151,6 +151,7 @@ namespace internal {
V(_, INTERPRETER_DATA_TYPE, InterpreterData, interpreter_data) \ V(_, INTERPRETER_DATA_TYPE, InterpreterData, interpreter_data) \
V(_, MODULE_REQUEST_TYPE, ModuleRequest, module_request) \ V(_, MODULE_REQUEST_TYPE, ModuleRequest, module_request) \
V(_, PROMISE_CAPABILITY_TYPE, PromiseCapability, promise_capability) \ V(_, PROMISE_CAPABILITY_TYPE, PromiseCapability, promise_capability) \
V(_, PROMISE_ON_STACK_TYPE, PromiseOnStack, promise_on_stack) \
V(_, PROMISE_REACTION_TYPE, PromiseReaction, promise_reaction) \ V(_, PROMISE_REACTION_TYPE, PromiseReaction, promise_reaction) \
V(_, PROPERTY_DESCRIPTOR_OBJECT_TYPE, PropertyDescriptorObject, \ V(_, PROPERTY_DESCRIPTOR_OBJECT_TYPE, PropertyDescriptorObject, \
property_descriptor_object) \ property_descriptor_object) \
......
...@@ -183,6 +183,7 @@ ...@@ -183,6 +183,7 @@
// - CallSiteInfo // - CallSiteInfo
// - CodeCache // - CodeCache
// - PropertyDescriptorObject // - PropertyDescriptorObject
// - PromiseOnStack
// - PrototypeInfo // - PrototypeInfo
// - Microtask // - Microtask
// - CallbackTask // - CallbackTask
......
...@@ -5764,7 +5764,7 @@ TEST(AwaitCleansUpGlobalPromiseStack) { ...@@ -5764,7 +5764,7 @@ TEST(AwaitCleansUpGlobalPromiseStack) {
"})();\n"); "})();\n");
CompileRun(source); CompileRun(source);
CHECK_EQ(CcTest::i_isolate()->thread_local_top()->promise_on_stack_, nullptr); CHECK(CcTest::i_isolate()->IsPromiseStackEmpty());
v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr);
CheckDebuggerUnloaded(); CheckDebuggerUnloaded();
......
This diff is collapsed.
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