[vm/debugger] Simplify async breakpoints implementation

We no longer rewrite suspendable function into closures nested
inside the original function, so the whole synthetic async breakpoint
machinery is not needed.

Refactor `Breakpoint` class to make one-shot and per-closure
separate properties of breakpoint, which allows creating
one-shot-per-closure breakpoints.

TEST=existing service tests

Change-Id: I208afe13f36efa40f5746e44867bd24684cf5f03
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/310601
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Reviewed-by: Derek Xu <derekx@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
This commit is contained in:
Vyacheslav Egorov 2023-06-26 22:45:10 +00:00 committed by Commit Queue
parent d1ecf01f71
commit 4a6f9328a2
13 changed files with 109 additions and 200 deletions

View file

@ -1,3 +1,7 @@
## 11.7.3
- Update to version `4.10` of the spec.
- Add deprecation notice to `Breakpoint.isSyntheticAsyncContinuation`.
## 11.7.2 ## 11.7.2
- Make Event classes abstract to permit `dap` event stream extensions. - Make Event classes abstract to permit `dap` event stream extensions.

View file

@ -1 +1 @@
version=4.9 version=4.10

View file

@ -28,7 +28,7 @@ export 'snapshot_graph.dart'
HeapSnapshotObjectNoData, HeapSnapshotObjectNoData,
HeapSnapshotObjectNullData; HeapSnapshotObjectNullData;
const String vmServiceVersion = '4.9.0'; const String vmServiceVersion = '4.10.0';
/// @optional /// @optional
const String optional = 'optional'; const String optional = 'optional';
@ -3347,8 +3347,7 @@ class Breakpoint extends Obj {
/// Has this breakpoint been assigned to a specific program location? /// Has this breakpoint been assigned to a specific program location?
bool? resolved; bool? resolved;
/// Is this a breakpoint that was added synthetically as part of a step /// Note: this property is deprecated and is always absent from the response.
/// OverAsyncSuspension resume command?
@optional @optional
bool? isSyntheticAsyncContinuation; bool? isSyntheticAsyncContinuation;

View file

@ -1,5 +1,5 @@
name: vm_service name: vm_service
version: 11.7.2 version: 11.7.3
description: >- description: >-
A library to communicate with a service implementing the Dart VM A library to communicate with a service implementing the Dart VM
service protocol. service protocol.

View file

@ -8,26 +8,37 @@ import 'dart:developer';
import 'service_test_common.dart'; import 'service_test_common.dart';
import 'test_helper.dart'; import 'test_helper.dart';
const LINE_A = 21; // AUTOGENERATED START
const LINE_B = 22; //
const LINE_C = 23; // Update these constants by running:
const LINE_D = 28; //
const LINE_E = 29; // dart runtime/observatory/tests/service/update_line_numbers.dart <test.dart>
const LINE_F = 30; //
const int LINE_A = 27;
const int LINE_B = 28;
const int LINE_C = 29;
const int LINE_0 = 38;
const int LINE_D = 39;
const int LINE_E = 40;
const int LINE_F = 41;
// AUTOGENERATED END
const LINE_0 = 27; Future<int> helper() async {
helper() async {
await null; // LINE_A. await null; // LINE_A.
print('helper'); // LINE_B. print('line b'); // LINE_B.
print('foobar'); // LINE_C. print('line c'); // LINE_C.
return 0;
} }
testMain() async { Future<void> testMain() async {
int process(int value) {
return value + 1;
}
debugger(); // LINE_0. debugger(); // LINE_0.
print('mmmmm'); // LINE_D. print('line d'); // LINE_D.
await helper(); // LINE_E. await helper().then(process); // LINE_E.
print('z'); // LINE_F. print('line f'); // LINE_F.
} }
var tests = <IsolateTest>[ var tests = <IsolateTest>[

View file

@ -12,7 +12,7 @@ var tests = <VMTest>[
final result = await vm.invokeRpcNoUpgrade('getVersion', {}); final result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], 'Version'); expect(result['type'], 'Version');
expect(result['major'], 4); expect(result['major'], 4);
expect(result['minor'], 9); expect(result['minor'], 10);
expect(result['_privateMajor'], 0); expect(result['_privateMajor'], 0);
expect(result['_privateMinor'], 0); expect(result['_privateMinor'], 0);
}, },

View file

@ -8,26 +8,36 @@ import 'dart:developer';
import 'service_test_common.dart'; import 'service_test_common.dart';
import 'test_helper.dart'; import 'test_helper.dart';
const LINE_A = 21; // AUTOGENERATED START
const LINE_B = 22; //
const LINE_C = 23; // Update these constants by running:
const LINE_D = 28; //
const LINE_E = 29; // dart runtime/observatory/tests/service/update_line_numbers.dart <test.dart>
const LINE_F = 30; //
const int LINE_A = 27;
const LINE_0 = 27; const int LINE_B = 28;
const int LINE_C = 29;
const int LINE_0 = 37;
const int LINE_D = 38;
const int LINE_E = 39;
const int LINE_F = 40;
// AUTOGENERATED END
helper() async { helper() async {
await null; // LINE_A. await null; // LINE_A.
print('helper'); // LINE_B. print('line b'); // LINE_B.
print('foobar'); // LINE_C. print('line c'); // LINE_C.
} }
testMain() async { testMain() async {
int process(int value) {
return value + 1;
}
debugger(); // LINE_0. debugger(); // LINE_0.
print('mmmmm'); // LINE_D. print('line d'); // LINE_D.
await helper(); // LINE_E. await helper().then(process); // LINE_E.
print('z'); // LINE_F. print('line f'); // LINE_F.
} }
var tests = <IsolateTest>[ var tests = <IsolateTest>[

View file

@ -12,7 +12,7 @@ var tests = <VMTest>[
final result = await vm.invokeRpcNoUpgrade('getVersion', {}); final result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], equals('Version')); expect(result['type'], equals('Version'));
expect(result['major'], equals(4)); expect(result['major'], equals(4));
expect(result['minor'], equals(9)); expect(result['minor'], equals(10));
expect(result['_privateMajor'], equals(0)); expect(result['_privateMajor'], equals(0));
expect(result['_privateMinor'], equals(0)); expect(result['_privateMinor'], equals(0));
}, },

View file

@ -195,9 +195,6 @@ void Breakpoint::PrintJSON(JSONStream* stream) {
jsobj.AddFixedServiceId("breakpoints/%" Pd "", id()); jsobj.AddFixedServiceId("breakpoints/%" Pd "", id());
jsobj.AddProperty("enabled", enabled_); jsobj.AddProperty("enabled", enabled_);
jsobj.AddProperty("breakpointNumber", id()); jsobj.AddProperty("breakpointNumber", id());
if (is_synthetic_async()) {
jsobj.AddProperty("isSyntheticAsyncContinuation", is_synthetic_async());
}
jsobj.AddProperty("resolved", bpt_location_->IsResolved()); jsobj.AddProperty("resolved", bpt_location_->IsResolved());
if (bpt_location_->IsResolved()) { if (bpt_location_->IsResolved()) {
jsobj.AddLocation(bpt_location_); jsobj.AddLocation(bpt_location_);
@ -354,51 +351,26 @@ void BreakpointLocation::AddBreakpoint(Breakpoint* bpt, Debugger* dbg) {
} }
Breakpoint* BreakpointLocation::AddRepeated(Debugger* dbg) { Breakpoint* BreakpointLocation::AddRepeated(Debugger* dbg) {
Breakpoint* bpt = breakpoints(); return AddBreakpoint(dbg, Closure::Handle(), /*single_shot=*/false);
while (bpt != nullptr) {
if (bpt->IsRepeated()) break;
bpt = bpt->next();
}
if (bpt == nullptr) {
bpt = new Breakpoint(dbg->nextId(), this);
bpt->SetIsRepeated();
AddBreakpoint(bpt, dbg);
}
return bpt;
} }
Breakpoint* BreakpointLocation::AddSingleShot(Debugger* dbg) { Breakpoint* BreakpointLocation::AddSingleShot(Debugger* dbg) {
return AddBreakpoint(dbg, Closure::Handle(), /*single_shot=*/true);
}
Breakpoint* BreakpointLocation::AddBreakpoint(Debugger* dbg,
const Closure& closure,
bool single_shot) {
Breakpoint* bpt = breakpoints(); Breakpoint* bpt = breakpoints();
while (bpt != nullptr) { while (bpt != nullptr) {
if (bpt->IsSingleShot()) break; if ((bpt->closure() == closure.ptr()) &&
(bpt->is_single_shot() == single_shot)) {
break;
}
bpt = bpt->next(); bpt = bpt->next();
} }
if (bpt == nullptr) { if (bpt == nullptr) {
bpt = new Breakpoint(dbg->nextId(), this); bpt = new Breakpoint(dbg->nextId(), this, single_shot, closure);
bpt->SetIsSingleShot();
AddBreakpoint(bpt, dbg);
}
return bpt;
}
Breakpoint* BreakpointLocation::AddPerClosure(Debugger* dbg,
const Instance& closure,
bool for_over_await) {
Breakpoint* bpt = nullptr;
// Do not reuse existing breakpoints for stepping over await clauses.
// A second async step-over command will set a new breakpoint before
// the existing one gets deleted when first async step-over resumes.
if (!for_over_await) {
bpt = breakpoints();
while (bpt != nullptr) {
if (bpt->IsPerClosure() && (bpt->closure() == closure.ptr())) break;
bpt = bpt->next();
}
}
if (bpt == nullptr) {
bpt = new Breakpoint(dbg->nextId(), this);
bpt->SetIsPerClosure(closure);
bpt->set_is_synthetic_async(for_over_await);
AddBreakpoint(bpt, dbg); AddBreakpoint(bpt, dbg);
} }
return bpt; return bpt;
@ -1468,7 +1440,6 @@ Debugger::Debugger(Isolate* isolate)
last_stepping_fp_(0), last_stepping_fp_(0),
last_stepping_pos_(TokenPosition::kNoSource), last_stepping_pos_(TokenPosition::kNoSource),
skip_next_step_(false), skip_next_step_(false),
synthetic_async_breakpoint_(nullptr),
exc_pause_info_(kNoPauseOnExceptions) {} exc_pause_info_(kNoPauseOnExceptions) {}
Debugger::~Debugger() { Debugger::~Debugger() {
@ -1477,7 +1448,6 @@ Debugger::~Debugger() {
ASSERT(breakpoint_locations_ == nullptr); ASSERT(breakpoint_locations_ == nullptr);
ASSERT(stack_trace_ == nullptr); ASSERT(stack_trace_ == nullptr);
ASSERT(async_causal_stack_trace_ == nullptr); ASSERT(async_causal_stack_trace_ == nullptr);
ASSERT(synthetic_async_breakpoint_ == nullptr);
} }
void Debugger::Shutdown() { void Debugger::Shutdown() {
@ -2737,7 +2707,7 @@ Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function,
} }
Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure, Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure,
bool for_over_await) { bool single_shot) {
if (!closure.IsClosure()) { if (!closure.IsClosure()) {
return nullptr; return nullptr;
} }
@ -2746,7 +2716,7 @@ Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure,
BreakpointLocation* bpt_location = BreakpointLocation* bpt_location =
SetBreakpoint(script, func.token_pos(), func.end_token_pos(), -1, SetBreakpoint(script, func.token_pos(), func.end_token_pos(), -1,
-1 /* no line/col */, func); -1 /* no line/col */, func);
return bpt_location->AddPerClosure(this, closure, for_over_await); return bpt_location->AddBreakpoint(this, Closure::Cast(closure), single_shot);
} }
Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) { Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) {
@ -2758,10 +2728,8 @@ Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) {
while (loc != nullptr) { while (loc != nullptr) {
Breakpoint* bpt = loc->breakpoints(); Breakpoint* bpt = loc->breakpoints();
while (bpt != nullptr) { while (bpt != nullptr) {
if (bpt->IsPerClosure()) { if (closure.ptr() == bpt->closure()) {
if (closure.ptr() == bpt->closure()) { return bpt;
return bpt;
}
} }
bpt = bpt->next(); bpt = bpt->next();
} }
@ -3098,17 +3066,6 @@ void Debugger::ResetSteppingFramePointer() {
stepping_fp_ = 0; stepping_fp_ = 0;
} }
bool Debugger::SteppedForSyntheticAsyncBreakpoint() const {
return synthetic_async_breakpoint_ != nullptr;
}
void Debugger::CleanupSyntheticAsyncBreakpoint() {
if (synthetic_async_breakpoint_ != nullptr) {
RemoveBreakpoint(synthetic_async_breakpoint_->id());
synthetic_async_breakpoint_ = nullptr;
}
}
void Debugger::SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace) { void Debugger::SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace) {
if (stack_trace->Length() > 0) { if (stack_trace->Length() > 0) {
stepping_fp_ = stack_trace->FrameAt(0)->fp(); stepping_fp_ = stack_trace->FrameAt(0)->fp();
@ -3518,7 +3475,7 @@ void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) {
ResetSteppingFramePointer(); ResetSteppingFramePointer();
NotifySingleStepping(false); NotifySingleStepping(false);
ASSERT(!IsPaused()); ASSERT(!IsPaused());
if ((bpt != nullptr) && bpt->IsSingleShot()) { if ((bpt != nullptr) && bpt->is_single_shot()) {
RemoveBreakpoint(bpt->id()); RemoveBreakpoint(bpt->id());
bpt = nullptr; bpt = nullptr;
} }
@ -3630,9 +3587,6 @@ ErrorPtr Debugger::PauseStepping() {
CacheStackTraces(DebuggerStackTrace::Collect(), CacheStackTraces(DebuggerStackTrace::Collect(),
DebuggerStackTrace::CollectAsyncCausal()); DebuggerStackTrace::CollectAsyncCausal());
if (SteppedForSyntheticAsyncBreakpoint()) {
CleanupSyntheticAsyncBreakpoint();
}
SignalPausedEvent(frame, nullptr); SignalPausedEvent(frame, nullptr);
HandleSteppingRequest(stack_trace_); HandleSteppingRequest(stack_trace_);
ClearCachedStackTraces(); ClearCachedStackTraces();
@ -3679,34 +3633,6 @@ ErrorPtr Debugger::PauseBreakpoint() {
return Error::null(); return Error::null();
} }
if (bpt_hit->is_synthetic_async()) {
CacheStackTraces(stack_trace, DebuggerStackTrace::CollectAsyncCausal());
// Hit a synthetic async breakpoint.
if (FLAG_verbose_debug) {
OS::PrintErr(
">>> hit synthetic %s"
" (func %s token %s address %#" Px " offset %#" Px ")\n",
cbpt_tostring,
String::Handle(top_frame->QualifiedFunctionName()).ToCString(),
bpt_location->token_pos().ToCString(), top_frame->pc(),
top_frame->pc() - top_frame->code().PayloadStart());
}
ASSERT(synthetic_async_breakpoint_ == nullptr);
synthetic_async_breakpoint_ = bpt_hit;
bpt_hit = nullptr;
// We are at the entry of an async function.
// We issue a step over to resume at the point after the await statement.
SetResumeAction(kStepOver);
// 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 */);
ClearCachedStackTraces();
return Error::null();
}
if (FLAG_verbose_debug) { if (FLAG_verbose_debug) {
OS::PrintErr(">>> hit %" Pd OS::PrintErr(">>> hit %" Pd
" %s" " %s"
@ -3736,7 +3662,7 @@ Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) {
// First check for a single-shot breakpoint. // First check for a single-shot breakpoint.
Breakpoint* bpt = breakpoints(); Breakpoint* bpt = breakpoints();
while (bpt != nullptr) { while (bpt != nullptr) {
if (bpt->IsSingleShot()) { if (bpt->is_single_shot() && bpt->closure() == Instance::null()) {
return bpt; return bpt;
} }
bpt = bpt->next(); bpt = bpt->next();
@ -3745,11 +3671,9 @@ Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) {
// Now check for a closure-specific breakpoint. // Now check for a closure-specific breakpoint.
bpt = breakpoints(); bpt = breakpoints();
while (bpt != nullptr) { while (bpt != nullptr) {
if (bpt->IsPerClosure()) { if (bpt->closure() != Instance::null() &&
Closure& closure = Closure::Handle(top_frame->GetClosure()); bpt->closure() == top_frame->GetClosure()) {
if (closure.ptr() == bpt->closure()) { return bpt;
return bpt;
}
} }
bpt = bpt->next(); bpt = bpt->next();
} }
@ -3757,7 +3681,7 @@ Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) {
// Finally, check for a general breakpoint. // Finally, check for a general breakpoint.
bpt = breakpoints(); bpt = breakpoints();
while (bpt != nullptr) { while (bpt != nullptr) {
if (bpt->IsRepeated()) { if (!bpt->is_single_shot() && bpt->closure() == Instance::null()) {
return bpt; return bpt;
} }
bpt = bpt->next(); bpt = bpt->next();
@ -4041,9 +3965,6 @@ bool Debugger::RemoveBreakpointFromTheList(intptr_t bp_id,
if (pause_event_ != nullptr && pause_event_->breakpoint() == curr_bpt) { if (pause_event_ != nullptr && pause_event_->breakpoint() == curr_bpt) {
pause_event_->set_breakpoint(nullptr); pause_event_->set_breakpoint(nullptr);
} }
if (synthetic_async_breakpoint_ == curr_bpt) {
synthetic_async_breakpoint_ = nullptr;
}
delete curr_bpt; delete curr_bpt;
curr_bpt = nullptr; curr_bpt = nullptr;
@ -4211,18 +4132,18 @@ void Debugger::MaybeAsyncStepInto(const Closure& async_op) {
} }
} }
void Debugger::AsyncStepInto(const Closure& async_op) { void Debugger::AsyncStepInto(const Closure& awaiter) {
Zone* zone = Thread::Current()->zone(); Zone* zone = Thread::Current()->zone();
CallerClosureFinder caller_closure_finder(zone); CallerClosureFinder caller_closure_finder(zone);
if (caller_closure_finder.IsAsyncCallback( if (caller_closure_finder.IsAsyncCallback(
Function::Handle(zone, async_op.function()))) { Function::Handle(zone, awaiter.function()))) {
const auto& suspend_state = SuspendState::Handle( const auto& suspend_state = SuspendState::Handle(
zone, caller_closure_finder.GetSuspendStateFromAsyncCallback(async_op)); zone, caller_closure_finder.GetSuspendStateFromAsyncCallback(awaiter));
const auto& function_data = const auto& function_data =
Object::Handle(zone, suspend_state.function_data()); Object::Handle(zone, suspend_state.function_data());
SetBreakpointAtResumption(function_data); SetBreakpointAtResumption(function_data);
} else { } else {
SetBreakpointAtActivation(async_op, true); SetBreakpointAtActivation(awaiter, /*single_shot=*/true);
} }
Continue(); Continue();
} }

View file

@ -41,17 +41,20 @@ class ObjectPointerVisitor;
class BreakpointLocation; class BreakpointLocation;
class StackFrame; class StackFrame;
// A user-defined breakpoint, which either fires once, for a particular closure, // A user-defined breakpoint, which can be set for a particular closure
// or always. The API's notion of a breakpoint corresponds to this object. // (if |closure| is not |null|) and can fire one (|is_single_shot| is |true|)
// or many times.
class Breakpoint { class Breakpoint {
public: public:
Breakpoint(intptr_t id, BreakpointLocation* bpt_location) Breakpoint(intptr_t id,
BreakpointLocation* bpt_location,
bool is_single_shot,
const Closure& closure)
: id_(id), : id_(id),
kind_(Breakpoint::kNone),
next_(nullptr), next_(nullptr),
closure_(Instance::null()), closure_(closure.ptr()),
bpt_location_(bpt_location), bpt_location_(bpt_location),
is_synthetic_async_(false) {} is_single_shot_(is_single_shot) {}
intptr_t id() const { return id_; } intptr_t id() const { return id_; }
Breakpoint* next() const { return next_; } Breakpoint* next() const { return next_; }
@ -60,26 +63,8 @@ class Breakpoint {
BreakpointLocation* bpt_location() const { return bpt_location_; } BreakpointLocation* bpt_location() const { return bpt_location_; }
void set_bpt_location(BreakpointLocation* new_bpt_location); void set_bpt_location(BreakpointLocation* new_bpt_location);
bool IsRepeated() const { return kind_ == kRepeated; } bool is_single_shot() const { return is_single_shot_; }
bool IsSingleShot() const { return kind_ == kSingleShot; } ClosurePtr closure() const { return closure_; }
bool IsPerClosure() const { return kind_ == kPerClosure; }
InstancePtr closure() const { return closure_; }
void SetIsRepeated() {
ASSERT(kind_ == kNone);
kind_ = kRepeated;
}
void SetIsSingleShot() {
ASSERT(kind_ == kNone);
kind_ = kSingleShot;
}
void SetIsPerClosure(const Instance& closure) {
ASSERT(kind_ == kNone);
kind_ = kPerClosure;
closure_ = closure.ptr();
}
void Enable() { void Enable() {
ASSERT(!enabled_); ASSERT(!enabled_);
@ -93,30 +78,16 @@ class Breakpoint {
bool is_enabled() const { return enabled_; } bool is_enabled() const { return enabled_; }
// Mark that this breakpoint is a result of a step OverAwait request.
void set_is_synthetic_async(bool is_synthetic_async) {
is_synthetic_async_ = is_synthetic_async;
}
bool is_synthetic_async() const { return is_synthetic_async_; }
void PrintJSON(JSONStream* stream); void PrintJSON(JSONStream* stream);
private: private:
void VisitObjectPointers(ObjectPointerVisitor* visitor); void VisitObjectPointers(ObjectPointerVisitor* visitor);
enum ConditionKind {
kNone,
kRepeated,
kSingleShot,
kPerClosure,
};
intptr_t id_; intptr_t id_;
ConditionKind kind_;
Breakpoint* next_; Breakpoint* next_;
InstancePtr closure_; ClosurePtr closure_;
BreakpointLocation* bpt_location_; BreakpointLocation* bpt_location_;
bool is_synthetic_async_; bool is_single_shot_;
bool enabled_ = false; bool enabled_ = false;
friend class BreakpointLocation; friend class BreakpointLocation;
@ -170,9 +141,9 @@ class BreakpointLocation {
Breakpoint* AddRepeated(Debugger* dbg); Breakpoint* AddRepeated(Debugger* dbg);
Breakpoint* AddSingleShot(Debugger* dbg); Breakpoint* AddSingleShot(Debugger* dbg);
Breakpoint* AddPerClosure(Debugger* dbg, Breakpoint* AddBreakpoint(Debugger* dbg,
const Instance& closure, const Closure& closure,
bool for_over_await); bool single_shot);
bool AnyEnabled() const; bool AnyEnabled() const;
bool IsResolved() const { return code_token_pos_.IsReal(); } bool IsResolved() const { return code_token_pos_.IsReal(); }
@ -705,7 +676,7 @@ class Debugger {
Breakpoint* SetBreakpointAtEntry(const Function& target_function, Breakpoint* SetBreakpointAtEntry(const Function& target_function,
bool single_shot); bool single_shot);
Breakpoint* SetBreakpointAtActivation(const Instance& closure, Breakpoint* SetBreakpointAtActivation(const Instance& closure,
bool for_over_await); bool single_shot);
Breakpoint* BreakpointAtActivation(const Instance& closure); Breakpoint* BreakpointAtActivation(const Instance& closure);
// TODO(turnidge): script_url may no longer be specific enough. // TODO(turnidge): script_url may no longer be specific enough.
@ -726,7 +697,7 @@ class Debugger {
Breakpoint* GetBreakpointById(intptr_t id); Breakpoint* GetBreakpointById(intptr_t id);
void MaybeAsyncStepInto(const Closure& async_op); void MaybeAsyncStepInto(const Closure& async_op);
void AsyncStepInto(const Closure& async_op); void AsyncStepInto(const Closure& awaiter);
void Continue(); void Continue();
@ -895,8 +866,6 @@ class Debugger {
intptr_t post_deopt_frame_index); intptr_t post_deopt_frame_index);
void ResetSteppingFramePointer(); void ResetSteppingFramePointer();
bool SteppedForSyntheticAsyncBreakpoint() const;
void CleanupSyntheticAsyncBreakpoint();
void SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace); void SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace);
GroupDebugger* group_debugger() { return isolate_->group()->debugger(); } GroupDebugger* group_debugger() { return isolate_->group()->debugger(); }
@ -945,11 +914,6 @@ class Debugger {
// breakpoint. // breakpoint.
bool skip_next_step_; bool skip_next_step_;
// We keep this breakpoint alive until after the debugger does the step over
// async continuation machinery so that we can report that we've stopped
// at the breakpoint.
Breakpoint* synthetic_async_breakpoint_;
Dart_ExceptionPauseInfo exc_pause_info_; Dart_ExceptionPauseInfo exc_pause_info_;
// Holds function data corresponding to suspendable // Holds function data corresponding to suspendable

View file

@ -3974,8 +3974,8 @@ static void AddBreakpointAtActivation(Thread* thread, JSONStream* js) {
return; return;
} }
const Instance& closure = Instance::Cast(obj); const Instance& closure = Instance::Cast(obj);
Breakpoint* bpt = Breakpoint* bpt = thread->isolate()->debugger()->SetBreakpointAtActivation(
thread->isolate()->debugger()->SetBreakpointAtActivation(closure, false); closure, /*single_shot=*/false);
if (bpt == nullptr) { if (bpt == nullptr) {
js->PrintError(kCannotAddBreakpoint, js->PrintError(kCannotAddBreakpoint,
"%s: Cannot add breakpoint at activation", js->method()); "%s: Cannot add breakpoint at activation", js->method());

View file

@ -17,7 +17,7 @@
namespace dart { namespace dart {
#define SERVICE_PROTOCOL_MAJOR_VERSION 4 #define SERVICE_PROTOCOL_MAJOR_VERSION 4
#define SERVICE_PROTOCOL_MINOR_VERSION 9 #define SERVICE_PROTOCOL_MINOR_VERSION 10
class Array; class Array;
class EmbedderServiceHandler; class EmbedderServiceHandler;

View file

@ -1,4 +1,4 @@
# Dart VM Service Protocol 4.9 # Dart VM Service Protocol 4.10
> Please post feedback to the [observatory-discuss group][discuss-list] > Please post feedback to the [observatory-discuss group][discuss-list]
@ -1927,8 +1927,7 @@ class Breakpoint extends Object {
// Has this breakpoint been assigned to a specific program location? // Has this breakpoint been assigned to a specific program location?
bool resolved; bool resolved;
// Is this a breakpoint that was added synthetically as part of a step // Note: this property is deprecated and is always absent from the response.
// OverAsyncSuspension resume command?
bool isSyntheticAsyncContinuation [optional]; bool isSyntheticAsyncContinuation [optional];
// SourceLocation when breakpoint is resolved, UnresolvedSourceLocation // SourceLocation when breakpoint is resolved, UnresolvedSourceLocation
@ -4700,5 +4699,6 @@ version | comments
4.7 | Added a deprecation notice to `Stack.awaiterFrames` field. Added a deprecation notice to `FrameKind.AsyncActivation`. 4.7 | Added a deprecation notice to `Stack.awaiterFrames` field. Added a deprecation notice to `FrameKind.AsyncActivation`.
4.8 | Added `getIsolatePauseEvent` RPC. 4.8 | Added `getIsolatePauseEvent` RPC.
4.9 | Added `isolateGroup` property to `Event`. 4.9 | Added `isolateGroup` property to `Event`.
4.10 | Deprecated `isSyntheticAsyncContinuation` on `Breakpoint`.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss [discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss