Index: runtime/observatory/lib/src/service/object.dart |
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart |
index bd76e67d0caa9d24a4791ee05345935b0b0feb5e..ec786e1156e38dee1bfdf0308bf85b52fde974ec 100644 |
--- a/runtime/observatory/lib/src/service/object.dart |
+++ b/runtime/observatory/lib/src/service/object.dart |
@@ -1577,91 +1577,88 @@ class Isolate extends ServiceObjectOwner { |
return invokeRpc('resume', {'step': 'Over'}); |
} |
+ Future stepOverAwait() { |
+ return invokeRpc('resume', {'step': 'OverAwait'}); |
+ } |
+ |
Future stepOut() { |
return invokeRpc('resume', {'step': 'Out'}); |
} |
- static const int kFirstResume = 0; |
- static const int kSecondResume = 1; |
- /// result[kFirstResume] completes after the inital resume. The UI should |
+ static const int kUIFuture = 0; |
+ static const int kTestFuture = 1; |
+ /// result[kUIFuture] completes after the inital resume. The UI should |
/// wait on this future because some other breakpoint may be hit before the |
/// async continuation. |
- /// result[kSecondResume] completes after the second resume. Tests should |
+ /// result[kTestFuture] completes after the second resume. Tests should |
/// wait on this future to avoid confusing the pause event at the |
/// state-machine switch with the pause event after the state-machine switch. |
Future<List<Future>> asyncStepOver() async { |
- final Completer firstResume = new Completer(); |
- final Completer secondResume = new Completer(); |
- final List<Future> result = [firstResume.future, secondResume.future]; |
+ final Completer breakpointAdded = new Completer(); |
+ final Completer breakpointRemoved = new Completer(); |
rmacnak
2016/02/16 21:21:58
It would make more sense for the tests to wait for
Cutch
2016/02/17 18:24:21
Event sequence is now:
- kBreapointAdded (#1).
-
|
+ final List<Future> result = |
+ [breakpointAdded.future, breakpointRemoved.future]; |
StreamSubscription subscription; |
- // Inner error handling function. |
- handleError(error) { |
+ // Cancel the subscription. |
+ cancelSubscription() { |
if (subscription != null) { |
subscription.cancel(); |
subscription = null; |
} |
- firstResume.completeError(error); |
- secondResume.completeError(error); |
} |
- if ((pauseEvent == null) || |
- (pauseEvent.kind != ServiceEvent.kPauseBreakpoint) || |
- (pauseEvent.asyncContinuation == null)) { |
- handleError(new Exception("No async continuation available")); |
- } else { |
- Instance continuation = pauseEvent.asyncContinuation; |
- assert(continuation.isClosure); |
- |
- // Add breakpoint at continuation. |
- Breakpoint continuationBpt; |
- try { |
- continuationBpt = await addBreakOnActivation(continuation); |
- } catch (e) { |
- handleError(e); |
- return result; |
+ // Complete futures with with error. |
+ completeError(error) { |
+ if (!breakpointAdded.isCompleted) { |
+ breakpointAdded.completeError(error); |
} |
- |
- // Subscribe to the debugger event stream. |
- Stream stream; |
- try { |
- stream = await vm.getEventStream(VM.kDebugStream); |
- } catch (e) { |
- handleError(e); |
- return result; |
+ if (!breakpointRemoved.isCompleted) { |
+ breakpointRemoved.completeError(error); |
} |
+ } |
- Completer onResume = firstResume; |
- subscription = stream.listen((ServiceEvent event) async { |
- if ((event.kind == ServiceEvent.kPauseBreakpoint) && |
- (event.breakpoint == continuationBpt)) { |
- // We are stopped before state-machine dispatch: |
- // 1) Remove the continuation breakpoint. |
- // 2) step over. |
- // reach user code. |
- await removeBreakpoint(continuationBpt); |
- onResume = secondResume; |
- stepOver().catchError(handleError); |
- } else if (event.kind == ServiceEvent.kResume) { |
- // We've resumed. |
- if (onResume == secondResume) { |
- // This is our second resume, cancel our subscription to the debug |
- // stream. |
- subscription.cancel(); |
- subscription = null; |
- } |
- // Complete onResume and clear it. |
- if (onResume != null) { |
- onResume.complete(this); |
- onResume = null; |
- } |
- } |
- }); |
+ // Subscribe to the debugger event stream. |
+ Stream stream; |
+ try { |
+ stream = await vm.getEventStream(VM.kDebugStream); |
+ } catch (e) { |
+ completeError(e); |
+ return result; |
+ } |
+ |
+ Breakpoint syntheticBreakpoint; |
+ |
+ subscription = stream.listen((ServiceEvent event) async { |
+ // Synthetic breakpoint add event for this isolate. |
+ bool isAdd = (event.kind == ServiceEvent.kBreakpointAdded) && |
+ (event.breakpoint.isSyntheticAsync) && |
+ (event.owner == this); |
+ // Synthetic breakpoint remove event. |
+ bool isRemove = (event.kind == ServiceEvent.kBreakpointRemoved) && |
+ (syntheticBreakpoint != null) && |
+ (event.breakpoint == syntheticBreakpoint); |
+ if (isAdd) { |
+ syntheticBreakpoint = event.breakpoint; |
+ breakpointAdded.complete(this); |
+ } else if (isRemove) { |
+ breakpointRemoved.complete(this); |
+ // We are finished, cancel the subscription. |
+ cancelSubscription(); |
+ } |
+ }); |
- // Call resume, which will eventually cause us to hit continuationBpt. |
- resume().catchError(handleError); |
+ // Issue the step OverAwait command. |
+ try { |
+ await isolate.stepOverAwait(); |
+ } catch (e) { |
+ // This can fail when another client issued the same resume command |
+ // or another client has moved the isolate forward. |
+ cancelSubscription(); |
+ completeError(e); |
} |
+ |
return result; |
} |
@@ -1916,6 +1913,7 @@ class ServiceEvent extends ServiceObject { |
@observable Instance exception; |
@observable Instance asyncContinuation; |
@observable bool atAsyncJump; |
rmacnak
2016/02/16 21:21:58
Don't need atAsyncJump anymore.
Cutch
2016/02/17 18:24:21
Removed this and asyncContinuation.
|
+ @observable bool atAwait; |
@observable ServiceObject inspectee; |
@observable ByteData data; |
@observable int count; |
@@ -1970,6 +1968,11 @@ class ServiceEvent extends ServiceObject { |
} else { |
atAsyncJump = false; |
} |
+ if (map['atAwait'] != null) { |
+ atAwait = true; |
+ } else { |
+ atAwait = false; |
+ } |
if (map['inspectee'] != null) { |
inspectee = map['inspectee']; |
} |
@@ -2040,6 +2043,10 @@ class Breakpoint extends ServiceObject { |
// The breakpoint has been assigned to a final source location. |
@observable bool resolved; |
+ // The breakpoint was synthetically created as part of an 'OverAWait' resume |
+ // request. |
+ @observable bool isSyntheticAsync; |
+ |
void _update(ObservableMap map, bool mapIsRef) { |
_loaded = true; |
_upgradeCollection(map, owner); |
@@ -2066,6 +2073,8 @@ class Breakpoint extends ServiceObject { |
newScript._addBreakpoint(this); |
} |
+ isSyntheticAsync = map['isSyntheticAsyncBreakpoint'] != null; |
+ |
assert(resolved || location is UnresolvedSourceLocation); |
} |
@@ -2081,7 +2090,11 @@ class Breakpoint extends ServiceObject { |
String toString() { |
if (number != null) { |
- return 'Breakpoint ${number} at ${location})'; |
+ if (isSyntheticAsync) { |
+ return 'Async Synthetic Breakpoint ${number} at ${location}'; |
+ } else { |
+ return 'Breakpoint ${number} at ${location}'; |
+ } |
} else { |
return 'Uninitialized breakpoint'; |
} |