diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md index 0b881cedff0..0f2a4f2d10b 100644 --- a/pkg/vm_service/CHANGELOG.md +++ b/pkg/vm_service/CHANGELOG.md @@ -1,3 +1,7 @@ +## 11.7.3 +- Update to version `4.10` of the spec. +- Add deprecation notice to `Breakpoint.isSyntheticAsyncContinuation`. + ## 11.7.2 - Make Event classes abstract to permit `dap` event stream extensions. diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties index baf625a8edc..088eb5450d2 100644 --- a/pkg/vm_service/java/version.properties +++ b/pkg/vm_service/java/version.properties @@ -1 +1 @@ -version=4.9 +version=4.10 diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart index 79793ad4a3d..04494dbe990 100644 --- a/pkg/vm_service/lib/src/vm_service.dart +++ b/pkg/vm_service/lib/src/vm_service.dart @@ -28,7 +28,7 @@ export 'snapshot_graph.dart' HeapSnapshotObjectNoData, HeapSnapshotObjectNullData; -const String vmServiceVersion = '4.9.0'; +const String vmServiceVersion = '4.10.0'; /// @optional const String optional = 'optional'; @@ -3347,8 +3347,7 @@ class Breakpoint extends Obj { /// Has this breakpoint been assigned to a specific program location? bool? resolved; - /// Is this a breakpoint that was added synthetically as part of a step - /// OverAsyncSuspension resume command? + /// Note: this property is deprecated and is always absent from the response. @optional bool? isSyntheticAsyncContinuation; diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml index 863d5394554..744a96d0d14 100644 --- a/pkg/vm_service/pubspec.yaml +++ b/pkg/vm_service/pubspec.yaml @@ -1,5 +1,5 @@ name: vm_service -version: 11.7.2 +version: 11.7.3 description: >- A library to communicate with a service implementing the Dart VM service protocol. diff --git a/runtime/observatory/tests/service/async_step_out_test.dart b/runtime/observatory/tests/service/async_step_out_test.dart index 64be6e4cba2..91c36e8e10f 100644 --- a/runtime/observatory/tests/service/async_step_out_test.dart +++ b/runtime/observatory/tests/service/async_step_out_test.dart @@ -8,26 +8,37 @@ import 'dart:developer'; import 'service_test_common.dart'; import 'test_helper.dart'; -const LINE_A = 21; -const LINE_B = 22; -const LINE_C = 23; -const LINE_D = 28; -const LINE_E = 29; -const LINE_F = 30; +// AUTOGENERATED START +// +// Update these constants by running: +// +// dart runtime/observatory/tests/service/update_line_numbers.dart +// +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; - -helper() async { +Future helper() async { await null; // LINE_A. - print('helper'); // LINE_B. - print('foobar'); // LINE_C. + print('line b'); // LINE_B. + print('line c'); // LINE_C. + return 0; } -testMain() async { +Future testMain() async { + int process(int value) { + return value + 1; + } + debugger(); // LINE_0. - print('mmmmm'); // LINE_D. - await helper(); // LINE_E. - print('z'); // LINE_F. + print('line d'); // LINE_D. + await helper().then(process); // LINE_E. + print('line f'); // LINE_F. } var tests = [ diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart index 0ac5acbc537..ddde6d5b487 100644 --- a/runtime/observatory/tests/service/get_version_rpc_test.dart +++ b/runtime/observatory/tests/service/get_version_rpc_test.dart @@ -12,7 +12,7 @@ var tests = [ final result = await vm.invokeRpcNoUpgrade('getVersion', {}); expect(result['type'], 'Version'); expect(result['major'], 4); - expect(result['minor'], 9); + expect(result['minor'], 10); expect(result['_privateMajor'], 0); expect(result['_privateMinor'], 0); }, diff --git a/runtime/observatory_2/tests/service_2/async_step_out_test.dart b/runtime/observatory_2/tests/service_2/async_step_out_test.dart index 64be6e4cba2..b5a648a6ba6 100644 --- a/runtime/observatory_2/tests/service_2/async_step_out_test.dart +++ b/runtime/observatory_2/tests/service_2/async_step_out_test.dart @@ -8,26 +8,36 @@ import 'dart:developer'; import 'service_test_common.dart'; import 'test_helper.dart'; -const LINE_A = 21; -const LINE_B = 22; -const LINE_C = 23; -const LINE_D = 28; -const LINE_E = 29; -const LINE_F = 30; - -const LINE_0 = 27; +// AUTOGENERATED START +// +// Update these constants by running: +// +// dart runtime/observatory/tests/service/update_line_numbers.dart +// +const int LINE_A = 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 { await null; // LINE_A. - print('helper'); // LINE_B. - print('foobar'); // LINE_C. + print('line b'); // LINE_B. + print('line c'); // LINE_C. } testMain() async { + int process(int value) { + return value + 1; + } + debugger(); // LINE_0. - print('mmmmm'); // LINE_D. - await helper(); // LINE_E. - print('z'); // LINE_F. + print('line d'); // LINE_D. + await helper().then(process); // LINE_E. + print('line f'); // LINE_F. } var tests = [ diff --git a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart index 52d23e66978..9f1bf2febba 100644 --- a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart +++ b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart @@ -12,7 +12,7 @@ var tests = [ final result = await vm.invokeRpcNoUpgrade('getVersion', {}); expect(result['type'], equals('Version')); expect(result['major'], equals(4)); - expect(result['minor'], equals(9)); + expect(result['minor'], equals(10)); expect(result['_privateMajor'], equals(0)); expect(result['_privateMinor'], equals(0)); }, diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index f5bf4879ded..20a2f4fb1e5 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -195,9 +195,6 @@ void Breakpoint::PrintJSON(JSONStream* stream) { jsobj.AddFixedServiceId("breakpoints/%" Pd "", id()); jsobj.AddProperty("enabled", enabled_); 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_); @@ -354,51 +351,26 @@ void BreakpointLocation::AddBreakpoint(Breakpoint* bpt, Debugger* dbg) { } Breakpoint* BreakpointLocation::AddRepeated(Debugger* dbg) { - Breakpoint* bpt = breakpoints(); - 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; + return AddBreakpoint(dbg, Closure::Handle(), /*single_shot=*/false); } 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(); while (bpt != nullptr) { - if (bpt->IsSingleShot()) break; + if ((bpt->closure() == closure.ptr()) && + (bpt->is_single_shot() == single_shot)) { + break; + } bpt = bpt->next(); } if (bpt == nullptr) { - bpt = new Breakpoint(dbg->nextId(), this); - 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); + bpt = new Breakpoint(dbg->nextId(), this, single_shot, closure); AddBreakpoint(bpt, dbg); } return bpt; @@ -1468,7 +1440,6 @@ Debugger::Debugger(Isolate* isolate) last_stepping_fp_(0), last_stepping_pos_(TokenPosition::kNoSource), skip_next_step_(false), - synthetic_async_breakpoint_(nullptr), exc_pause_info_(kNoPauseOnExceptions) {} Debugger::~Debugger() { @@ -1477,7 +1448,6 @@ Debugger::~Debugger() { ASSERT(breakpoint_locations_ == nullptr); ASSERT(stack_trace_ == nullptr); ASSERT(async_causal_stack_trace_ == nullptr); - ASSERT(synthetic_async_breakpoint_ == nullptr); } void Debugger::Shutdown() { @@ -2737,7 +2707,7 @@ Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function, } Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure, - bool for_over_await) { + bool single_shot) { if (!closure.IsClosure()) { return nullptr; } @@ -2746,7 +2716,7 @@ Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure, BreakpointLocation* bpt_location = SetBreakpoint(script, func.token_pos(), func.end_token_pos(), -1, -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) { @@ -2758,10 +2728,8 @@ Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) { while (loc != nullptr) { Breakpoint* bpt = loc->breakpoints(); while (bpt != nullptr) { - if (bpt->IsPerClosure()) { - if (closure.ptr() == bpt->closure()) { - return bpt; - } + if (closure.ptr() == bpt->closure()) { + return bpt; } bpt = bpt->next(); } @@ -3098,17 +3066,6 @@ void Debugger::ResetSteppingFramePointer() { 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) { if (stack_trace->Length() > 0) { stepping_fp_ = stack_trace->FrameAt(0)->fp(); @@ -3518,7 +3475,7 @@ void Debugger::SignalPausedEvent(ActivationFrame* top_frame, Breakpoint* bpt) { ResetSteppingFramePointer(); NotifySingleStepping(false); ASSERT(!IsPaused()); - if ((bpt != nullptr) && bpt->IsSingleShot()) { + if ((bpt != nullptr) && bpt->is_single_shot()) { RemoveBreakpoint(bpt->id()); bpt = nullptr; } @@ -3630,9 +3587,6 @@ ErrorPtr Debugger::PauseStepping() { CacheStackTraces(DebuggerStackTrace::Collect(), DebuggerStackTrace::CollectAsyncCausal()); - if (SteppedForSyntheticAsyncBreakpoint()) { - CleanupSyntheticAsyncBreakpoint(); - } SignalPausedEvent(frame, nullptr); HandleSteppingRequest(stack_trace_); ClearCachedStackTraces(); @@ -3679,34 +3633,6 @@ ErrorPtr Debugger::PauseBreakpoint() { 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) { OS::PrintErr(">>> hit %" Pd " %s" @@ -3736,7 +3662,7 @@ Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) { // First check for a single-shot breakpoint. Breakpoint* bpt = breakpoints(); while (bpt != nullptr) { - if (bpt->IsSingleShot()) { + if (bpt->is_single_shot() && bpt->closure() == Instance::null()) { return bpt; } bpt = bpt->next(); @@ -3745,11 +3671,9 @@ Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) { // Now check for a closure-specific breakpoint. bpt = breakpoints(); while (bpt != nullptr) { - if (bpt->IsPerClosure()) { - Closure& closure = Closure::Handle(top_frame->GetClosure()); - if (closure.ptr() == bpt->closure()) { - return bpt; - } + if (bpt->closure() != Instance::null() && + bpt->closure() == top_frame->GetClosure()) { + return bpt; } bpt = bpt->next(); } @@ -3757,7 +3681,7 @@ Breakpoint* BreakpointLocation::FindHitBreakpoint(ActivationFrame* top_frame) { // Finally, check for a general breakpoint. bpt = breakpoints(); while (bpt != nullptr) { - if (bpt->IsRepeated()) { + if (!bpt->is_single_shot() && bpt->closure() == Instance::null()) { return bpt; } bpt = bpt->next(); @@ -4041,9 +3965,6 @@ bool Debugger::RemoveBreakpointFromTheList(intptr_t bp_id, if (pause_event_ != nullptr && pause_event_->breakpoint() == curr_bpt) { pause_event_->set_breakpoint(nullptr); } - if (synthetic_async_breakpoint_ == curr_bpt) { - synthetic_async_breakpoint_ = nullptr; - } delete curr_bpt; 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(); CallerClosureFinder caller_closure_finder(zone); if (caller_closure_finder.IsAsyncCallback( - Function::Handle(zone, async_op.function()))) { + Function::Handle(zone, awaiter.function()))) { const auto& suspend_state = SuspendState::Handle( - zone, caller_closure_finder.GetSuspendStateFromAsyncCallback(async_op)); + zone, caller_closure_finder.GetSuspendStateFromAsyncCallback(awaiter)); const auto& function_data = Object::Handle(zone, suspend_state.function_data()); SetBreakpointAtResumption(function_data); } else { - SetBreakpointAtActivation(async_op, true); + SetBreakpointAtActivation(awaiter, /*single_shot=*/true); } Continue(); } diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h index 160826596ac..8ec3506a001 100644 --- a/runtime/vm/debugger.h +++ b/runtime/vm/debugger.h @@ -41,17 +41,20 @@ class ObjectPointerVisitor; class BreakpointLocation; class StackFrame; -// A user-defined breakpoint, which either fires once, for a particular closure, -// or always. The API's notion of a breakpoint corresponds to this object. +// A user-defined breakpoint, which can be set for a particular closure +// (if |closure| is not |null|) and can fire one (|is_single_shot| is |true|) +// or many times. class Breakpoint { public: - Breakpoint(intptr_t id, BreakpointLocation* bpt_location) + Breakpoint(intptr_t id, + BreakpointLocation* bpt_location, + bool is_single_shot, + const Closure& closure) : id_(id), - kind_(Breakpoint::kNone), next_(nullptr), - closure_(Instance::null()), + closure_(closure.ptr()), bpt_location_(bpt_location), - is_synthetic_async_(false) {} + is_single_shot_(is_single_shot) {} intptr_t id() const { return id_; } Breakpoint* next() const { return next_; } @@ -60,26 +63,8 @@ class Breakpoint { BreakpointLocation* bpt_location() const { return bpt_location_; } void set_bpt_location(BreakpointLocation* new_bpt_location); - bool IsRepeated() const { return kind_ == kRepeated; } - bool IsSingleShot() const { return kind_ == kSingleShot; } - 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(); - } + bool is_single_shot() const { return is_single_shot_; } + ClosurePtr closure() const { return closure_; } void Enable() { ASSERT(!enabled_); @@ -93,30 +78,16 @@ class Breakpoint { 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); private: void VisitObjectPointers(ObjectPointerVisitor* visitor); - enum ConditionKind { - kNone, - kRepeated, - kSingleShot, - kPerClosure, - }; - intptr_t id_; - ConditionKind kind_; Breakpoint* next_; - InstancePtr closure_; + ClosurePtr closure_; BreakpointLocation* bpt_location_; - bool is_synthetic_async_; + bool is_single_shot_; bool enabled_ = false; friend class BreakpointLocation; @@ -170,9 +141,9 @@ class BreakpointLocation { Breakpoint* AddRepeated(Debugger* dbg); Breakpoint* AddSingleShot(Debugger* dbg); - Breakpoint* AddPerClosure(Debugger* dbg, - const Instance& closure, - bool for_over_await); + Breakpoint* AddBreakpoint(Debugger* dbg, + const Closure& closure, + bool single_shot); bool AnyEnabled() const; bool IsResolved() const { return code_token_pos_.IsReal(); } @@ -705,7 +676,7 @@ class Debugger { Breakpoint* SetBreakpointAtEntry(const Function& target_function, bool single_shot); Breakpoint* SetBreakpointAtActivation(const Instance& closure, - bool for_over_await); + bool single_shot); Breakpoint* BreakpointAtActivation(const Instance& closure); // TODO(turnidge): script_url may no longer be specific enough. @@ -726,7 +697,7 @@ class Debugger { Breakpoint* GetBreakpointById(intptr_t id); void MaybeAsyncStepInto(const Closure& async_op); - void AsyncStepInto(const Closure& async_op); + void AsyncStepInto(const Closure& awaiter); void Continue(); @@ -895,8 +866,6 @@ class Debugger { intptr_t post_deopt_frame_index); void ResetSteppingFramePointer(); - bool SteppedForSyntheticAsyncBreakpoint() const; - void CleanupSyntheticAsyncBreakpoint(); void SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace); GroupDebugger* group_debugger() { return isolate_->group()->debugger(); } @@ -945,11 +914,6 @@ class Debugger { // breakpoint. 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_; // Holds function data corresponding to suspendable diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc index 73882b00aeb..17613bc0a4f 100644 --- a/runtime/vm/service.cc +++ b/runtime/vm/service.cc @@ -3974,8 +3974,8 @@ static void AddBreakpointAtActivation(Thread* thread, JSONStream* js) { return; } const Instance& closure = Instance::Cast(obj); - Breakpoint* bpt = - thread->isolate()->debugger()->SetBreakpointAtActivation(closure, false); + Breakpoint* bpt = thread->isolate()->debugger()->SetBreakpointAtActivation( + closure, /*single_shot=*/false); if (bpt == nullptr) { js->PrintError(kCannotAddBreakpoint, "%s: Cannot add breakpoint at activation", js->method()); diff --git a/runtime/vm/service.h b/runtime/vm/service.h index 17047d2cce5..b6d418bd7bc 100644 --- a/runtime/vm/service.h +++ b/runtime/vm/service.h @@ -17,7 +17,7 @@ namespace dart { #define SERVICE_PROTOCOL_MAJOR_VERSION 4 -#define SERVICE_PROTOCOL_MINOR_VERSION 9 +#define SERVICE_PROTOCOL_MINOR_VERSION 10 class Array; class EmbedderServiceHandler; diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md index c2dad791a7a..99df6ad28bb 100644 --- a/runtime/vm/service/service.md +++ b/runtime/vm/service/service.md @@ -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] @@ -1927,8 +1927,7 @@ class Breakpoint extends Object { // Has this breakpoint been assigned to a specific program location? bool resolved; - // Is this a breakpoint that was added synthetically as part of a step - // OverAsyncSuspension resume command? + // Note: this property is deprecated and is always absent from the response. bool isSyntheticAsyncContinuation [optional]; // 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.8 | Added `getIsolatePauseEvent` RPC. 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