Index: runtime/vm/debugger.cc |
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc |
index 82511db787418fe52421c7147296fead76518912..06e788026781b828c52f60a0e7291ebd62a41adf 100644 |
--- a/runtime/vm/debugger.cc |
+++ b/runtime/vm/debugger.cc |
@@ -218,6 +218,9 @@ void Breakpoint::PrintJSON(JSONStream* stream) { |
jsobj.AddFixedServiceId("breakpoints/%" Pd "", id()); |
jsobj.AddProperty("breakpointNumber", id()); |
+ if (is_synthetic_async()) { |
+ jsobj.AddProperty("isSyntheticAsyncContinuation", is_synthetic_async()); |
+ } |
jsobj.AddProperty("resolved", bpt_location_->IsResolved()); |
if (bpt_location_->IsResolved()) { |
jsobj.AddLocation(bpt_location_); |
@@ -430,7 +433,8 @@ Breakpoint* BreakpointLocation::AddSingleShot(Debugger* dbg) { |
Breakpoint* BreakpointLocation::AddPerClosure(Debugger* dbg, |
- const Instance& closure) { |
+ const Instance& closure, |
+ bool for_over_await) { |
Breakpoint* bpt = breakpoints(); |
while (bpt != NULL) { |
if (bpt->IsPerClosure() && bpt->closure() == closure.raw()) break; |
@@ -439,6 +443,7 @@ Breakpoint* BreakpointLocation::AddPerClosure(Debugger* dbg, |
if (bpt == NULL) { |
bpt = new Breakpoint(dbg->nextId(), this); |
bpt->SetIsPerClosure(closure); |
+ bpt->set_is_synthetic_async(for_over_await); |
AddBreakpoint(bpt, dbg); |
} |
return bpt; |
@@ -1268,6 +1273,7 @@ Debugger::Debugger() |
stack_trace_(NULL), |
stepping_fp_(0), |
skip_next_step_(false), |
+ synthetic_async_breakpoint_(NULL), |
exc_pause_info_(kNoPauseOnExceptions) { |
} |
@@ -1280,6 +1286,7 @@ Debugger::~Debugger() { |
ASSERT(code_breakpoints_ == NULL); |
ASSERT(stack_trace_ == NULL); |
ASSERT(obj_cache_ == NULL); |
+ ASSERT(synthetic_async_breakpoint_ == NULL); |
} |
@@ -1326,6 +1333,25 @@ static RawFunction* ResolveLibraryFunction( |
} |
+bool Debugger::SetupStepOverAsyncSuspension() { |
+ ActivationFrame* top_frame = TopDartFrame(); |
+ if (!IsAtAsyncJump(top_frame)) { |
+ // Not at an async operation. |
+ return false; |
+ } |
+ Object& closure = Object::Handle(top_frame->GetAsyncOperation()); |
+ ASSERT(!closure.IsNull()); |
+ ASSERT(closure.IsInstance()); |
+ ASSERT(Instance::Cast(closure).IsClosure()); |
+ Breakpoint* bpt = SetBreakpointAtActivation(Instance::Cast(closure), true); |
+ if (bpt == NULL) { |
+ // Unable to set the breakpoint. |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+ |
void Debugger::SetSingleStep() { |
resume_action_ = kSingleStep; |
} |
@@ -2155,7 +2181,8 @@ Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function, |
} |
-Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure) { |
+Breakpoint* Debugger::SetBreakpointAtActivation( |
+ const Instance& closure, bool for_over_await) { |
if (!closure.IsClosure()) { |
return NULL; |
} |
@@ -2165,7 +2192,7 @@ Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure) { |
func.token_pos(), |
func.end_token_pos(), |
-1, -1 /* no line/col */); |
- return bpt_location->AddPerClosure(this, closure); |
+ return bpt_location->AddPerClosure(this, closure, for_over_await); |
} |
@@ -2617,24 +2644,28 @@ void Debugger::SignalPausedEvent(ActivationFrame* top_frame, |
DebuggerEvent event(isolate_, DebuggerEvent::kBreakpointReached); |
event.set_top_frame(top_frame); |
event.set_breakpoint(bpt); |
+ event.set_at_async_jump(IsAtAsyncJump(top_frame)); |
+ Pause(&event); |
+} |
+ |
+ |
+bool Debugger::IsAtAsyncJump(ActivationFrame* top_frame) { |
Object& closure_or_null = Object::Handle(top_frame->GetAsyncOperation()); |
if (!closure_or_null.IsNull()) { |
ASSERT(closure_or_null.IsInstance()); |
ASSERT(Instance::Cast(closure_or_null).IsClosure()); |
- event.set_async_continuation(&closure_or_null); |
const Script& script = Script::Handle(top_frame->SourceScript()); |
const TokenStream& tokens = TokenStream::Handle(script.tokens()); |
TokenStream::Iterator iter(tokens, top_frame->TokenPos()); |
if ((iter.CurrentTokenKind() == Token::kIDENT) && |
((iter.CurrentLiteral() == Symbols::Await().raw()) || |
(iter.CurrentLiteral() == Symbols::YieldKw().raw()))) { |
- event.set_at_async_jump(true); |
+ return true; |
} |
} |
- Pause(&event); |
+ return false; |
} |
- |
RawError* Debugger::DebuggerStepCallback() { |
ASSERT(isolate_->single_step()); |
// Don't pause recursively. |
@@ -2691,7 +2722,14 @@ RawError* Debugger::DebuggerStepCallback() { |
ASSERT(stack_trace_ == NULL); |
stack_trace_ = CollectStackTrace(); |
- SignalPausedEvent(frame, NULL); |
+ // If this step callback is part of stepping over an await statement, |
+ // we saved the synthetic async breakpoint in SignalBpReached. We report |
+ // that we are paused at that breakpoint and then delete it after continuing. |
+ SignalPausedEvent(frame, synthetic_async_breakpoint_); |
+ if (synthetic_async_breakpoint_ != NULL) { |
+ RemoveBreakpoint(synthetic_async_breakpoint_->id()); |
+ synthetic_async_breakpoint_ = NULL; |
+ } |
HandleSteppingRequest(stack_trace_); |
stack_trace_ = NULL; |
@@ -2764,6 +2802,36 @@ RawError* Debugger::SignalBpReached() { |
return Error::null(); |
} |
+ if (bpt_hit->is_synthetic_async()) { |
+ DebuggerStackTrace* stack_trace = CollectStackTrace(); |
+ ASSERT(stack_trace->Length() > 0); |
+ ASSERT(stack_trace_ == NULL); |
+ stack_trace_ = stack_trace; |
+ |
+ // Hit a synthetic async breakpoint. |
+ if (FLAG_verbose_debug) { |
+ OS::Print(">>> hit synthetic breakpoint at %s:%" Pd " " |
+ "(token %s) (address %#" Px ")\n", |
+ String::Handle(cbpt->SourceUrl()).ToCString(), |
+ cbpt->LineNumber(), |
+ cbpt->token_pos().ToCString(), |
+ top_frame->pc()); |
+ } |
+ |
+ ASSERT(synthetic_async_breakpoint_ == NULL); |
+ synthetic_async_breakpoint_ = bpt_hit; |
+ bpt_hit = NULL; |
+ |
+ // We are at the entry of an async function. |
+ // We issue a step over to resume at the point after the await statement. |
+ SetStepOver(); |
+ // When we single step from a user breakpoint, our next stepping |
+ // point will be at the exact same pc. Skip it. |
+ HandleSteppingRequest(stack_trace_, true /* skip next step */); |
+ stack_trace_ = NULL; |
+ return Error::null(); |
+ } |
+ |
if (FLAG_verbose_debug) { |
OS::Print(">>> hit %s breakpoint at %s:%" Pd " " |
"(token %s) (address %#" Px ")\n", |