| 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",
|
|
|