Commit b6c9086c authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

[inspector] reworked async stack instrumentation for async functions

New intstrumentation consists of:
- kAsyncFunctionSuspended when async function is suspended on await
  (called on each await),
- kAsyncFunctionFinished when async function is finished.

Old instrumentation was based on reusing async function promise.
Using this promise produces couple side effects:
- for any promise instrumentation we first need to check if it is
  special case for async function promise or not - it requires
  expensive reading from promise object.
- we capture stack for async functions even if it does not contain
  awaits.
- we do not properly cancel async task created for async function.

New intsrumntation resolved all these problems as well as provide
clear mapping between async task and generator which we can use later
to fetch scope information for async functions on pause.

R=dgozman@chromium.org,yangguo@chromium.org

Bug: v8:7078
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ifdcec947d91e6e3d4d5f9029bc080a19b8e23d41
Reviewed-on: https://chromium-review.googlesource.com/1043096Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarDmitry Gozman <dgozman@chromium.org>
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53445}
parent 9f4e7484
...@@ -4110,7 +4110,7 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate, ...@@ -4110,7 +4110,7 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
{ {
Handle<JSFunction> function = SimpleCreateFunction( Handle<JSFunction> function = SimpleCreateFunction(
isolate, factory->empty_string(), isolate, factory->empty_string(),
Builtins::kAsyncFunctionPromiseRelease, 1, false); Builtins::kAsyncFunctionPromiseRelease, 2, false);
native_context->set_async_function_promise_release(*function); native_context->set_async_function_promise_release(*function);
} }
} }
......
...@@ -120,6 +120,11 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait( ...@@ -120,6 +120,11 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
// TODO(jgruber): Use a faster specialized version of // TODO(jgruber): Use a faster specialized version of
// InternalPerformPromiseThen. // InternalPerformPromiseThen.
Label after_debug_hook(this), call_debug_hook(this, Label::kDeferred);
GotoIf(IsDebugActive(), &call_debug_hook);
Goto(&after_debug_hook);
BIND(&after_debug_hook);
Await(context, generator, awaited, outer_promise, AwaitContext::kLength, Await(context, generator, awaited, outer_promise, AwaitContext::kLength,
init_closure_context, Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN, init_closure_context, Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN,
...@@ -128,6 +133,10 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait( ...@@ -128,6 +133,10 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
// Return outer promise to avoid adding an load of the outer promise before // Return outer promise to avoid adding an load of the outer promise before
// suspending in BytecodeGenerator. // suspending in BytecodeGenerator.
Return(outer_promise); Return(outer_promise);
BIND(&call_debug_hook);
CallRuntime(Runtime::kDebugAsyncFunctionSuspended, context, outer_promise);
Goto(&after_debug_hook);
} }
// Called by the parser from the desugaring of 'await' when catch // Called by the parser from the desugaring of 'await' when catch
...@@ -177,15 +186,13 @@ TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) { ...@@ -177,15 +186,13 @@ TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) {
// 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.
// Assign ID and create a recurring task to save stack for future CallRuntime(Runtime::kDebugPushPromise, context, promise);
// resumptions from await.
CallRuntime(Runtime::kDebugAsyncFunctionPromiseCreated, context, promise);
Return(promise); Return(promise);
} }
} }
TF_BUILTIN(AsyncFunctionPromiseRelease, AsyncFunctionBuiltinsAssembler) { TF_BUILTIN(AsyncFunctionPromiseRelease, AsyncFunctionBuiltinsAssembler) {
CSA_ASSERT_JS_ARGC_EQ(this, 1); CSA_ASSERT_JS_ARGC_EQ(this, 2);
Node* const promise = Parameter(Descriptor::kPromise); Node* const promise = Parameter(Descriptor::kPromise);
Node* const context = Parameter(Descriptor::kContext); Node* const context = Parameter(Descriptor::kContext);
...@@ -199,7 +206,8 @@ TF_BUILTIN(AsyncFunctionPromiseRelease, AsyncFunctionBuiltinsAssembler) { ...@@ -199,7 +206,8 @@ TF_BUILTIN(AsyncFunctionPromiseRelease, AsyncFunctionBuiltinsAssembler) {
{ {
// 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.
CallRuntime(Runtime::kDebugPopPromise, context); CallRuntime(Runtime::kDebugAsyncFunctionFinished, context,
Parameter(Descriptor::kCanSuspend), promise);
Return(promise); Return(promise);
} }
} }
......
...@@ -99,9 +99,8 @@ Node* AsyncBuiltinsAssembler::Await( ...@@ -99,9 +99,8 @@ Node* AsyncBuiltinsAssembler::Await(
// Add PromiseHooks if needed // Add PromiseHooks if needed
Label next(this); Label next(this);
GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &next); GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &next);
CallRuntime(Runtime::kPromiseHookInit, context, wrapped_value, CallRuntime(Runtime::kAwaitPromisesInit, context, wrapped_value,
outer_promise); outer_promise, throwaway);
CallRuntime(Runtime::kPromiseHookInit, context, throwaway, wrapped_value);
Goto(&next); Goto(&next);
BIND(&next); BIND(&next);
} }
......
...@@ -421,7 +421,7 @@ namespace internal { ...@@ -421,7 +421,7 @@ namespace internal {
TFJ(AsyncFunctionAwaitRejectClosure, 1, kSentError) \ TFJ(AsyncFunctionAwaitRejectClosure, 1, kSentError) \
TFJ(AsyncFunctionAwaitResolveClosure, 1, kSentValue) \ TFJ(AsyncFunctionAwaitResolveClosure, 1, kSentValue) \
TFJ(AsyncFunctionPromiseCreate, 0) \ TFJ(AsyncFunctionPromiseCreate, 0) \
TFJ(AsyncFunctionPromiseRelease, 1, kPromise) \ TFJ(AsyncFunctionPromiseRelease, 2, kPromise, kCanSuspend) \
\ \
/* BigInt */ \ /* BigInt */ \
CPP(BigIntConstructor) \ CPP(BigIntConstructor) \
......
...@@ -171,8 +171,8 @@ MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* isolate, ...@@ -171,8 +171,8 @@ MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* isolate,
class DebugDelegate { class DebugDelegate {
public: public:
virtual ~DebugDelegate() {} virtual ~DebugDelegate() {}
virtual void PromiseEventOccurred(debug::PromiseDebugActionType type, int id, virtual void AsyncEventOccurred(debug::DebugAsyncActionType type, int id,
bool is_blackboxed) {} bool is_blackboxed) {}
virtual void ScriptCompiled(v8::Local<Script> script, bool is_live_edited, virtual void ScriptCompiled(v8::Local<Script> script, bool is_live_edited,
bool has_compile_error) {} bool has_compile_error) {}
// |inspector_break_points_hit| contains id of breakpoints installed with // |inspector_break_points_hit| contains id of breakpoints installed with
......
...@@ -1734,7 +1734,7 @@ MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script, ...@@ -1734,7 +1734,7 @@ MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script,
} }
MaybeHandle<Object> Debug::MakeAsyncTaskEvent( MaybeHandle<Object> Debug::MakeAsyncTaskEvent(
v8::debug::PromiseDebugActionType type, int id) { v8::debug::DebugAsyncActionType type, int id) {
// Create the async task event object. // Create the async task event object.
Handle<Object> argv[] = {Handle<Smi>(Smi::FromInt(type), isolate_), Handle<Object> argv[] = {Handle<Smi>(Smi::FromInt(type), isolate_),
Handle<Smi>(Smi::FromInt(id), isolate_)}; Handle<Smi>(Smi::FromInt(id), isolate_)};
...@@ -1771,6 +1771,15 @@ void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) { ...@@ -1771,6 +1771,15 @@ void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
} }
} }
void Debug::OnAsyncFunctionStateChanged(Handle<JSPromise> promise,
debug::DebugAsyncActionType event) {
if (in_debug_scope() || ignore_events()) return;
if (!debug_delegate_) return;
PostponeInterruptsScope no_interrupts(isolate_);
int id = NextAsyncTaskId(promise);
debug_delegate_->AsyncEventOccurred(event, id, false);
}
namespace { namespace {
v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) { v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) {
Handle<Context> context = isolate->debug()->debugger_entry()->GetContext(); Handle<Context> context = isolate->debug()->debugger_entry()->GetContext();
...@@ -1909,39 +1918,6 @@ void Debug::OnAfterCompile(Handle<Script> script) { ...@@ -1909,39 +1918,6 @@ void Debug::OnAfterCompile(Handle<Script> script) {
ProcessCompileEvent(v8::AfterCompile, script); ProcessCompileEvent(v8::AfterCompile, script);
} }
namespace {
// In an async function, reuse the existing stack related to the outer
// Promise. Otherwise, e.g. in a direct call to then, save a new stack.
// Promises with multiple reactions with one or more of them being async
// functions will not get a good stack trace, as async functions require
// different stacks from direct Promise use, but we save and restore a
// stack once for all reactions.
//
// If this isn't a case of async function, we return false, otherwise
// we set the correct id and return true.
//
// TODO(littledan): Improve this case.
int GetReferenceAsyncTaskId(Isolate* isolate, Handle<JSPromise> promise) {
Handle<Symbol> handled_by_symbol =
isolate->factory()->promise_handled_by_symbol();
Handle<Object> handled_by_promise =
JSObject::GetDataProperty(promise, handled_by_symbol);
if (!handled_by_promise->IsJSPromise()) {
return isolate->debug()->NextAsyncTaskId(promise);
}
Handle<JSPromise> handled_by_promise_js =
Handle<JSPromise>::cast(handled_by_promise);
Handle<Symbol> async_stack_id_symbol =
isolate->factory()->promise_async_stack_id_symbol();
Handle<Object> async_task_id =
JSObject::GetDataProperty(handled_by_promise_js, async_stack_id_symbol);
if (!async_task_id->IsSmi()) {
return isolate->debug()->NextAsyncTaskId(promise);
}
return Handle<Smi>::cast(async_task_id)->value();
}
} // namespace
void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise, void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise,
Handle<Object> parent) { Handle<Object> parent) {
if (hook_type == PromiseHookType::kResolve) return; if (hook_type == PromiseHookType::kResolve) return;
...@@ -1949,14 +1925,17 @@ void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise, ...@@ -1949,14 +1925,17 @@ void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise,
if (!debug_delegate_) return; if (!debug_delegate_) return;
PostponeInterruptsScope no_interrupts(isolate_); PostponeInterruptsScope no_interrupts(isolate_);
int id = GetReferenceAsyncTaskId(isolate_, promise);
if (hook_type == PromiseHookType::kBefore) { if (hook_type == PromiseHookType::kBefore) {
debug_delegate_->PromiseEventOccurred(debug::kDebugWillHandle, id, false); if (!promise->async_task_id()) return;
debug_delegate_->AsyncEventOccurred(debug::kDebugWillHandle,
promise->async_task_id(), false);
} else if (hook_type == PromiseHookType::kAfter) { } else if (hook_type == PromiseHookType::kAfter) {
debug_delegate_->PromiseEventOccurred(debug::kDebugDidHandle, id, false); if (!promise->async_task_id()) return;
debug_delegate_->AsyncEventOccurred(debug::kDebugDidHandle,
promise->async_task_id(), false);
} else { } else {
DCHECK(hook_type == PromiseHookType::kInit); DCHECK(hook_type == PromiseHookType::kInit);
debug::PromiseDebugActionType type = debug::kDebugPromiseThen; debug::DebugAsyncActionType type = debug::kDebugPromiseThen;
bool last_frame_was_promise_builtin = false; bool last_frame_was_promise_builtin = false;
JavaScriptFrameIterator it(isolate_); JavaScriptFrameIterator it(isolate_);
while (!it.done()) { while (!it.done()) {
...@@ -1967,18 +1946,15 @@ void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise, ...@@ -1967,18 +1946,15 @@ void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise,
if (info->IsUserJavaScript()) { if (info->IsUserJavaScript()) {
// We should not report PromiseThen and PromiseCatch which is called // We should not report PromiseThen and PromiseCatch which is called
// indirectly, e.g. Promise.all calls Promise.then internally. // indirectly, e.g. Promise.all calls Promise.then internally.
if (type == debug::kDebugAsyncFunctionPromiseCreated || if (last_frame_was_promise_builtin) {
last_frame_was_promise_builtin) { int id = NextAsyncTaskId(promise);
debug_delegate_->PromiseEventOccurred(type, id, IsBlackboxed(info)); debug_delegate_->AsyncEventOccurred(type, id, IsBlackboxed(info));
} }
return; return;
} }
last_frame_was_promise_builtin = false; last_frame_was_promise_builtin = false;
if (info->HasBuiltinId()) { if (info->HasBuiltinId()) {
if (info->builtin_id() == Builtins::kAsyncFunctionPromiseCreate) { if (info->builtin_id() == Builtins::kPromisePrototypeThen) {
type = debug::kDebugAsyncFunctionPromiseCreated;
last_frame_was_promise_builtin = true;
} else if (info->builtin_id() == Builtins::kPromisePrototypeThen) {
type = debug::kDebugPromiseThen; type = debug::kDebugPromiseThen;
last_frame_was_promise_builtin = true; last_frame_was_promise_builtin = true;
} else if (info->builtin_id() == Builtins::kPromisePrototypeCatch) { } else if (info->builtin_id() == Builtins::kPromisePrototypeCatch) {
...@@ -1995,19 +1971,11 @@ void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise, ...@@ -1995,19 +1971,11 @@ void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise,
} }
} }
int Debug::NextAsyncTaskId(Handle<JSObject> promise) { int Debug::NextAsyncTaskId(Handle<JSPromise> promise) {
LookupIterator it(promise, isolate_->factory()->promise_async_id_symbol()); if (!promise->async_task_id()) {
Maybe<bool> maybe = JSReceiver::HasProperty(&it); promise->set_async_task_id(++thread_local_.async_task_count_);
if (maybe.ToChecked()) {
MaybeHandle<Object> result = Object::GetProperty(&it);
return Handle<Smi>::cast(result.ToHandleChecked())->value();
} }
Handle<Smi> async_id = return promise->async_task_id();
handle(Smi::FromInt(++thread_local_.async_task_count_), isolate_);
Object::SetProperty(&it, async_id, LanguageMode::kSloppy,
Object::MAY_BE_STORE_FROM_KEYED)
.ToChecked();
return async_id->value();
} }
namespace { namespace {
...@@ -2494,8 +2462,8 @@ bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) { ...@@ -2494,8 +2462,8 @@ bool Debug::PerformSideEffectCheckForObject(Handle<Object> object) {
return false; return false;
} }
void LegacyDebugDelegate::PromiseEventOccurred( void LegacyDebugDelegate::AsyncEventOccurred(
v8::debug::PromiseDebugActionType type, int id, bool is_blackboxed) { v8::debug::DebugAsyncActionType type, int id, bool is_blackboxed) {
DebugScope debug_scope(isolate_->debug()); DebugScope debug_scope(isolate_->debug());
if (debug_scope.failed()) return; if (debug_scope.failed()) return;
HandleScope scope(isolate_); HandleScope scope(isolate_);
......
...@@ -225,6 +225,9 @@ class Debug { ...@@ -225,6 +225,9 @@ class Debug {
void OnCompileError(Handle<Script> script); void OnCompileError(Handle<Script> script);
void OnAfterCompile(Handle<Script> script); void OnAfterCompile(Handle<Script> script);
void OnAsyncFunctionStateChanged(Handle<JSPromise> promise,
debug::DebugAsyncActionType);
V8_WARN_UNUSED_RESULT MaybeHandle<Object> Call(Handle<Object> fun, V8_WARN_UNUSED_RESULT MaybeHandle<Object> Call(Handle<Object> fun,
Handle<Object> data); Handle<Object> data);
Handle<Context> GetDebugContext(); Handle<Context> GetDebugContext();
...@@ -279,7 +282,7 @@ class Debug { ...@@ -279,7 +282,7 @@ class Debug {
void RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise, void RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise,
Handle<Object> parent); Handle<Object> parent);
int NextAsyncTaskId(Handle<JSObject> promise); int NextAsyncTaskId(Handle<JSPromise> promise);
bool IsBlackboxed(Handle<SharedFunctionInfo> shared); bool IsBlackboxed(Handle<SharedFunctionInfo> shared);
...@@ -459,7 +462,7 @@ class Debug { ...@@ -459,7 +462,7 @@ class Debug {
V8_WARN_UNUSED_RESULT MaybeHandle<Object> MakeCompileEvent( V8_WARN_UNUSED_RESULT MaybeHandle<Object> MakeCompileEvent(
Handle<Script> script, v8::DebugEvent type); Handle<Script> script, v8::DebugEvent type);
V8_WARN_UNUSED_RESULT MaybeHandle<Object> MakeAsyncTaskEvent( V8_WARN_UNUSED_RESULT MaybeHandle<Object> MakeAsyncTaskEvent(
v8::debug::PromiseDebugActionType type, int id); v8::debug::DebugAsyncActionType type, int id);
void ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script); void ProcessCompileEvent(v8::DebugEvent event, Handle<Script> script);
void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data); void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data);
...@@ -623,8 +626,8 @@ class Debug { ...@@ -623,8 +626,8 @@ class Debug {
class LegacyDebugDelegate : public v8::debug::DebugDelegate { class LegacyDebugDelegate : public v8::debug::DebugDelegate {
public: public:
explicit LegacyDebugDelegate(Isolate* isolate) : isolate_(isolate) {} explicit LegacyDebugDelegate(Isolate* isolate) : isolate_(isolate) {}
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, void AsyncEventOccurred(v8::debug::DebugAsyncActionType type, int id,
bool is_blackboxed) override; bool is_blackboxed) override;
void ScriptCompiled(v8::Local<v8::debug::Script> script, bool is_live_edited, void ScriptCompiled(v8::Local<v8::debug::Script> script, bool is_live_edited,
bool has_compile_error) override; bool has_compile_error) override;
void BreakProgramRequested(v8::Local<v8::Context> paused_context, void BreakProgramRequested(v8::Local<v8::Context> paused_context,
......
...@@ -69,13 +69,14 @@ struct WasmDisassembly { ...@@ -69,13 +69,14 @@ struct WasmDisassembly {
OffsetTable offset_table; OffsetTable offset_table;
}; };
enum PromiseDebugActionType { enum DebugAsyncActionType {
kDebugAsyncFunctionPromiseCreated,
kDebugPromiseThen, kDebugPromiseThen,
kDebugPromiseCatch, kDebugPromiseCatch,
kDebugPromiseFinally, kDebugPromiseFinally,
kDebugWillHandle, kDebugWillHandle,
kDebugDidHandle, kDebugDidHandle,
kAsyncFunctionSuspended,
kAsyncFunctionFinished
}; };
enum BreakLocationType { enum BreakLocationType {
......
...@@ -604,15 +604,12 @@ bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script, ...@@ -604,15 +604,12 @@ bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
return hasAgents && allBlackboxed; return hasAgents && allBlackboxed;
} }
void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, void V8Debugger::AsyncEventOccurred(v8::debug::DebugAsyncActionType type,
int id, bool isBlackboxed) { int id, bool isBlackboxed) {
// 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. // from overlapping with other Blink task identifiers.
void* task = reinterpret_cast<void*>(id * 2 + 1); void* task = reinterpret_cast<void*>(id * 2 + 1);
switch (type) { switch (type) {
case v8::debug::kDebugAsyncFunctionPromiseCreated:
asyncTaskScheduledForStack("async function", task, true);
break;
case v8::debug::kDebugPromiseThen: case v8::debug::kDebugPromiseThen:
asyncTaskScheduledForStack("Promise.then", task, false); asyncTaskScheduledForStack("Promise.then", task, false);
if (!isBlackboxed) asyncTaskCandidateForStepping(task, true); if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
...@@ -633,6 +630,14 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, ...@@ -633,6 +630,14 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
asyncTaskFinishedForStack(task); asyncTaskFinishedForStack(task);
asyncTaskFinishedForStepping(task); asyncTaskFinishedForStepping(task);
break; break;
case v8::debug::kAsyncFunctionSuspended:
if (m_asyncTaskStacks.find(task) == m_asyncTaskStacks.end()) {
asyncTaskScheduledForStack("async function", task, true);
}
break;
case v8::debug::kAsyncFunctionFinished:
asyncTaskCanceledForStack(task);
break;
} }
} }
......
...@@ -170,8 +170,8 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -170,8 +170,8 @@ class V8Debugger : public v8::debug::DebugDelegate {
void asyncTaskCanceledForStepping(void* task); void asyncTaskCanceledForStepping(void* task);
// v8::debug::DebugEventListener implementation. // v8::debug::DebugEventListener implementation.
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, void AsyncEventOccurred(v8::debug::DebugAsyncActionType type, int id,
bool isBlackboxed) override; bool isBlackboxed) override;
void ScriptCompiled(v8::Local<v8::debug::Script> script, bool is_live_edited, void ScriptCompiled(v8::Local<v8::debug::Script> script, bool is_live_edited,
bool has_compile_error) override; bool has_compile_error) override;
void BreakProgramRequested( void BreakProgramRequested(
......
...@@ -15984,6 +15984,14 @@ const char* JSPromise::Status(v8::Promise::PromiseState status) { ...@@ -15984,6 +15984,14 @@ const char* JSPromise::Status(v8::Promise::PromiseState status) {
UNREACHABLE(); UNREACHABLE();
} }
int JSPromise::async_task_id() const {
return AsyncTaskIdField::decode(flags());
}
void JSPromise::set_async_task_id(int id) {
set_flags(AsyncTaskIdField::update(flags(), id));
}
// static // static
Handle<Object> JSPromise::Fulfill(Handle<JSPromise> promise, Handle<Object> JSPromise::Fulfill(Handle<JSPromise> promise,
Handle<Object> value) { Handle<Object> value) {
......
...@@ -45,6 +45,9 @@ class JSPromise : public JSObject { ...@@ -45,6 +45,9 @@ class JSPromise : public JSObject {
// block in an async function. // block in an async function.
DECL_BOOLEAN_ACCESSORS(handled_hint) DECL_BOOLEAN_ACCESSORS(handled_hint)
int async_task_id() const;
void set_async_task_id(int id);
static const char* Status(Promise::PromiseState status); static const char* Status(Promise::PromiseState status);
Promise::PromiseState status() const; Promise::PromiseState status() const;
void set_status(Promise::PromiseState status); void set_status(Promise::PromiseState status);
...@@ -77,6 +80,7 @@ class JSPromise : public JSObject { ...@@ -77,6 +80,7 @@ class JSPromise : public JSObject {
static const int kStatusBits = 2; static const int kStatusBits = 2;
static const int kHasHandlerBit = 2; static const int kHasHandlerBit = 2;
static const int kHandledHintBit = 3; static const int kHandledHintBit = 3;
class AsyncTaskIdField : public BitField<int, kHandledHintBit + 1, 22> {};
static const int kStatusShift = 0; static const int kStatusShift = 0;
static const int kStatusMask = 0x3; static const int kStatusMask = 0x3;
......
...@@ -407,6 +407,7 @@ class ParserBase { ...@@ -407,6 +407,7 @@ class ParserBase {
void AddSuspend() { suspend_count_++; } void AddSuspend() { suspend_count_++; }
int suspend_count() const { return suspend_count_; } int suspend_count() const { return suspend_count_; }
bool CanSuspend() const { return suspend_count_ > 0; }
FunctionKind kind() const { return scope()->function_kind(); } FunctionKind kind() const { return scope()->function_kind(); }
......
...@@ -2970,11 +2970,14 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block) { ...@@ -2970,11 +2970,14 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block) {
// There is no TryCatchFinally node, so wrap it in an outer try/finally // There is no TryCatchFinally node, so wrap it in an outer try/finally
Block* outer_try_block = IgnoreCompletion(try_catch_statement); Block* outer_try_block = IgnoreCompletion(try_catch_statement);
// finally { %AsyncFunctionPromiseRelease(.promise) } // finally { %AsyncFunctionPromiseRelease(.promise, can_suspend) }
Block* finally_block; Block* finally_block;
{ {
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone()); ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
args->Add(factory()->NewVariableProxy(PromiseVariable()), zone()); args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
args->Add(factory()->NewBooleanLiteral(function_state_->CanSuspend(),
kNoSourcePosition),
zone());
Expression* call_promise_release = factory()->NewCallRuntime( Expression* call_promise_release = factory()->NewCallRuntime(
Context::ASYNC_FUNCTION_PROMISE_RELEASE_INDEX, args, kNoSourcePosition); Context::ASYNC_FUNCTION_PROMISE_RELEASE_INDEX, args, kNoSourcePosition);
Statement* promise_release = factory()->NewExpressionStatement( Statement* promise_release = factory()->NewExpressionStatement(
......
...@@ -1718,21 +1718,6 @@ RUNTIME_FUNCTION(Runtime_DebugPopPromise) { ...@@ -1718,21 +1718,6 @@ RUNTIME_FUNCTION(Runtime_DebugPopPromise) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionPromiseCreated) {
DCHECK_EQ(1, args.length());
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
isolate->PushPromise(promise);
int id = isolate->debug()->NextAsyncTaskId(promise);
Handle<Symbol> async_stack_id_symbol =
isolate->factory()->promise_async_stack_id_symbol();
JSObject::SetProperty(promise, async_stack_id_symbol,
handle(Smi::FromInt(id), isolate),
LanguageMode::kStrict)
.Assert();
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugIsActive) { RUNTIME_FUNCTION(Runtime_DebugIsActive) {
SealHandleScope shs(isolate); SealHandleScope shs(isolate);
return Smi::FromInt(isolate->debug()->is_active()); return Smi::FromInt(isolate->debug()->is_active());
...@@ -1843,5 +1828,27 @@ RUNTIME_FUNCTION(Runtime_IncBlockCounter) { ...@@ -1843,5 +1828,27 @@ RUNTIME_FUNCTION(Runtime_IncBlockCounter) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionSuspended) {
DCHECK_EQ(1, args.length());
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
isolate->debug()->OnAsyncFunctionStateChanged(promise,
debug::kAsyncFunctionSuspended);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionFinished) {
DCHECK_EQ(2, args.length());
HandleScope scope(isolate);
CONVERT_BOOLEAN_ARG_CHECKED(has_suspend, 0);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 1);
isolate->PopPromise();
if (has_suspend) {
isolate->debug()->OnAsyncFunctionStateChanged(
promise, debug::kAsyncFunctionFinished);
}
return isolate->heap()->undefined_value();
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -112,6 +112,23 @@ RUNTIME_FUNCTION(Runtime_PromiseHookInit) { ...@@ -112,6 +112,23 @@ RUNTIME_FUNCTION(Runtime_PromiseHookInit) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RUNTIME_FUNCTION(Runtime_AwaitPromisesInit) {
DCHECK_EQ(3, args.length());
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, wrapped_value, 0);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, outer_promise, 1);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, throwaway, 2);
isolate->RunPromiseHook(PromiseHookType::kInit, wrapped_value, outer_promise);
isolate->RunPromiseHook(PromiseHookType::kInit, throwaway, wrapped_value);
// On inspector side we capture async stack trace and store it by
// outer_promise->async_task_id when async function is suspended first time.
// To use captured stack trace later throwaway promise should have the same
// async_task_id as outer_promise since we generate WillHandle and DidHandle
// events using throwaway promise.
throwaway->set_async_task_id(outer_promise->async_task_id());
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_PromiseHookBefore) { RUNTIME_FUNCTION(Runtime_PromiseHookBefore) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
......
...@@ -132,7 +132,6 @@ namespace internal { ...@@ -132,7 +132,6 @@ namespace internal {
F(ClearStepping, 0, 1) \ F(ClearStepping, 0, 1) \
F(CollectGarbage, 1, 1) \ F(CollectGarbage, 1, 1) \
F(DebugApplyInstrumentation, 1, 1) \ F(DebugApplyInstrumentation, 1, 1) \
F(DebugAsyncFunctionPromiseCreated, 1, 1) \
F(DebugBreakAtEntry, 1, 1) \ F(DebugBreakAtEntry, 1, 1) \
F(DebugCollectCoverage, 0, 1) \ F(DebugCollectCoverage, 0, 1) \
F(DebugConstructedBy, 2, 1) \ F(DebugConstructedBy, 2, 1) \
...@@ -150,6 +149,8 @@ namespace internal { ...@@ -150,6 +149,8 @@ namespace internal {
F(DebugPropertyAttributesFromDetails, 1, 1) \ F(DebugPropertyAttributesFromDetails, 1, 1) \
F(DebugPropertyKindFromDetails, 1, 1) \ F(DebugPropertyKindFromDetails, 1, 1) \
F(DebugPushPromise, 1, 1) \ F(DebugPushPromise, 1, 1) \
F(DebugAsyncFunctionSuspended, 1, 1) \
F(DebugAsyncFunctionFinished, 2, 1) \
F(DebugReferencedBy, 3, 1) \ F(DebugReferencedBy, 3, 1) \
F(DebugSetScriptSource, 2, 1) \ F(DebugSetScriptSource, 2, 1) \
F(DebugToggleBlockCoverage, 1, 1) \ F(DebugToggleBlockCoverage, 1, 1) \
...@@ -426,6 +427,7 @@ namespace internal { ...@@ -426,6 +427,7 @@ namespace internal {
F(PromiseHookAfter, 1, 1) \ F(PromiseHookAfter, 1, 1) \
F(PromiseHookBefore, 1, 1) \ F(PromiseHookBefore, 1, 1) \
F(PromiseHookInit, 2, 1) \ F(PromiseHookInit, 2, 1) \
F(AwaitPromisesInit, 3, 1) \
F(PromiseMarkAsHandled, 1, 1) \ F(PromiseMarkAsHandled, 1, 1) \
F(PromiseRejectEventFromStack, 2, 1) \ F(PromiseRejectEventFromStack, 2, 1) \
F(PromiseResult, 1, 1) \ F(PromiseResult, 1, 1) \
......
...@@ -16,7 +16,7 @@ snippet: " ...@@ -16,7 +16,7 @@ snippet: "
" "
frame size: 23 frame size: 23
parameter count: 1 parameter count: 1
bytecode array length: 508 bytecode array length: 514
bytecodes: [ bytecodes: [
B(SwitchOnGeneratorState), R(2), U8(0), U8(3), B(SwitchOnGeneratorState), R(2), U8(0), U8(3),
B(Mov), R(closure), R(12), B(Mov), R(closure), R(12),
...@@ -219,7 +219,10 @@ bytecodes: [ ...@@ -219,7 +219,10 @@ bytecodes: [
B(LdaTheHole), B(LdaTheHole),
B(SetPendingMessage), B(SetPendingMessage),
B(Star), R(14), B(Star), R(14),
B(CallJSRuntime), U8(%async_function_promise_release), R(11), U8(1), B(LdaTrue),
B(Star), R(16),
B(Mov), R(11), R(15),
B(CallJSRuntime), U8(%async_function_promise_release), R(15), U8(2),
B(Ldar), R(14), B(Ldar), R(14),
B(SetPendingMessage), B(SetPendingMessage),
B(Ldar), R(12), B(Ldar), R(12),
...@@ -266,7 +269,7 @@ snippet: " ...@@ -266,7 +269,7 @@ snippet: "
" "
frame size: 23 frame size: 23
parameter count: 1 parameter count: 1
bytecode array length: 537 bytecode array length: 543
bytecodes: [ bytecodes: [
B(SwitchOnGeneratorState), R(2), U8(0), U8(3), B(SwitchOnGeneratorState), R(2), U8(0), U8(3),
B(Mov), R(closure), R(12), B(Mov), R(closure), R(12),
...@@ -474,7 +477,10 @@ bytecodes: [ ...@@ -474,7 +477,10 @@ bytecodes: [
B(LdaTheHole), B(LdaTheHole),
B(SetPendingMessage), B(SetPendingMessage),
B(Star), R(14), B(Star), R(14),
B(CallJSRuntime), U8(%async_function_promise_release), R(11), U8(1), B(LdaTrue),
B(Star), R(16),
B(Mov), R(11), R(15),
B(CallJSRuntime), U8(%async_function_promise_release), R(15), U8(2),
B(Ldar), R(14), B(Ldar), R(14),
B(SetPendingMessage), B(SetPendingMessage),
B(Ldar), R(12), B(Ldar), R(12),
...@@ -532,7 +538,7 @@ snippet: " ...@@ -532,7 +538,7 @@ snippet: "
" "
frame size: 23 frame size: 23
parameter count: 1 parameter count: 1
bytecode array length: 526 bytecode array length: 532
bytecodes: [ bytecodes: [
B(SwitchOnGeneratorState), R(2), U8(0), U8(3), B(SwitchOnGeneratorState), R(2), U8(0), U8(3),
B(Mov), R(closure), R(12), B(Mov), R(closure), R(12),
...@@ -743,7 +749,10 @@ bytecodes: [ ...@@ -743,7 +749,10 @@ bytecodes: [
B(LdaTheHole), B(LdaTheHole),
B(SetPendingMessage), B(SetPendingMessage),
B(Star), R(14), B(Star), R(14),
B(CallJSRuntime), U8(%async_function_promise_release), R(11), U8(1), B(LdaTrue),
B(Star), R(16),
B(Mov), R(11), R(15),
B(CallJSRuntime), U8(%async_function_promise_release), R(15), U8(2),
B(Ldar), R(14), B(Ldar), R(14),
B(SetPendingMessage), B(SetPendingMessage),
B(Ldar), R(12), B(Ldar), R(12),
...@@ -791,7 +800,7 @@ snippet: " ...@@ -791,7 +800,7 @@ snippet: "
" "
frame size: 20 frame size: 20
parameter count: 1 parameter count: 1
bytecode array length: 397 bytecode array length: 403
bytecodes: [ bytecodes: [
/* 16 E> */ B(StackCheck), /* 16 E> */ B(StackCheck),
B(CallJSRuntime), U8(%async_function_promise_create), R(0), U8(0), B(CallJSRuntime), U8(%async_function_promise_create), R(0), U8(0),
...@@ -947,7 +956,10 @@ bytecodes: [ ...@@ -947,7 +956,10 @@ bytecodes: [
B(LdaTheHole), B(LdaTheHole),
B(SetPendingMessage), B(SetPendingMessage),
B(Star), R(12), B(Star), R(12),
B(CallJSRuntime), U8(%async_function_promise_release), R(9), U8(1), B(LdaFalse),
B(Star), R(14),
B(Mov), R(9), R(13),
B(CallJSRuntime), U8(%async_function_promise_release), R(13), U8(2),
B(Ldar), R(12), B(Ldar), R(12),
B(SetPendingMessage), B(SetPendingMessage),
B(Ldar), R(10), B(Ldar), R(10),
......
...@@ -926,7 +926,7 @@ snippet: " ...@@ -926,7 +926,7 @@ snippet: "
" "
frame size: 23 frame size: 23
parameter count: 2 parameter count: 2
bytecode array length: 357 bytecode array length: 363
bytecodes: [ bytecodes: [
/* 16 E> */ B(StackCheck), /* 16 E> */ B(StackCheck),
B(CallJSRuntime), U8(%async_function_promise_create), R(0), U8(0), B(CallJSRuntime), U8(%async_function_promise_create), R(0), U8(0),
...@@ -1074,7 +1074,10 @@ bytecodes: [ ...@@ -1074,7 +1074,10 @@ bytecodes: [
B(LdaTheHole), B(LdaTheHole),
B(SetPendingMessage), B(SetPendingMessage),
B(Star), R(15), B(Star), R(15),
B(CallJSRuntime), U8(%async_function_promise_release), R(12), U8(1), B(LdaFalse),
B(Star), R(17),
B(Mov), R(12), R(16),
B(CallJSRuntime), U8(%async_function_promise_release), R(16), U8(2),
B(Ldar), R(15), B(Ldar), R(15),
B(SetPendingMessage), B(SetPendingMessage),
B(Ldar), R(13), B(Ldar), R(13),
...@@ -1116,7 +1119,7 @@ snippet: " ...@@ -1116,7 +1119,7 @@ snippet: "
" "
frame size: 23 frame size: 23
parameter count: 2 parameter count: 2
bytecode array length: 408 bytecode array length: 414
bytecodes: [ bytecodes: [
B(SwitchOnGeneratorState), R(2), U8(0), U8(1), B(SwitchOnGeneratorState), R(2), U8(0), U8(1),
B(Mov), R(closure), R(12), B(Mov), R(closure), R(12),
...@@ -1282,7 +1285,10 @@ bytecodes: [ ...@@ -1282,7 +1285,10 @@ bytecodes: [
B(LdaTheHole), B(LdaTheHole),
B(SetPendingMessage), B(SetPendingMessage),
B(Star), R(14), B(Star), R(14),
B(CallJSRuntime), U8(%async_function_promise_release), R(11), U8(1), B(LdaTrue),
B(Star), R(16),
B(Mov), R(11), R(15),
B(CallJSRuntime), U8(%async_function_promise_release), R(15), U8(2),
B(Ldar), R(14), B(Ldar), R(14),
B(SetPendingMessage), B(SetPendingMessage),
B(Ldar), R(12), B(Ldar), R(12),
......
...@@ -381,7 +381,7 @@ snippet: " ...@@ -381,7 +381,7 @@ snippet: "
" "
frame size: 12 frame size: 12
parameter count: 1 parameter count: 1
bytecode array length: 134 bytecode array length: 140
bytecodes: [ bytecodes: [
/* 16 E> */ B(StackCheck), /* 16 E> */ B(StackCheck),
B(CallJSRuntime), U8(%async_function_promise_create), R(0), U8(0), B(CallJSRuntime), U8(%async_function_promise_create), R(0), U8(0),
...@@ -436,7 +436,10 @@ bytecodes: [ ...@@ -436,7 +436,10 @@ bytecodes: [
B(LdaTheHole), B(LdaTheHole),
B(SetPendingMessage), B(SetPendingMessage),
B(Star), R(6), B(Star), R(6),
B(CallJSRuntime), U8(%async_function_promise_release), R(3), U8(1), B(LdaFalse),
B(Star), R(8),
B(Mov), R(3), R(7),
B(CallJSRuntime), U8(%async_function_promise_release), R(7), U8(2),
B(Ldar), R(6), B(Ldar), R(6),
B(SetPendingMessage), B(SetPendingMessage),
B(Ldar), R(4), B(Ldar), R(4),
...@@ -468,7 +471,7 @@ snippet: " ...@@ -468,7 +471,7 @@ snippet: "
" "
frame size: 11 frame size: 11
parameter count: 1 parameter count: 1
bytecode array length: 185 bytecode array length: 191
bytecodes: [ bytecodes: [
B(SwitchOnGeneratorState), R(1), U8(0), U8(1), B(SwitchOnGeneratorState), R(1), U8(0), U8(1),
B(Mov), R(closure), R(3), B(Mov), R(closure), R(3),
...@@ -541,7 +544,10 @@ bytecodes: [ ...@@ -541,7 +544,10 @@ bytecodes: [
B(LdaTheHole), B(LdaTheHole),
B(SetPendingMessage), B(SetPendingMessage),
B(Star), R(5), B(Star), R(5),
B(CallJSRuntime), U8(%async_function_promise_release), R(2), U8(1), B(LdaTrue),
B(Star), R(7),
B(Mov), R(2), R(6),
B(CallJSRuntime), U8(%async_function_promise_release), R(6), U8(2),
B(Ldar), R(5), B(Ldar), R(5),
B(SetPendingMessage), B(SetPendingMessage),
B(Ldar), R(3), B(Ldar), R(3),
......
Checks that async chains for for-await-of are correct. Checks that async chains for for-await-of are correct.
Running test: testBasic Running test: testBasic
Debugger (test.js:12:2) Debugger (test.js:10:2)
Basic (test.js:50:4) Basic (test.js:48:4)
-- async function -- -- async function --
Basic (test.js:48:20) Basic (test.js:47:19)
(anonymous) (testBasic.js:0:0) (anonymous) (testBasic.js:0:0)
Running test: testUncaughtReject Running test: testUncaughtReject
Debugger (test.js:12:2) Debugger (test.js:10:2)
-- async function -- -- Promise.catch --
UncaughtReject (test.js:54:29) UncaughtReject (test.js:58:21)
(anonymous) (testUncaughtReject.js:0:0) (anonymous) (testUncaughtReject.js:0:0)
Running test: testUncaughtThrow Running test: testUncaughtThrow
Debugger (test.js:12:2) Debugger (test.js:10:2)
-- async function -- -- Promise.catch --
UncaughtThrow (test.js:63:28) UncaughtThrow (test.js:67:21)
(anonymous) (testUncaughtThrow.js:0:0) (anonymous) (testUncaughtThrow.js:0:0)
Running test: testCaughtReject Running test: testCaughtReject
Debugger (test.js:12:2) Debugger (test.js:10:2)
CaughtReject (test.js:78:4) CaughtReject (test.js:76:4)
-- async function -- -- async function --
CaughtReject (test.js:72:27) CaughtReject (test.js:72:21)
(anonymous) (testCaughtReject.js:0:0) (anonymous) (testCaughtReject.js:0:0)
Running test: testCaughtThrow Running test: testCaughtThrow
Debugger (test.js:12:2) Debugger (test.js:10:2)
CaughtThrow (test.js:88:4) CaughtThrow (test.js:86:4)
-- async function -- -- async function --
CaughtThrow (test.js:82:26) CaughtThrow (test.js:82:21)
(anonymous) (testCaughtThrow.js:0:0) (anonymous) (testCaughtThrow.js:0:0)
Running test: testUncaughtRejectOnBreak Running test: testUncaughtRejectOnBreak
Running test: testUncaughtThrowOnBreak Running test: testUncaughtThrowOnBreak
Debugger (test.js:12:2) Debugger (test.js:10:2)
-- async function -- -- Promise.catch --
UncaughtThrowOnBreak (test.js:101:35) UncaughtThrowOnBreak (test.js:105:21)
(anonymous) (testUncaughtThrowOnBreak.js:0:0) (anonymous) (testUncaughtThrowOnBreak.js:0:0)
Running test: testCaughtRejectOnBreak Running test: testCaughtRejectOnBreak
Running test: testCaughtThrowOnBreak Running test: testCaughtThrowOnBreak
Debugger (test.js:12:2) Debugger (test.js:10:2)
CaughtThrowOnBreak (test.js:126:4) CaughtThrowOnBreak (test.js:124:4)
-- async function -- -- async function --
CaughtThrowOnBreak (test.js:120:33) CaughtThrowOnBreak (test.js:120:21)
(anonymous) (testCaughtThrowOnBreak.js:0:0) (anonymous) (testCaughtThrowOnBreak.js:0:0)
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
let {session, contextGroup, Protocol} = InspectorTest.start('Checks that async chains for for-await-of are correct.'); let {session, contextGroup, Protocol} = InspectorTest.start('Checks that async chains for for-await-of are correct.');
contextGroup.addScript(` contextGroup.addInlineScript(`
function Debugger(value) { function Debugger(value) {
debugger; debugger;
...@@ -48,7 +48,7 @@ async function Basic() { ...@@ -48,7 +48,7 @@ async function Basic() {
Debugger(); Debugger();
} }
} }
// TODO(kozyatinskiy): this stack trace is suspicious.
async function UncaughtReject() { async function UncaughtReject() {
async function loop() { async function loop() {
for await (let x of [Reject(new Error("boop"))]) { for await (let x of [Reject(new Error("boop"))]) {
...@@ -57,7 +57,7 @@ async function UncaughtReject() { ...@@ -57,7 +57,7 @@ async function UncaughtReject() {
} }
return loop().catch(Debugger); return loop().catch(Debugger);
} }
// TODO(kozyatinskiy): this stack trace is suspicious.
async function UncaughtThrow() { async function UncaughtThrow() {
async function loop() { async function loop() {
for await (let x of [Throw(new Error("boop"))]) { for await (let x of [Throw(new Error("boop"))]) {
...@@ -86,7 +86,7 @@ async function CaughtThrow() { ...@@ -86,7 +86,7 @@ async function CaughtThrow() {
Debugger(e); Debugger(e);
} }
} }
// TODO(kozyatinskiy): this stack trace is suspicious.
async function UncaughtRejectOnBreak() { async function UncaughtRejectOnBreak() {
async function loop() { async function loop() {
for await (let x of RejectOnReturn(["0", "1"])) { for await (let x of RejectOnReturn(["0", "1"])) {
...@@ -95,7 +95,7 @@ async function UncaughtRejectOnBreak() { ...@@ -95,7 +95,7 @@ async function UncaughtRejectOnBreak() {
} }
return loop().catch(Debugger); return loop().catch(Debugger);
} }
// TODO(kozyatinskiy): this stack trace is suspicious.
async function UncaughtThrowOnBreak() { async function UncaughtThrowOnBreak() {
async function loop() { async function loop() {
for await (let x of ThrowOnReturn(["0", "1"])) { for await (let x of ThrowOnReturn(["0", "1"])) {
...@@ -104,7 +104,7 @@ async function UncaughtThrowOnBreak() { ...@@ -104,7 +104,7 @@ async function UncaughtThrowOnBreak() {
} }
return loop().catch(Debugger); return loop().catch(Debugger);
} }
// TODO(kozyatinskiy): this stack trace is suspicious.
async function CaughtRejectOnBreak() { async function CaughtRejectOnBreak() {
try { try {
for await (let x of RejectOnReturn(["0", "1"])) { for await (let x of RejectOnReturn(["0", "1"])) {
...@@ -123,8 +123,7 @@ async function CaughtThrowOnBreak() { ...@@ -123,8 +123,7 @@ async function CaughtThrowOnBreak() {
} catch (e) { } catch (e) {
Debugger(e); Debugger(e);
} }
} }`, 'test.js');
//# sourceURL=test.js`, 9, 26);
session.setupScriptMap(); session.setupScriptMap();
Protocol.Debugger.onPaused(message => { Protocol.Debugger.onPaused(message => {
......
stepOut async function
bar (test.js:22:2)
-- async function --
bar (test.js:21:16)
foo (test.js:17:8)
-- async function --
foo (test.js:16:16)
test (test.js:12:8)
-- async function --
test (test.js:11:16)
(anonymous) (:0:0)
foo (test.js:18:0)
-- async function --
foo (test.js:16:16)
test (test.js:12:8)
-- async function --
test (test.js:11:16)
(anonymous) (:0:0)
test (test.js:13:0)
-- async function --
test (test.js:11:16)
(anonymous) (:0:0)
// Copyright 2018 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.
let {session, contextGroup, Protocol} =
InspectorTest.start('stepOut async function');
session.setupScriptMap();
contextGroup.addInlineScript(`
async function test() {
await Promise.resolve();
await foo();
}
async function foo() {
await Promise.resolve();
await bar();
}
async function bar() {
await Promise.resolve();
debugger;
}
`, 'test.js');
(async function test() {
Protocol.Runtime.enable();
Protocol.Runtime.onConsoleAPICalled(
msg => InspectorTest.log(msg.params.args[0].value));
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
let finished =
Protocol.Runtime.evaluate({expression: 'test()', awaitPromise: true})
.then(() => false);
while (true) {
const r = await Promise.race([finished, waitPauseAndDumpStack()]);
if (!r) break;
Protocol.Debugger.stepOut();
}
InspectorTest.completeTest();
})()
async function
waitPauseAndDumpStack() {
const {params} = await Protocol.Debugger.oncePaused();
session.logCallFrames(params.callFrames);
session.logAsyncStackTrace(params.asyncStackTrace);
InspectorTest.log('');
return true;
}
Checks that async stacks works for async/await Checks that async stacks works for async/await
foo2 (test.js:15:2) foo2 (test.js:15:2)
-- async function -- -- async function --
foo2 (test.js:13:19) foo2 (test.js:14:16)
test (test.js:24:8) test (test.js:24:8)
(anonymous) (expr.js:0:0) (anonymous) (expr.js:0:0)
foo2 (test.js:17:2) foo2 (test.js:17:2)
-- async function -- -- async function --
foo2 (test.js:13:19) foo2 (test.js:14:16)
test (test.js:24:8) test (test.js:24:8)
(anonymous) (expr.js:0:0) (anonymous) (expr.js:0:0)
foo1 (test.js:9:2) foo1 (test.js:9:2)
foo2 (test.js:18:8) foo2 (test.js:18:8)
-- async function -- -- async function --
foo2 (test.js:13:19) foo2 (test.js:14:16)
test (test.js:24:8) test (test.js:24:8)
(anonymous) (expr.js:0:0) (anonymous) (expr.js:0:0)
...@@ -22,13 +22,13 @@ foo1 (test.js:9:2) ...@@ -22,13 +22,13 @@ foo1 (test.js:9:2)
-- Promise.then -- -- Promise.then --
foo2 (test.js:19:43) foo2 (test.js:19:43)
-- async function -- -- async function --
foo2 (test.js:13:19) foo2 (test.js:14:16)
test (test.js:24:8) test (test.js:24:8)
(anonymous) (expr.js:0:0) (anonymous) (expr.js:0:0)
foo2 (test.js:20:2) foo2 (test.js:20:2)
-- async function -- -- async function --
foo2 (test.js:13:19) foo2 (test.js:14:16)
test (test.js:24:8) test (test.js:24:8)
(anonymous) (expr.js:0:0) (anonymous) (expr.js:0:0)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
let {session, contextGroup, Protocol} = InspectorTest.start('Checks that async stacks works for async/await'); let {session, contextGroup, Protocol} = InspectorTest.start('Checks that async stacks works for async/await');
contextGroup.addScript(` contextGroup.addInlineScript(`
async function foo1() { async function foo1() {
debugger; debugger;
return Promise.resolve(); return Promise.resolve();
...@@ -22,8 +22,7 @@ async function foo2() { ...@@ -22,8 +22,7 @@ async function foo2() {
async function test() { async function test() {
await foo2(); await foo2();
} }`, 'test.js');
//# sourceURL=test.js`, 7, 26);
session.setupScriptMap(); session.setupScriptMap();
Protocol.Debugger.onPaused(message => { Protocol.Debugger.onPaused(message => {
......
...@@ -8,7 +8,7 @@ asyncFact (test.js:9:2) ...@@ -8,7 +8,7 @@ asyncFact (test.js:9:2)
asyncFact (test.js:11:2) asyncFact (test.js:11:2)
-- async function -- -- async function --
asyncFact (test.js:8:24) asyncFact (test.js:10:20)
asyncFact (test.js:10:20) asyncFact (test.js:10:20)
asyncFact (test.js:10:20) asyncFact (test.js:10:20)
asyncFact (test.js:10:20) asyncFact (test.js:10:20)
...@@ -23,7 +23,7 @@ asyncFact (test.js:9:2) ...@@ -23,7 +23,7 @@ asyncFact (test.js:9:2)
asyncFact (test.js:11:2) asyncFact (test.js:11:2)
-- async function -- -- async function --
asyncFact (test.js:8:24) asyncFact (test.js:10:20)
(anonymous) (expr.js:0:0) (anonymous) (expr.js:0:0)
......
...@@ -120,6 +120,12 @@ InspectorTest.ContextGroup = class { ...@@ -120,6 +120,12 @@ InspectorTest.ContextGroup = class {
utils.compileAndRunWithOrigin(this.id, string, url || '', lineOffset || 0, columnOffset || 0, false); utils.compileAndRunWithOrigin(this.id, string, url || '', lineOffset || 0, columnOffset || 0, false);
} }
addInlineScript(string, url) {
const match = (new Error().stack).split('\n')[2].match(/([0-9]+):([0-9]+)/);
this.addScript(
string, match[1] * 1, match[1] * 1 + '.addInlineScript('.length, url);
}
addModule(string, url, lineOffset, columnOffset) { addModule(string, url, lineOffset, columnOffset) {
utils.compileAndRunWithOrigin(this.id, string, url, lineOffset || 0, columnOffset || 0, true); utils.compileAndRunWithOrigin(this.id, string, url, lineOffset || 0, columnOffset || 0, true);
} }
...@@ -213,7 +219,8 @@ InspectorTest.Session = class { ...@@ -213,7 +219,8 @@ InspectorTest.Session = class {
logCallFrames(callFrames) { logCallFrames(callFrames) {
for (var frame of callFrames) { for (var frame of callFrames) {
var functionName = frame.functionName || '(anonymous)'; var functionName = frame.functionName || '(anonymous)';
var url = frame.url ? frame.url : this._scriptMap.get(frame.location.scriptId).url; var scriptId = frame.location ? frame.location.scriptId : frame.scriptId;
var url = frame.url ? frame.url : this._scriptMap.get(scriptId).url;
var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber; var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber;
var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber; var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber;
InspectorTest.log(`${functionName} (${url}:${lineNumber}:${columnNumber})`); InspectorTest.log(`${functionName} (${url}:${lineNumber}:${columnNumber})`);
......
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