mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 14:32:24 +00:00
[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:
parent
d1ecf01f71
commit
4a6f9328a2
13 changed files with 109 additions and 200 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
version=4.9
|
version=4.10
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>[
|
||||||
|
|
|
@ -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);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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>[
|
||||||
|
|
|
@ -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));
|
||||||
},
|
},
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue