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 09b0de853ab1bc0a891d5aa9d999b71d3a811573..5fe97e6bec5e4ae4ffe1f92348e401c653972e5e 100644 |
--- a/runtime/bin/vmservice/client/lib/src/service/object.dart |
+++ b/runtime/bin/vmservice/client/lib/src/service/object.dart |
@@ -1103,6 +1103,146 @@ class Allocations { |
bool get empty => accumulated.empty && current.empty; |
} |
+class ClassParser { |
hausner
2014/07/01 23:49:07
When we spoke offline I was under the impression t
|
+ List<ScriptLine> _source; |
+ int _offset; |
+ |
+ ClassParser(this._source) { |
+ assert(_source != null && _source.length > 0); |
+ _offset = this._source.first.line; |
+ } |
+ |
+ // Internal state for parsing the class. |
+ var _line; |
+ var _col; |
+ int _currentToken; |
+ |
+ int _char(String str) => str.codeUnitAt(0); |
+ |
+ void _initTokens() { |
+ _line = 0; |
+ _col = 0; |
+ _currentToken = _source[_line].text.codeUnitAt(_col); |
+ } |
+ |
+ int _advanceToken([int num, stripWhite=true]) { |
+ _col++; |
+ if (_col >= _source[_line].text.codeUnits.length) { |
+ var col = _col; |
+ _col = 0; |
+ _line++; |
+ if (_line == _source.length) { |
+ _line--; |
+ _col = col; |
+ _currentToken = 0; |
+ return 0; |
+ } |
+ } |
+ |
+ while (_source[_line].text.length == 0) { _line++; } |
+ _currentToken = _source[_line].text.codeUnitAt(_col); |
+ if (stripWhite && _currentToken == _char(' ')) { |
+ _advanceToken(); |
+ } |
+ if (num != null) { |
+ for (int i = 1; i < num; i++) { |
+ _advanceToken(); |
+ } |
+ } |
+ return _currentToken; |
+ } |
+ |
+ int _peek(int len, [stripWhite=true]) { |
+ int saved_line = _line; |
+ int saved_col = _col; |
+ int c = _advanceToken(len,stripWhite); |
+ _line = saved_line; |
+ _col = saved_col; |
+ return c; |
+ } |
+ |
+ void _consumeMultilineString(int quote) { |
+ _advanceToken(3); |
+ for (int c = _currentToken; c != 0; c = _advanceToken()) { |
+ if (c == quote && _peek(1) == quote && _peek(2) == quote) { |
+ _advanceToken(3); |
+ break; |
+ } |
+ } |
+ } |
+ |
+ void _consumeString(int quote) { |
+ _advanceToken(); |
+ for (int c = _currentToken; c != 0; c = _advanceToken()) { |
+ if (c == _char('\\')) { |
+ _advanceToken(); |
+ } |
+ if (c == quote) { |
+ _advanceToken(); |
+ break; |
+ } |
+ } |
+ } |
+ |
+ void _consumeComment() { |
+ _col = _source[_line].text.codeUnits.length; |
+ } |
+ |
+ void _consumeMultilineComment() { |
+ _advanceToken(2); |
+ int commentLevel = 1; |
+ for (int c = _currentToken; c != 0; c = _advanceToken()) { |
+ if (c == _char('/') && _peek(1, false) == _char('*')) { |
+ commentLevel++; |
+ _advanceToken(); |
+ } else if (c == _char('*') && _peek(1, false) == _char('/')) { |
+ commentLevel--; |
+ _advanceToken(); |
+ if (commentLevel == 0) { |
+ _advanceToken(); |
+ break; |
+ } |
+ } |
+ } |
+ } |
+ |
+ /// Finds the end position, i.e., the closing curly brackets of the given |
+ /// class. |
+ /// Returns a tuple [line, col]. |
+ List findEndPos() { |
+ int level = 0; |
+ _initTokens(); |
+ for (int c = _currentToken; c != 0; c=_currentToken) { |
+ if ((c == _char("'") && |
+ _peek(1) == _char("'") && |
+ _peek(2) == _char("'")) || |
+ (c == _char('"') && |
+ _peek(1) == _char('"') && |
+ _peek(2) == _char('"'))) { |
+ _consumeMultilineString(c); |
+ continue; |
+ } else if (c == _char("'") || c == _char('"')) { |
+ _consumeString(c); |
+ continue; |
+ } else if (c == _char('/') && _peek(1, false) == _char('/')) { |
+ _consumeComment(); |
+ continue; |
+ } else if (c == _char('/') && _peek(1, false) == _char('*')) { |
+ _consumeMultilineComment(); |
+ continue; |
+ } else if (c == _char('{')) { |
+ level++; |
+ } else if (c == _char('}')) { |
+ if (--level == 0) { |
+ break; |
+ } |
+ } |
+ c = _advanceToken(); |
+ } |
+ return [_line + _offset, _col + 1]; |
+ } |
+} |
+ |
class Class extends ServiceObject with Coverage { |
@observable Library library; |
@observable Script script; |
@@ -1116,6 +1256,14 @@ class Class extends ServiceObject with Coverage { |
@observable int tokenPos; |
+ int _endTokenPos = null; |
+ @observable int get endTokenPos { |
+ if (_endTokenPos == null) { |
+ _computeEndTokenPos(); |
+ } |
+ return _endTokenPos; |
+ } |
+ |
@observable ServiceMap error; |
final Allocations newSpace = new Allocations(); |
@@ -1201,6 +1349,26 @@ class Class extends ServiceObject with Coverage { |
Future<ServiceObject> get(String command) { |
return isolate.get(id + "/$command"); |
} |
+ |
+ void _computeEndTokenPos() { |
+ script.load().whenComplete(() { |
+ int line = script.tokenToLine(tokenPos); // Lines are 1-based. |
+ ClassParser clsp = new ClassParser(script.lines.sublist(line - 1)); |
+ List posTuple = clsp.findEndPos(); |
+ // Search for the token one this line and column. |
+ List tokenCandidates = []; |
+ script._tokenToLine.forEach((k,v) { |
+ if (script._tokenToLine[k] == posTuple[0]) { |
+ tokenCandidates.add(k); |
+ } |
+ }); |
+ Iterable tokenIndex = |
+ tokenCandidates.where((e) => script._tokenToCol[e] == posTuple[1]); |
+ assert(tokenIndex.length == 1); |
+ _endTokenPos = tokenIndex.first; |
+ notifyPropertyChange(#endTokenPos, null, _endTokenPos); |
+ }); |
+ } |
} |
class ScriptLine extends Observable { |