| Index: runtime/bin/vmservice/client/lib/src/service/object.dart
|
| diff --git a/runtime/bin/vmservice/client/lib/src/service/object.dart b/runtime/bin/vmservice/client/lib/src/service/object.dart
|
| index 521aaa3a93b29090a9664b06189cd52086b8e62b..f85881faddefb0d9fb5f148cac7d0f80c7680b0c 100644
|
| --- a/runtime/bin/vmservice/client/lib/src/service/object.dart
|
| +++ b/runtime/bin/vmservice/client/lib/src/service/object.dart
|
| @@ -263,6 +263,7 @@ abstract class VM extends ServiceObjectOwner {
|
| "Expected 'ServiceEvent' but found '${map['type']}'");
|
| return;
|
| }
|
| +
|
| // Extract the owning isolate from the event itself.
|
| String owningIsolateId = map['isolate']['id'];
|
| _getIsolate(owningIsolateId).then((owningIsolate) {
|
| @@ -749,6 +750,8 @@ class Isolate extends ServiceObjectOwner with Coverage {
|
| _loaded = true;
|
| loading = false;
|
|
|
| + reloadBreakpoints();
|
| +
|
| // Remap DebuggerEvent to ServiceEvent so that the observatory can
|
| // work against 1.5 vms in the short term.
|
| //
|
| @@ -885,6 +888,142 @@ class Isolate extends ServiceObjectOwner with Coverage {
|
| }
|
| return node;
|
| }
|
| +
|
| + ServiceMap breakpoints;
|
| +
|
| + void _removeBreakpoint(ServiceMap bpt) {
|
| + var script = bpt['location']['script'];
|
| + var tokenPos = bpt['location']['tokenPos'];
|
| + assert(tokenPos != null);
|
| + if (script.loaded) {
|
| + var line = script.tokenToLine(tokenPos);
|
| + assert(line != null);
|
| + assert(script.lines[line - 1].bpt == bpt);
|
| + script.lines[line - 1].bpt = null;
|
| + }
|
| + }
|
| +
|
| + void _addBreakpoint(ServiceMap bpt) {
|
| + var script = bpt['location']['script'];
|
| + var tokenPos = bpt['location']['tokenPos'];
|
| + assert(tokenPos != null);
|
| + if (script.loaded) {
|
| + var line = script.tokenToLine(tokenPos);
|
| + assert(line != null);
|
| + assert(script.lines[line - 1].bpt == null);
|
| + script.lines[line - 1].bpt = bpt;
|
| + } else {
|
| + // Load the script and then plop in the breakpoint.
|
| + script.load().then((_) {
|
| + _addBreakpoint(bpt);
|
| + });
|
| + }
|
| + }
|
| +
|
| + void _updateBreakpoints(ServiceMap newBreakpoints) {
|
| + // Remove all of the old breakpoints from the Script lines.
|
| + if (breakpoints != null) {
|
| + for (var bpt in breakpoints['breakpoints']) {
|
| + _removeBreakpoint(bpt);
|
| + }
|
| + }
|
| + // Add all of the new breakpoints to the Script lines.
|
| + for (var bpt in newBreakpoints['breakpoints']) {
|
| + _addBreakpoint(bpt);
|
| + }
|
| + breakpoints = newBreakpoints;
|
| + }
|
| +
|
| + Future<ServiceObject> _inProgressReloadBpts;
|
| +
|
| + Future reloadBreakpoints() {
|
| + if (_inProgressReloadBpts == null) {
|
| + _inProgressReloadBpts =
|
| + get('debug/breakpoints').then((newBpts) {
|
| + _updateBreakpoints(newBpts);
|
| + }).whenComplete(() {
|
| + _inProgressReloadBpts = null;
|
| + });
|
| + }
|
| + return _inProgressReloadBpts;
|
| + }
|
| +
|
| + Future<ServiceObject> setBreakpoint(Script script, int line) {
|
| + return get(script.id + "/setBreakpoint?line=${line}").then((result) {
|
| + if (result is DartError) {
|
| + // Unable to set a breakpoint at desired line.
|
| + script.lines[line - 1].possibleBpt = false;
|
| + }
|
| + return reloadBreakpoints();
|
| + });
|
| + }
|
| +
|
| + Future clearBreakpoint(ServiceMap bpt) {
|
| + return get('${bpt.id}/clear').then((result) {
|
| + if (result is DartError) {
|
| + // TODO(turnidge): Handle this more gracefully.
|
| + Logger.root.severe(result.message);
|
| + }
|
| + if (pauseEvent != null &&
|
| + pauseEvent.breakpoint != null &&
|
| + (pauseEvent.breakpoint['id'] == bpt['id'])) {
|
| + return isolate.reload();
|
| + } else {
|
| + return reloadBreakpoints();
|
| + }
|
| + });
|
| + }
|
| +
|
| + Future pause() {
|
| + return get("debug/pause").then((result) {
|
| + if (result is DartError) {
|
| + // TODO(turnidge): Handle this more gracefully.
|
| + Logger.root.severe(result.message);
|
| + }
|
| + return isolate.reload();
|
| + });
|
| + }
|
| +
|
| + Future resume() {
|
| + return get("debug/resume").then((result) {
|
| + if (result is DartError) {
|
| + // TODO(turnidge): Handle this more gracefully.
|
| + Logger.root.severe(result.message);
|
| + }
|
| + return isolate.reload();
|
| + });
|
| + }
|
| +
|
| + Future stepInto() {
|
| + print('isolate.stepInto');
|
| + return get("debug/resume?step=into").then((result) {
|
| + if (result is DartError) {
|
| + // TODO(turnidge): Handle this more gracefully.
|
| + Logger.root.severe(result.message);
|
| + }
|
| + return isolate.reload();
|
| + });
|
| + }
|
| +
|
| + Future stepOver() {
|
| + return get("debug/resume?step=over").then((result) {
|
| + if (result is DartError) {
|
| + // TODO(turnidge): Handle this more gracefully.
|
| + Logger.root.severe(result.message);
|
| + }
|
| + return isolate.reload();
|
| + });
|
| + }
|
| +
|
| + Future stepOut() {
|
| + return get("debug/resume?step=out").then((result) {
|
| + if (result is DartError) {
|
| + // TODO(turnidge): Handle this more gracefully.
|
| + Logger.root.severe(result.message);
|
| + }
|
| + return isolate.reload();
|
| + });
|
| + }
|
| }
|
|
|
| /// A [ServiceObject] which implements [ObservableMap].
|
| @@ -1218,10 +1357,58 @@ class Class extends ServiceObject with Coverage {
|
| }
|
|
|
| class ScriptLine extends Observable {
|
| + final Script script;
|
| final int line;
|
| final String text;
|
| @observable int hits;
|
| - ScriptLine(this.line, this.text);
|
| + @observable ServiceMap bpt;
|
| + @observable bool possibleBpt = true;
|
| +
|
| + static bool _isTrivialToken(String token) {
|
| + if (token == 'else') {
|
| + return true;
|
| + }
|
| + for (var c in token.split('')) {
|
| + switch (c) {
|
| + case '{':
|
| + case '}':
|
| + case '(':
|
| + case ')':
|
| + case ';':
|
| + break;
|
| + default:
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + static bool _isTrivialLine(String text) {
|
| + var wsTokens = text.split(new RegExp(r"(\s)+"));
|
| + for (var wsToken in wsTokens) {
|
| + var tokens = wsToken.split(new RegExp(r"(\b)"));
|
| + for (var token in tokens) {
|
| + if (!_isTrivialToken(token)) {
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + ScriptLine(this.script, this.line, this.text) {
|
| + possibleBpt = !_isTrivialLine(text);
|
| +
|
| + // TODO(turnidge): This is not so efficient. Consider improving.
|
| + for (var bpt in this.script.isolate.breakpoints['breakpoints']) {
|
| + var bptScript = bpt['location']['script'];
|
| + var bptTokenPos = bpt['location']['tokenPos'];
|
| + if (bptScript == this.script &&
|
| + bptScript.tokenToLine(bptTokenPos) == line) {
|
| + this.bpt = bpt;
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
| class Script extends ServiceObject with Coverage {
|
| @@ -1269,9 +1456,12 @@ class Script extends ServiceObject with Coverage {
|
| _tokenToCol.clear();
|
| firstTokenPos = null;
|
| lastTokenPos = null;
|
| + var lineSet = new Set();
|
| +
|
| for (var line in table) {
|
| // Each entry begins with a line number...
|
| var lineNumber = line[0];
|
| + lineSet.add(lineNumber);
|
| for (var pos = 1; pos < line.length; pos += 2) {
|
| // ...and is followed by (token offset, col number) pairs.
|
| var tokenOffset = line[pos];
|
| @@ -1291,6 +1481,13 @@ class Script extends ServiceObject with Coverage {
|
| _tokenToCol[tokenOffset] = colNumber;
|
| }
|
| }
|
| +
|
| + for (var line in lines) {
|
| + // Remove possible breakpoints on lines with no tokens.
|
| + if (!lineSet.contains(line.line)) {
|
| + line.possibleBpt = false;
|
| + }
|
| + }
|
| }
|
|
|
| void _processHits(List scriptHits) {
|
| @@ -1323,15 +1520,12 @@ class Script extends ServiceObject with Coverage {
|
| lines.clear();
|
| Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}');
|
| for (var i = 0; i < sourceLines.length; i++) {
|
| - lines.add(new ScriptLine(i + 1, sourceLines[i]));
|
| + lines.add(new ScriptLine(this, i + 1, sourceLines[i]));
|
| }
|
| _applyHitsToLines();
|
| }
|
|
|
| void _applyHitsToLines() {
|
| - if (lines.length == 0) {
|
| - return;
|
| - }
|
| for (var line in lines) {
|
| var hits = _hits[line.line];
|
| line.hits = hits;
|
|
|