Commit 754736d2 authored by kozyatinskiy's avatar kozyatinskiy Committed by Commit bot

[inspector] async stacks for Promise.then calls...

... which were done after the promise has been resolved.

Goal of this CL - change promise instrumentation to support better callbacks, chained after promise resolution and prepare instrumentation for adding new asyncTaskCreated instrumentation.

Instrumentation changes:
- asyncTaskScheduled(recurring) when promise is fulfilled or rejected,
- asyncTaskCancelled when promise is collected (since [1] we can be sure that promise will survive scheduled microtasks).

Minor changes:
- async task type in inspector <-> debugger API transferred by enum instead of string,
- Debug manages async task ids based on promise objects.

More details: https://docs.google.com/document/d/1u19N45f1gSF7M39mGsycJEK3IPyJgIXCBnWyiPeuJFE

[1] https://codereview.chromium.org/2581503003/

BUG=chromium:632829,v8:5738
R=dgozman@chromium.org,yangguo@chromium.org,gsathya@chromium.org

Review-Url: https://codereview.chromium.org/2578923002
Cr-Commit-Position: refs/heads/master@{#42178}
parent 0f159f59
...@@ -794,10 +794,9 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context, ...@@ -794,10 +794,9 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Label enqueue(this); Label enqueue(this);
GotoUnless(IsDebugActive(), &enqueue); GotoUnless(IsDebugActive(), &enqueue);
Node* const debug_id = CallRuntime(Runtime::kDebugNextMicrotaskId, context); Node* const debug_id =
CallRuntime(Runtime::kDebugNextAsyncTaskId, context, promise);
Node* const debug_name = SmiConstant(kDebugPromiseResolveThenableJob); Node* const debug_name = SmiConstant(kDebugPromiseResolveThenableJob);
CallRuntime(Runtime::kDebugAsyncTaskEvent, context,
SmiConstant(kDebugEnqueue), debug_id, debug_name);
StoreObjectField(info, PromiseResolveThenableJobInfo::kDebugIdOffset, StoreObjectField(info, PromiseResolveThenableJobInfo::kDebugIdOffset,
debug_id); debug_id);
...@@ -855,13 +854,13 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context, ...@@ -855,13 +854,13 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
void PromiseBuiltinsAssembler::PromiseFulfill( void PromiseBuiltinsAssembler::PromiseFulfill(
Node* context, Node* promise, Node* result, Node* context, Node* promise, Node* result,
v8::Promise::PromiseState status) { v8::Promise::PromiseState status) {
Label do_promisereset(this); Label do_promisereset(this), debug_async_event_enqueue_recurring(this);
Node* const status_smi = SmiConstant(static_cast<int>(status)); Node* const status_smi = SmiConstant(static_cast<int>(status));
Node* const deferred_promise = Node* const deferred_promise =
LoadObjectField(promise, JSPromise::kDeferredPromiseOffset); LoadObjectField(promise, JSPromise::kDeferredPromiseOffset);
GotoIf(IsUndefined(deferred_promise), &do_promisereset); GotoIf(IsUndefined(deferred_promise), &debug_async_event_enqueue_recurring);
Node* const tasks = Node* const tasks =
status == v8::Promise::kFulfilled status == v8::Promise::kFulfilled
...@@ -876,8 +875,17 @@ void PromiseBuiltinsAssembler::PromiseFulfill( ...@@ -876,8 +875,17 @@ void PromiseBuiltinsAssembler::PromiseFulfill(
Node* const info = AllocatePromiseReactionJobInfo( Node* const info = AllocatePromiseReactionJobInfo(
promise, result, tasks, deferred_promise, deferred_on_resolve, promise, result, tasks, deferred_promise, deferred_on_resolve,
deferred_on_reject, context); deferred_on_reject, context);
CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info, status_smi); CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, info, status_smi);
Goto(&do_promisereset); Goto(&debug_async_event_enqueue_recurring);
Bind(&debug_async_event_enqueue_recurring);
{
GotoUnless(IsDebugActive(), &do_promisereset);
CallRuntime(Runtime::kDebugAsyncEventEnqueueRecurring, context, promise,
status_smi);
Goto(&do_promisereset);
}
Bind(&do_promisereset); Bind(&do_promisereset);
{ {
......
...@@ -401,6 +401,7 @@ void Debug::ThreadInit() { ...@@ -401,6 +401,7 @@ void Debug::ThreadInit() {
thread_local_.last_fp_ = 0; thread_local_.last_fp_ = 0;
thread_local_.target_fp_ = 0; thread_local_.target_fp_ = 0;
thread_local_.return_value_ = Handle<Object>(); thread_local_.return_value_ = Handle<Object>();
thread_local_.async_task_count_ = 0;
clear_suspended_generator(); clear_suspended_generator();
// TODO(isolates): frames_are_dropped_? // TODO(isolates): frames_are_dropped_?
base::NoBarrier_Store(&thread_local_.current_debug_scope_, base::NoBarrier_Store(&thread_local_.current_debug_scope_,
...@@ -1778,7 +1779,63 @@ void Debug::OnAfterCompile(Handle<Script> script) { ...@@ -1778,7 +1779,63 @@ void Debug::OnAfterCompile(Handle<Script> script) {
ProcessCompileEvent(v8::AfterCompile, script); ProcessCompileEvent(v8::AfterCompile, script);
} }
void Debug::OnAsyncTaskEvent(PromiseDebugActionType type, int id, namespace {
struct CollectedCallbackData {
Object** location;
int id;
Debug* debug;
Isolate* isolate;
CollectedCallbackData(Object** location, int id, Debug* debug,
Isolate* isolate)
: location(location), id(id), debug(debug), isolate(isolate) {}
};
void SendAsyncTaskEventCancel(const v8::WeakCallbackInfo<void>& info) {
std::unique_ptr<CollectedCallbackData> data(
reinterpret_cast<CollectedCallbackData*>(info.GetParameter()));
if (!data->debug->is_active()) return;
HandleScope scope(data->isolate);
data->debug->OnAsyncTaskEvent(debug::kDebugCancel, data->id,
kDebugPromiseCollected);
}
void ResetPromiseHandle(const v8::WeakCallbackInfo<void>& info) {
CollectedCallbackData* data =
reinterpret_cast<CollectedCallbackData*>(info.GetParameter());
GlobalHandles::Destroy(data->location);
info.SetSecondPassCallback(&SendAsyncTaskEventCancel);
}
} // namespace
int Debug::NextAsyncTaskId(Handle<JSObject> promise) {
LookupIterator it(promise, isolate_->factory()->promise_async_id_symbol());
Maybe<bool> maybe = JSReceiver::HasProperty(&it);
if (maybe.ToChecked()) {
MaybeHandle<Object> result = Object::GetProperty(&it);
return Handle<Smi>::cast(result.ToHandleChecked())->value();
}
Handle<Smi> async_id =
handle(Smi::FromInt(++thread_local_.async_task_count_), isolate_);
Object::SetProperty(&it, async_id, SLOPPY, Object::MAY_BE_STORE_FROM_KEYED)
.ToChecked();
Handle<Object> global_handle = isolate_->global_handles()->Create(*promise);
// We send EnqueueRecurring async task event when promise is fulfilled or
// rejected, WillHandle and DidHandle for every scheduled microtask for this
// promise.
// We need to send a cancel event when no other microtasks can be
// started for this promise and all current microtasks are finished.
// Since we holding promise when at least one microtask is scheduled (inside
// PromiseReactionJobInfo), we can send cancel event in weak callback.
GlobalHandles::MakeWeak(
global_handle.location(),
new CollectedCallbackData(global_handle.location(), async_id->value(),
this, isolate_),
&ResetPromiseHandle, v8::WeakCallbackType::kParameter);
return async_id->value();
}
void Debug::OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
PromiseDebugActionName name) { PromiseDebugActionName name) {
if (in_debug_scope() || ignore_events()) return; if (in_debug_scope() || ignore_events()) return;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "src/base/hashmap.h" #include "src/base/hashmap.h"
#include "src/base/platform/platform.h" #include "src/base/platform/platform.h"
#include "src/debug/debug-interface.h" #include "src/debug/debug-interface.h"
#include "src/debug/interface-types.h"
#include "src/execution.h" #include "src/execution.h"
#include "src/factory.h" #include "src/factory.h"
#include "src/flags.h" #include "src/flags.h"
...@@ -80,14 +81,7 @@ enum PromiseDebugActionName { ...@@ -80,14 +81,7 @@ enum PromiseDebugActionName {
kDebugPromiseResolve, kDebugPromiseResolve,
kDebugPromiseReject, kDebugPromiseReject,
kDebugPromiseResolveThenableJob, kDebugPromiseResolveThenableJob,
}; kDebugPromiseCollected,
enum PromiseDebugActionType {
kDebugEnqueue,
kDebugEnqueueRecurring,
kDebugCancel,
kDebugWillHandle,
kDebugDidHandle,
}; };
class BreakLocation { class BreakLocation {
...@@ -426,7 +420,7 @@ class Debug { ...@@ -426,7 +420,7 @@ class Debug {
void OnPromiseReject(Handle<Object> promise, Handle<Object> value); void OnPromiseReject(Handle<Object> promise, Handle<Object> value);
void OnCompileError(Handle<Script> script); void OnCompileError(Handle<Script> script);
void OnAfterCompile(Handle<Script> script); void OnAfterCompile(Handle<Script> script);
void OnAsyncTaskEvent(PromiseDebugActionType type, int id, void OnAsyncTaskEvent(debug::PromiseDebugActionType type, int id,
PromiseDebugActionName name); PromiseDebugActionName name);
// API facing. // API facing.
...@@ -474,6 +468,8 @@ class Debug { ...@@ -474,6 +468,8 @@ class Debug {
void RecordGenerator(Handle<JSGeneratorObject> generator_object); void RecordGenerator(Handle<JSGeneratorObject> generator_object);
int NextAsyncTaskId(Handle<JSObject> promise);
// Returns whether the operation succeeded. Compilation can only be triggered // Returns whether the operation succeeded. Compilation can only be triggered
// if a valid closure is passed as the second argument, otherwise the shared // if a valid closure is passed as the second argument, otherwise the shared
// function needs to be compiled already. // function needs to be compiled already.
...@@ -721,6 +717,8 @@ class Debug { ...@@ -721,6 +717,8 @@ class Debug {
Handle<Object> return_value_; Handle<Object> return_value_;
Object* suspended_generator_; Object* suspended_generator_;
int async_task_count_;
}; };
// Storage location for registers when handling debug break calls // Storage location for registers when handling debug break calls
......
...@@ -1094,21 +1094,12 @@ function PromiseDebugActionNameToString(name) { ...@@ -1094,21 +1094,12 @@ function PromiseDebugActionNameToString(name) {
case kPromiseResolve: return "Promise.resolve"; case kPromiseResolve: return "Promise.resolve";
case kPromiseReject: return "Promise.reject"; case kPromiseReject: return "Promise.reject";
case kPromiseResolveThenableJob: return "PromiseResolveThenableJob"; case kPromiseResolveThenableJob: return "PromiseResolveThenableJob";
} case kDebugPromiseCollected: return "Promise collected";
}
function PromiseDebugActionTypeToString(type) {
switch (type) {
case kEnqueue: return "enqueue";
case kEnqueueRecurring: return "enqueueRecurring";
case kCancel: return "cancel";
case kWillHandle: return "willHandle";
case kDidHandle: return "didHandle";
} }
} }
function MakeAsyncTaskEvent(type, id, name) { function MakeAsyncTaskEvent(type, id, name) {
return new AsyncTaskEvent(PromiseDebugActionTypeToString(type), return new AsyncTaskEvent(type,
id, PromiseDebugActionNameToString(name)); id, PromiseDebugActionNameToString(name));
} }
......
...@@ -59,6 +59,13 @@ struct WasmDisassembly { ...@@ -59,6 +59,13 @@ struct WasmDisassembly {
OffsetTable offset_table; OffsetTable offset_table;
}; };
enum PromiseDebugActionType {
kDebugEnqueueRecurring,
kDebugCancel,
kDebugWillHandle,
kDebugDidHandle,
};
} // namespace debug } // namespace debug
} // namespace v8 } // namespace v8
......
...@@ -218,6 +218,7 @@ ...@@ -218,6 +218,7 @@
V(promise_debug_marker_symbol) \ V(promise_debug_marker_symbol) \
V(promise_forwarding_handler_symbol) \ V(promise_forwarding_handler_symbol) \
V(promise_handled_by_symbol) \ V(promise_handled_by_symbol) \
V(promise_async_id_symbol) \
V(sealed_symbol) \ V(sealed_symbol) \
V(stack_trace_symbol) \ V(stack_trace_symbol) \
V(strict_function_transition_symbol) \ V(strict_function_transition_symbol) \
......
...@@ -20,11 +20,6 @@ ...@@ -20,11 +20,6 @@
namespace v8_inspector { namespace v8_inspector {
namespace { namespace {
static const char v8AsyncTaskEventEnqueue[] = "enqueue";
static const char v8AsyncTaskEventEnqueueRecurring[] = "enqueueRecurring";
static const char v8AsyncTaskEventWillHandle[] = "willHandle";
static const char v8AsyncTaskEventDidHandle[] = "didHandle";
static const char v8AsyncTaskEventCancel[] = "cancel";
// Based on DevTools frontend measurement, with asyncCallStackDepth = 4, // Based on DevTools frontend measurement, with asyncCallStackDepth = 4,
// average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb // average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb
...@@ -535,27 +530,25 @@ v8::Local<v8::Value> V8Debugger::callInternalGetterFunction( ...@@ -535,27 +530,25 @@ v8::Local<v8::Value> V8Debugger::callInternalGetterFunction(
void V8Debugger::handleV8DebugEvent( void V8Debugger::handleV8DebugEvent(
const v8::debug::EventDetails& eventDetails) { const v8::debug::EventDetails& eventDetails) {
if (!enabled()) return; if (!enabled()) return;
v8::HandleScope scope(m_isolate);
v8::DebugEvent event = eventDetails.GetEvent(); v8::DebugEvent event = eventDetails.GetEvent();
if (event != v8::AsyncTaskEvent && event != v8::Break && if (event != v8::AsyncTaskEvent && event != v8::Break &&
event != v8::Exception && event != v8::AfterCompile && event != v8::Exception && event != v8::AfterCompile &&
event != v8::CompileError) event != v8::CompileError)
return; return;
v8::Local<v8::Context> eventContext = eventDetails.GetEventContext();
DCHECK(!eventContext.IsEmpty());
if (event == v8::AsyncTaskEvent) { if (event == v8::AsyncTaskEvent) {
v8::HandleScope scope(m_isolate); handleV8AsyncTaskEvent(eventDetails.GetEventData());
handleV8AsyncTaskEvent(eventContext, eventDetails.GetExecutionState(),
eventDetails.GetEventData());
return; return;
} }
v8::Local<v8::Context> eventContext = eventDetails.GetEventContext();
DCHECK(!eventContext.IsEmpty());
V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup( V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup(
m_inspector->contextGroupId(eventContext)); m_inspector->contextGroupId(eventContext));
if (!agent) return; if (!agent) return;
v8::HandleScope scope(m_isolate);
if (event == v8::AfterCompile || event == v8::CompileError) { if (event == v8::AfterCompile || event == v8::CompileError) {
v8::Context::Scope contextScope(debuggerContext()); v8::Context::Scope contextScope(debuggerContext());
// Determine if the script is a wasm script. // Determine if the script is a wasm script.
...@@ -603,35 +596,50 @@ void V8Debugger::handleV8DebugEvent( ...@@ -603,35 +596,50 @@ void V8Debugger::handleV8DebugEvent(
} }
} }
void V8Debugger::handleV8AsyncTaskEvent(v8::Local<v8::Context> context, void V8Debugger::handleV8AsyncTaskEvent(v8::Local<v8::Object> eventData) {
v8::Local<v8::Object> executionState,
v8::Local<v8::Object> eventData) {
if (!m_maxAsyncCallStackDepth) return; if (!m_maxAsyncCallStackDepth) return;
String16 type = toProtocolStringWithTypeCheck( // TODO(kozyatinskiy): remove usage of current context as soon as async event
callInternalGetterFunction(eventData, "type")); // is migrated to pure C++ API.
v8::debug::PromiseDebugActionType type =
static_cast<v8::debug::PromiseDebugActionType>(
eventData
->Get(m_isolate->GetCurrentContext(),
toV8StringInternalized(m_isolate, "type_"))
.ToLocalChecked()
->ToInteger(m_isolate->GetCurrentContext())
.ToLocalChecked()
->Value());
String16 name = toProtocolStringWithTypeCheck( String16 name = toProtocolStringWithTypeCheck(
callInternalGetterFunction(eventData, "name")); eventData
int id = static_cast<int>(callInternalGetterFunction(eventData, "id") ->Get(m_isolate->GetCurrentContext(),
->ToInteger(context) toV8StringInternalized(m_isolate, "name_"))
.ToLocalChecked());
int id = static_cast<int>(eventData
->Get(m_isolate->GetCurrentContext(),
toV8StringInternalized(m_isolate, "id_"))
.ToLocalChecked()
->ToInteger(m_isolate->GetCurrentContext())
.ToLocalChecked() .ToLocalChecked()
->Value()); ->Value());
// Async task events from Promises are given misaligned pointers to prevent // Async task events from Promises are given misaligned pointers to prevent
// from overlapping with other Blink task identifiers. There is a single // from overlapping with other Blink task identifiers. There is a single
// namespace of such ids, managed by src/js/promise.js. // namespace of such ids, managed by src/js/promise.js.
void* ptr = reinterpret_cast<void*>(id * 2 + 1); void* ptr = reinterpret_cast<void*>(id * 2 + 1);
if (type == v8AsyncTaskEventEnqueue) switch (type) {
asyncTaskScheduled(name, ptr, false); case v8::debug::kDebugEnqueueRecurring:
else if (type == v8AsyncTaskEventEnqueueRecurring) asyncTaskScheduled(name, ptr, true);
asyncTaskScheduled(name, ptr, true); break;
else if (type == v8AsyncTaskEventWillHandle) case v8::debug::kDebugCancel:
asyncTaskStarted(ptr); asyncTaskCanceled(ptr);
else if (type == v8AsyncTaskEventDidHandle) break;
asyncTaskFinished(ptr); case v8::debug::kDebugWillHandle:
else if (type == v8AsyncTaskEventCancel) asyncTaskStarted(ptr);
asyncTaskCanceled(ptr); break;
else case v8::debug::kDebugDidHandle:
UNREACHABLE(); asyncTaskFinished(ptr);
break;
}
} }
V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
......
...@@ -114,9 +114,7 @@ class V8Debugger { ...@@ -114,9 +114,7 @@ class V8Debugger {
v8::Local<v8::Value> callInternalGetterFunction(v8::Local<v8::Object>, v8::Local<v8::Value> callInternalGetterFunction(v8::Local<v8::Object>,
const char* functionName); const char* functionName);
void handleV8DebugEvent(const v8::debug::EventDetails&); void handleV8DebugEvent(const v8::debug::EventDetails&);
void handleV8AsyncTaskEvent(v8::Local<v8::Context>, void handleV8AsyncTaskEvent(v8::Local<v8::Object> eventData);
v8::Local<v8::Object> executionState,
v8::Local<v8::Object> eventData);
v8::Local<v8::Value> collectionEntries(v8::Local<v8::Context>, v8::Local<v8::Value> collectionEntries(v8::Local<v8::Context>,
v8::Local<v8::Object>); v8::Local<v8::Object>);
......
...@@ -3265,13 +3265,13 @@ class PromiseDebugEventScope { ...@@ -3265,13 +3265,13 @@ class PromiseDebugEventScope {
is_debug_active_(isolate_->debug()->is_active() && is_debug_active_(isolate_->debug()->is_active() &&
id != kDebugPromiseNoID && name_ != kDebugNotActive) { id != kDebugPromiseNoID && name_ != kDebugNotActive) {
if (is_debug_active_) { if (is_debug_active_) {
isolate_->debug()->OnAsyncTaskEvent(kDebugWillHandle, id_, name_); isolate_->debug()->OnAsyncTaskEvent(debug::kDebugWillHandle, id_, name_);
} }
} }
~PromiseDebugEventScope() { ~PromiseDebugEventScope() {
if (is_debug_active_) { if (is_debug_active_) {
isolate_->debug()->OnAsyncTaskEvent(kDebugDidHandle, id_, name_); isolate_->debug()->OnAsyncTaskEvent(debug::kDebugDidHandle, id_, name_);
} }
} }
......
...@@ -405,7 +405,6 @@ typedef List<HeapObject*> DebugObjectCache; ...@@ -405,7 +405,6 @@ typedef List<HeapObject*> DebugObjectCache;
V(AddressToIndexHashMap*, external_reference_map, nullptr) \ V(AddressToIndexHashMap*, external_reference_map, nullptr) \
V(HeapObjectToIndexHashMap*, root_index_map, nullptr) \ V(HeapObjectToIndexHashMap*, root_index_map, nullptr) \
V(int, pending_microtask_count, 0) \ V(int, pending_microtask_count, 0) \
V(int, debug_microtask_count, kDebugPromiseFirstID) \
V(HStatistics*, hstatistics, nullptr) \ V(HStatistics*, hstatistics, nullptr) \
V(CompilationStatistics*, turbo_statistics, nullptr) \ V(CompilationStatistics*, turbo_statistics, nullptr) \
V(HTracer*, htracer, nullptr) \ V(HTracer*, htracer, nullptr) \
...@@ -1106,7 +1105,6 @@ class Isolate { ...@@ -1106,7 +1105,6 @@ class Isolate {
void EnqueueMicrotask(Handle<Object> microtask); void EnqueueMicrotask(Handle<Object> microtask);
void RunMicrotasks(); void RunMicrotasks();
bool IsRunningMicrotasks() const { return is_running_microtasks_; } bool IsRunningMicrotasks() const { return is_running_microtasks_; }
int GetNextDebugMicrotaskId() { return debug_microtask_count_++; }
Handle<Symbol> SymbolFor(Heap::RootListIndex dictionary_index, Handle<Symbol> SymbolFor(Heap::RootListIndex dictionary_index,
Handle<String> name, bool private_symbol); Handle<String> name, bool private_symbol);
......
...@@ -19,8 +19,6 @@ utils.Import(function(from) { ...@@ -19,8 +19,6 @@ utils.Import(function(from) {
AsyncFunctionThrow = from.AsyncFunctionThrow; AsyncFunctionThrow = from.AsyncFunctionThrow;
}); });
var promiseAsyncStackIDSymbol =
utils.ImportNow("promise_async_stack_id_symbol");
var promiseHandledBySymbol = var promiseHandledBySymbol =
utils.ImportNow("promise_handled_by_symbol"); utils.ImportNow("promise_handled_by_symbol");
var promiseForwardingHandlerSymbol = var promiseForwardingHandlerSymbol =
...@@ -119,26 +117,15 @@ function AsyncFunctionPromiseCreate() { ...@@ -119,26 +117,15 @@ function AsyncFunctionPromiseCreate() {
// Push the Promise under construction in an async function on // Push the Promise under construction in an async function on
// the catch prediction stack to handle exceptions thrown before // the catch prediction stack to handle exceptions thrown before
// the first await. // the first await.
%DebugPushPromise(promise);
// Assign ID and create a recurring task to save stack for future // Assign ID and create a recurring task to save stack for future
// resumptions from await. // resumptions from await.
var id = %DebugNextMicrotaskId(); %DebugAsyncFunctionPromiseCreated(promise);
SET_PRIVATE(promise, promiseAsyncStackIDSymbol, id);
%DebugAsyncTaskEvent(kEnqueueRecurring, id, kAsyncFunction);
} }
return promise; return promise;
} }
function AsyncFunctionPromiseRelease(promise) { function AsyncFunctionPromiseRelease(promise) {
if (DEBUG_IS_ACTIVE) { if (DEBUG_IS_ACTIVE) {
// Cancel
var id = GET_PRIVATE(promise, promiseAsyncStackIDSymbol);
// Don't send invalid events when catch prediction is turned on in
// the middle of some async operation.
if (!IS_UNDEFINED(id)) {
%DebugAsyncTaskEvent(kCancel, id, kAsyncFunction);
}
// Pop the Promise under construction in an async function on // Pop the Promise under construction in an async function on
// from catch prediction stack. // from catch prediction stack.
%DebugPopPromise(); %DebugPopPromise();
......
...@@ -211,11 +211,4 @@ define kAsyncFunction = 1; ...@@ -211,11 +211,4 @@ define kAsyncFunction = 1;
define kPromiseResolve = 2; define kPromiseResolve = 2;
define kPromiseReject = 3; define kPromiseReject = 3;
define kPromiseResolveThenableJob = 4; define kPromiseResolveThenableJob = 4;
define kDebugPromiseCollected = 5;
# These values must be kept in sync with PromiseDebugActionType in
# src/debug/debug.h
define kEnqueue = 0;
define kEnqueueRecurring = 1;
define kCancel = 2;
define kWillHandle = 3;
define kDidHandle = 4;
...@@ -1889,24 +1889,43 @@ RUNTIME_FUNCTION(Runtime_DebugPopPromise) { ...@@ -1889,24 +1889,43 @@ RUNTIME_FUNCTION(Runtime_DebugPopPromise) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RUNTIME_FUNCTION(Runtime_DebugNextMicrotaskId) { RUNTIME_FUNCTION(Runtime_DebugNextAsyncTaskId) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(0, args.length()); DCHECK_EQ(1, args.length());
return Smi::FromInt(isolate->GetNextDebugMicrotaskId()); CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
return Smi::FromInt(isolate->debug()->NextAsyncTaskId(promise));
} }
RUNTIME_FUNCTION(Runtime_DebugAsyncTaskEvent) { RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionPromiseCreated) {
DCHECK_EQ(3, args.length()); DCHECK_EQ(1, args.length());
HandleScope scope(isolate); HandleScope scope(isolate);
CONVERT_SMI_ARG_CHECKED(type, 0); CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
CONVERT_SMI_ARG_CHECKED(id, 1); isolate->PushPromise(promise);
CONVERT_SMI_ARG_CHECKED(name, 2); int id = isolate->debug()->NextAsyncTaskId(promise);
isolate->debug()->OnAsyncTaskEvent(static_cast<PromiseDebugActionType>(type), Handle<Symbol> async_stack_id_symbol =
id, isolate->factory()->promise_async_stack_id_symbol();
static_cast<PromiseDebugActionName>(name)); JSObject::SetProperty(promise, async_stack_id_symbol,
handle(Smi::FromInt(id), isolate), STRICT)
.Assert();
isolate->debug()->OnAsyncTaskEvent(debug::kDebugEnqueueRecurring, id,
kDebugAsyncFunction);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RUNTIME_FUNCTION(Runtime_DebugAsyncEventEnqueueRecurring) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
CONVERT_SMI_ARG_CHECKED(status, 1);
if (isolate->debug()->is_active()) {
isolate->debug()->OnAsyncTaskEvent(
debug::kDebugEnqueueRecurring,
isolate->debug()->NextAsyncTaskId(promise),
status == v8::Promise::kFulfilled ? kDebugPromiseResolve
: kDebugPromiseReject);
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugIsActive) { RUNTIME_FUNCTION(Runtime_DebugIsActive) {
SealHandleScope shs(isolate); SealHandleScope shs(isolate);
......
...@@ -43,6 +43,9 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) { ...@@ -43,6 +43,9 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) {
// undefined, which will be interpreted by PromiseRejectEvent // undefined, which will be interpreted by PromiseRejectEvent
// as being a caught exception event. // as being a caught exception event.
rejected_promise = isolate->GetPromiseOnStackOnThrow(); rejected_promise = isolate->GetPromiseOnStackOnThrow();
isolate->debug()->OnAsyncTaskEvent(
debug::kDebugEnqueueRecurring,
isolate->debug()->NextAsyncTaskId(promise), kDebugPromiseReject);
} }
PromiseRejectEvent(isolate, promise, rejected_promise, value, true); PromiseRejectEvent(isolate, promise, rejected_promise, value, true);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
...@@ -115,13 +118,10 @@ void SetDebugInfo(Isolate* isolate, Handle<PromiseReactionJobInfo> info, ...@@ -115,13 +118,10 @@ void SetDebugInfo(Isolate* isolate, Handle<PromiseReactionJobInfo> info,
if (GetDebugIdForAsyncFunction(isolate, info, &id)) { if (GetDebugIdForAsyncFunction(isolate, info, &id)) {
name = kDebugAsyncFunction; name = kDebugAsyncFunction;
} else { } else {
id = isolate->GetNextDebugMicrotaskId(); id = isolate->debug()->NextAsyncTaskId(handle(info->promise(), isolate));
DCHECK(status != v8::Promise::kPending); DCHECK(status != v8::Promise::kPending);
name = status == v8::Promise::kFulfilled ? kDebugPromiseResolve name = status == v8::Promise::kFulfilled ? kDebugPromiseResolve
: kDebugPromiseReject; : kDebugPromiseReject;
isolate->debug()->OnAsyncTaskEvent(kDebugEnqueue, id, name);
} }
info->set_debug_id(id); info->set_debug_id(id);
...@@ -151,6 +151,13 @@ void PromiseSet(Isolate* isolate, Handle<JSPromise> promise, int status, ...@@ -151,6 +151,13 @@ void PromiseSet(Isolate* isolate, Handle<JSPromise> promise, int status,
void PromiseFulfill(Isolate* isolate, Handle<JSPromise> promise, int status, void PromiseFulfill(Isolate* isolate, Handle<JSPromise> promise, int status,
Handle<Object> value) { Handle<Object> value) {
if (isolate->debug()->is_active()) {
isolate->debug()->OnAsyncTaskEvent(
debug::kDebugEnqueueRecurring,
isolate->debug()->NextAsyncTaskId(promise),
status == v8::Promise::kFulfilled ? kDebugPromiseResolve
: kDebugPromiseReject);
}
// Check if there are any callbacks. // Check if there are any callbacks.
if (!promise->deferred_promise()->IsUndefined(isolate)) { if (!promise->deferred_promise()->IsUndefined(isolate)) {
Handle<Object> tasks((status == v8::Promise::kFulfilled) Handle<Object> tasks((status == v8::Promise::kFulfilled)
......
...@@ -199,8 +199,9 @@ namespace internal { ...@@ -199,8 +199,9 @@ namespace internal {
F(DebugRecordGenerator, 1, 1) \ F(DebugRecordGenerator, 1, 1) \
F(DebugPushPromise, 1, 1) \ F(DebugPushPromise, 1, 1) \
F(DebugPopPromise, 0, 1) \ F(DebugPopPromise, 0, 1) \
F(DebugNextMicrotaskId, 0, 1) \ F(DebugNextAsyncTaskId, 1, 1) \
F(DebugAsyncTaskEvent, 3, 1) \ F(DebugAsyncEventEnqueueRecurring, 2, 1) \
F(DebugAsyncFunctionPromiseCreated, 1, 1) \
F(DebugIsActive, 0, 1) \ F(DebugIsActive, 0, 1) \
F(DebugBreakInOptimizedCode, 0, 1) F(DebugBreakInOptimizedCode, 0, 1)
...@@ -290,7 +291,7 @@ namespace internal { ...@@ -290,7 +291,7 @@ namespace internal {
F(CheckIsBootstrapping, 0, 1) \ F(CheckIsBootstrapping, 0, 1) \
F(CreateListFromArrayLike, 1, 1) \ F(CreateListFromArrayLike, 1, 1) \
F(EnqueueMicrotask, 1, 1) \ F(EnqueueMicrotask, 1, 1) \
F(EnqueuePromiseReactionJob, 2, 1) \ F(EnqueuePromiseReactionJob, 3, 1) \
F(EnqueuePromiseResolveThenableJob, 1, 1) \ F(EnqueuePromiseResolveThenableJob, 1, 1) \
F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \ F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \
F(ExportExperimentalFromRuntime, 1, 1) \ F(ExportExperimentalFromRuntime, 1, 1) \
......
Checks async instrumentation enabled in the middle.
Running test: beforeAsyncTaskScheduled
test (test.js:19:2)
(anonymous) (expr1.js:0:0)
test (test.js:21:2)
(anonymous) (expr1.js:0:0)
foo (test.js:10:2)
-- Promise.resolve --
test (test.js:20:2)
(anonymous) (expr1.js:0:0)
foo (test.js:12:2)
-- Promise.resolve --
test (test.js:20:2)
(anonymous) (expr1.js:0:0)
Running test: afterAsyncTaskScheduled
test (test.js:19:2)
(anonymous) (expr1.js:0:0)
test (test.js:21:2)
(anonymous) (expr1.js:0:0)
foo (test.js:10:2)
foo (test.js:12:2)
Running test: afterAsyncTaskStarted
test (test.js:19:2)
(anonymous) (expr1.js:0:0)
test (test.js:21:2)
(anonymous) (expr1.js:0:0)
foo (test.js:10:2)
foo (test.js:12:2)
// Copyright 2016 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.
print('Checks async instrumentation enabled in the middle.');
InspectorTest.addScript(`
function foo() {
// asyncTaskStarted
debugger;
// asyncTaskFinished
debugger;
}
function test() {
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = p1.then(foo);
debugger;
resolve1(); // asyncTaskScheduled
debugger;
return p2;
}
//# sourceURL=test.js`, 7, 26);
InspectorTest.setupScriptMap();
Protocol.Debugger.onPaused(message => {
if (enableOnPause-- === 0)
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
InspectorTest.logCallFrames(message.params.callFrames);
var asyncStackTrace = message.params.asyncStackTrace;
while (asyncStackTrace) {
InspectorTest.log(`-- ${asyncStackTrace.description} --`);
InspectorTest.logCallFrames(asyncStackTrace.callFrames);
asyncStackTrace = asyncStackTrace.parent;
}
InspectorTest.log('');
Protocol.Debugger.resume();
});
Protocol.Debugger.enable();
var enableOnPause = 0;
InspectorTest.runTestSuite([
function beforeAsyncTaskScheduled(next) {
enableOnPause = 0;
Protocol.Runtime.evaluate({ expression: 'test()//# sourceURL=expr1.js',
awaitPromise: true })
.then(() => Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 0 }))
.then(next);
},
function afterAsyncTaskScheduled(next) {
enableOnPause = 2;
Protocol.Runtime.evaluate({ expression: 'test()//# sourceURL=expr1.js',
awaitPromise: true })
.then(() => Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 0 }))
.then(next);
},
function afterAsyncTaskStarted(next) {
enableOnPause = 3;
Protocol.Runtime.evaluate({ expression: 'test()//# sourceURL=expr1.js',
awaitPromise: true })
.then(() => Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 0 }))
.then(next);
}
]);
Checks async stack for late .then handlers with gc
foo1 (test.js:11:2)
-- Promise.resolve --
test (test.js:20:2)
(anonymous) (expr.js:0:0)
foo1 (test.js:11:2)
-- Promise.resolve --
test (test.js:20:2)
(anonymous) (expr.js:0:0)
foo1 (test.js:11:2)
-- Promise.resolve --
test (test.js:20:2)
(anonymous) (expr.js:0:0)
// Copyright 2016 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.
// Flags: --expose-gc
print('Checks async stack for late .then handlers with gc');
InspectorTest.addScript(`
function foo1() {
gc();
debugger;
}
function test() {
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
gc();
var p2 = p1.then(foo1);
gc();
resolve1();
gc();
var p3 = p1.then(foo1);
gc();
var p4 = p1.then(foo1);
gc();
return Promise.all([p2,p3,p4]);
}
//# sourceURL=test.js`, 8, 26);
InspectorTest.setupScriptMap();
Protocol.Debugger.onPaused(message => {
InspectorTest.logCallFrames(message.params.callFrames);
var asyncStackTrace = message.params.asyncStackTrace;
while (asyncStackTrace) {
InspectorTest.log(`-- ${asyncStackTrace.description} --`);
InspectorTest.logCallFrames(asyncStackTrace.callFrames);
asyncStackTrace = asyncStackTrace.parent;
}
InspectorTest.log('');
Protocol.Debugger.resume();
});
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
Protocol.Runtime.evaluate({ expression: 'test()//# sourceURL=expr.js',
awaitPromise: true })
.then(() => Protocol.Runtime.evaluate({ expression: 'gc()'}))
.then(InspectorTest.completeTest);
Checks that async stacks works for async/await
foo2 (test.js:15:2)
-- async function --
foo2 (test.js:13:19)
test (test.js:24:8)
(anonymous) (expr.js:0:0)
foo2 (test.js:17:2)
-- async function --
foo2 (test.js:13:19)
test (test.js:24:8)
(anonymous) (expr.js:0:0)
foo1 (test.js:9:2)
foo2 (test.js:18:8)
-- async function --
foo2 (test.js:13:19)
test (test.js:24:8)
(anonymous) (expr.js:0:0)
foo1 (test.js:9:2)
-- Promise.resolve --
foo2 (test.js:19:30)
-- async function --
foo2 (test.js:13:19)
test (test.js:24:8)
(anonymous) (expr.js:0:0)
foo2 (test.js:20:2)
-- async function --
foo2 (test.js:13:19)
test (test.js:24:8)
(anonymous) (expr.js:0:0)
{
id : <messageId>
result : {
result : {
type : undefined
}
}
}
// Copyright 2016 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.
print('Checks that async stacks works for async/await');
InspectorTest.addScript(`
async function foo1() {
debugger;
return Promise.resolve();
}
async function foo2() {
await Promise.resolve();
debugger;
await Promise.resolve();
debugger;
await foo1();
await Promise.all([ Promise.resolve() ]).then(foo1);
debugger;
}
async function test() {
await foo2();
}
//# sourceURL=test.js`, 7, 26);
InspectorTest.setupScriptMap();
Protocol.Debugger.onPaused(message => {
InspectorTest.logCallFrames(message.params.callFrames);
var asyncStackTrace = message.params.asyncStackTrace;
while (asyncStackTrace) {
InspectorTest.log(`-- ${asyncStackTrace.description} --`);
InspectorTest.logCallFrames(asyncStackTrace.callFrames);
asyncStackTrace = asyncStackTrace.parent;
}
InspectorTest.log('');
Protocol.Debugger.resume();
});
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
Protocol.Runtime.evaluate({ expression: 'test()//# sourceURL=expr.js',
awaitPromise: true })
.then(InspectorTest.logMessage)
.then(InspectorTest.completeTest);
Checks that async chains for promises are correct.
Running test: testPromise
foo1 (test.js:9:2)
-- Promise.resolve --
promise (test.js:20:2)
(anonymous) (testPromise.js:0:0)
Running test: testPromiseResolvedBySetTimeout
foo1 (test.js:9:2)
-- setTimeout --
promiseResolvedBySetTimeout (test.js:28:2)
(anonymous) (testPromiseResolvedBySetTimeout.js:0:0)
Running test: testPromiseAll
foo1 (test.js:9:2)
-- Promise.resolve --
promiseAll (test.js:39:2)
(anonymous) (testPromiseAll.js:0:0)
Running test: testPromiseAllReverseOrder
foo1 (test.js:9:2)
-- Promise.resolve --
promiseAllReverseOrder (test.js:50:2)
(anonymous) (testPromiseAllReverseOrder.js:0:0)
Running test: testPromiseRace
foo1 (test.js:9:2)
-- Promise.resolve --
promiseRace (test.js:60:2)
(anonymous) (testPromiseRace.js:0:0)
Running test: testTwoChainedCallbacks
foo1 (test.js:9:2)
-- Promise.resolve --
twoChainedCallbacks (test.js:69:2)
(anonymous) (testTwoChainedCallbacks.js:0:0)
foo2 (test.js:13:2)
-- Promise.resolve --
twoChainedCallbacks (test.js:69:2)
(anonymous) (testTwoChainedCallbacks.js:0:0)
Running test: testPromiseResolve
foo1 (test.js:9:2)
-- Promise.resolve --
promiseResolve (test.js:74:17)
(anonymous) (testPromiseResolve.js:0:0)
foo2 (test.js:13:2)
-- Promise.resolve --
promiseResolve (test.js:74:17)
(anonymous) (testPromiseResolve.js:0:0)
Running test: testThenableJobResolvedInSetTimeout
foo1 (test.js:9:2)
-- setTimeout --
thenableJob (test.js:81:4)
p1.then (test.js:86:25)
-- Promise.resolve --
thenableJobResolvedInSetTimeout (test.js:87:2)
(anonymous) (testThenableJobResolvedInSetTimeout.js:0:0)
Running test: testThenableJobResolvedInSetTimeoutWithStack
foo1 (test.js:9:2)
-- Promise.resolve --
inner (test.js:94:6)
-- setTimeout --
thenableJob (test.js:99:4)
p1.then (test.js:104:25)
-- Promise.resolve --
thenableJobResolvedInSetTimeoutWithStack (test.js:105:2)
(anonymous) (testThenableJobResolvedInSetTimeoutWithStack.js:0:0)
Running test: testThenableJobResolvedByPromise
foo1 (test.js:9:2)
-- Promise.resolve --
thenableJob (test.js:113:12)
p1.then (test.js:118:25)
-- Promise.resolve --
thenableJobResolvedByPromise (test.js:119:2)
(anonymous) (testThenableJobResolvedByPromise.js:0:0)
Running test: testThenableJobResolvedByPromiseWithStack
foo1 (test.js:9:2)
-- Promise.resolve --
inner (test.js:126:6)
-- Promise.resolve --
thenableJob (test.js:131:12)
p1.then (test.js:136:25)
-- Promise.resolve --
thenableJobResolvedByPromiseWithStack (test.js:137:2)
(anonymous) (testThenableJobResolvedByPromiseWithStack.js:0:0)
Running test: testLateThenCallback
foo1 (test.js:9:2)
-- Promise.resolve --
lateThenCallback (test.js:144:2)
(anonymous) (testLateThenCallback.js:0:0)
Running test: testComplex
inner1 (test.js:154:6)
foo1 (test.js:156:4)
-- Promise.resolve --
inner2 (test.js:162:6)
-- Promise.resolve --
foo2 (test.js:165:12)
-- Promise.resolve --
inner3 (test.js:172:6)
-- setTimeout --
foo3 (test.js:175:4)
-- Promise.resolve --
foo5 (test.js:187:52)
-- Promise.resolve --
foo6 (test.js:192:34)
-- Promise.resolve --
complex (test.js:196:18)
(anonymous) (testComplex.js:0:0)
p.then (test.js:207:8)
-- Promise.resolve --
inner2 (test.js:162:6)
-- Promise.resolve --
foo2 (test.js:165:12)
-- Promise.resolve --
inner3 (test.js:172:6)
-- setTimeout --
foo3 (test.js:175:4)
-- Promise.resolve --
foo5 (test.js:187:52)
-- Promise.resolve --
foo6 (test.js:192:34)
-- Promise.resolve --
complex (test.js:196:18)
(anonymous) (testComplex.js:0:0)
Running test: testReject
foo1 (test.js:9:2)
-- Promise.reject --
reject (test.js:217:17)
(anonymous) (testReject.js:0:0)
// Copyright 2016 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.
print('Checks that async chains for promises are correct.');
InspectorTest.addScript(`
function foo1() {
debugger;
}
function foo2() {
debugger;
}
function promise() {
var resolve;
var p1 = new Promise(r => resolve = r);
var p2 = p1.then(foo1);
resolve();
return p2;
}
function promiseResolvedBySetTimeout() {
var resolve;
var p1 = new Promise(r => resolve = r);
var p2 = p1.then(foo1);
setTimeout(resolve, 0);
return p2;
}
function promiseAll() {
var resolve1;
var resolve2;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = new Promise(resolve => resolve2 = resolve);
var p3 = Promise.all([ p1, p2 ]).then(foo1);
resolve1();
resolve2();
return p3;
}
function promiseAllReverseOrder() {
var resolve1;
var resolve2;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = new Promise(resolve => resolve2 = resolve);
var p3 = Promise.all([ p1, p2 ]).then(foo1);
resolve2();
resolve1();
return p3;
}
function promiseRace() {
var resolve1;
var resolve2;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = new Promise(resolve => resolve2 = resolve);
var p3 = Promise.race([ p1, p2 ]).then(foo1);
resolve1();
resolve2();
return p3;
}
function twoChainedCallbacks() {
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = p1.then(foo1).then(foo2);
resolve1();
return p2;
}
function promiseResolve() {
return Promise.resolve().then(foo1).then(foo2);
}
function thenableJobResolvedInSetTimeout() {
function thenableJob() {
var resolve2;
var p2 = new Promise(resolve => resolve2 = resolve);
setTimeout(resolve2, 0);
return p2;
}
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p3 = p1.then(() => thenableJob()).then(foo1);
resolve1();
return p3;
}
function thenableJobResolvedInSetTimeoutWithStack() {
function thenableJob() {
function inner() {
resolve2();
}
var resolve2;
var p2 = new Promise(resolve => resolve2 = resolve);
setTimeout(inner, 0);
return p2;
}
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p3 = p1.then(() => thenableJob()).then(foo1);
resolve1();
return p3;
}
function thenableJobResolvedByPromise() {
function thenableJob() {
var resolve2;
var p2 = new Promise(resolve => resolve2 = resolve);
Promise.resolve().then(resolve2);
return p2;
}
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p3 = p1.then(() => thenableJob()).then(foo1);
resolve1();
return p3;
}
function thenableJobResolvedByPromiseWithStack() {
function thenableJob() {
function inner() {
resolve2();
}
var resolve2;
var p2 = new Promise(resolve => resolve2 = resolve);
Promise.resolve().then(inner);
return p2;
}
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p3 = p1.then(() => thenableJob()).then(foo1);
resolve1();
return p3;
}
function lateThenCallback() {
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
resolve1();
return p1.then(foo1);
}
function complex() {
var testResolve;
var testPromise = new Promise(resolve => testResolve = resolve);
function foo1() {
function inner1() {
debugger;
}
inner1();
}
function foo2() {
var resolve20;
function inner2() {
resolve20();
}
var p20 = new Promise(resolve => resolve20 = resolve);
Promise.resolve().then(inner2);
return p20;
}
function foo3() {
var resolve17;
function inner3() {
resolve17();
}
var p17 = new Promise(resolve => resolve17 = resolve);
setTimeout(inner3, 0);
return p17;
}
function foo4() {
function inner4() {
return;
}
return inner4();
}
function foo5() {
return Promise.all([ Promise.resolve(), Promise.resolve() ])
.then(() => 42);
}
function foo6() {
return Promise.race([ Promise.resolve(), Promise.resolve()])
.then(() => 42);
}
var p = Promise.resolve()
.then(foo6)
.then(foo5)
.then(foo4)
.then(foo3)
.then(foo2)
.then(foo1);
setTimeout(() => {
p.then(() => {
p.then(() => {
debugger;
testResolve();
})
})
}, 0)
return testPromise;
}
function reject() {
return Promise.reject().catch(foo1);
}
//# sourceURL=test.js`, 7, 26);
InspectorTest.setupScriptMap();
Protocol.Debugger.onPaused(message => {
InspectorTest.logCallFrames(message.params.callFrames);
var asyncStackTrace = message.params.asyncStackTrace;
while (asyncStackTrace) {
InspectorTest.log(`-- ${asyncStackTrace.description} --`);
InspectorTest.logCallFrames(asyncStackTrace.callFrames);
asyncStackTrace = asyncStackTrace.parent;
}
InspectorTest.log('');
Protocol.Debugger.resume();
});
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
var testList = [
'promise',
'promiseResolvedBySetTimeout',
'promiseAll',
'promiseAllReverseOrder',
'promiseRace',
'twoChainedCallbacks',
'promiseResolve',
'thenableJobResolvedInSetTimeout',
'thenableJobResolvedInSetTimeoutWithStack',
'thenableJobResolvedByPromise',
'thenableJobResolvedByPromiseWithStack',
'lateThenCallback',
'complex',
'reject',
]
InspectorTest.runTestSuite(testList.map(name => {
return eval(`
(function test${capitalize(name)}(next) {
Protocol.Runtime.evaluate({ expression: \`${name}()
//# sourceURL=test${capitalize(name)}.js\`, awaitPromise: true})
.then(next);
})
`);
}));
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
...@@ -29,6 +29,18 @@ Running test: testRejectedPromise ...@@ -29,6 +29,18 @@ Running test: testRejectedPromise
stackTrace : { stackTrace : {
callFrames : [ callFrames : [
] ]
parent : {
callFrames : [
[0] : {
columnNumber : 8
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
]
description : Promise.reject
}
} }
text : Uncaught (in promise) text : Uncaught (in promise)
} }
......
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